/* * Copyright (c) 1999-2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include "FSFormatName.h" static CFMutableDictionaryRef __FSLocalizedNameTable = NULL; static OSSpinLock __FSLocalizedNameTableLock = 0; static bool IsEncrypted(const char *bsdname); CFStringRef FSCopyFormatNameForFSType(CFStringRef fsType, int16_t fsSubtype, bool localized, bool encrypted) { CFTypeRef formatName; CFStringRef formatNameTableKey; CFIndex indx; if (NULL == fsType) return NULL; // Create a key for cache localized name table (i.e. "0hfs0") formatNameTableKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d%@%d"), (localized ? 1 : 0), fsType, fsSubtype); // Use OSSpinLock to protect the table accessed from multiple threads OSSpinLockLock(&__FSLocalizedNameTableLock); formatName = (void*)((NULL == __FSLocalizedNameTable) ? NULL : CFDictionaryGetValue(__FSLocalizedNameTable, (const void *)formatNameTableKey)); OSSpinLockUnlock(&__FSLocalizedNameTableLock); if (NULL == formatName) { // not in the cache CFBundleRef bundle = NULL; CFURLRef bundleURL; CFStringRef fsTypeName; static CFArrayRef searchPaths = NULL; /* Construct a bundle path URL from the fsType argument and create a CFBundle. We search (using CFCopySearchPathForDirectoriesInDomains) /Network/Library/Filesystems, /Library/Filesystems, and /System/Library/Filesystems. */ // Create CFURL for /System/Library/Filesystems and cache it if (NULL == searchPaths) { CFArrayRef tmpPaths = CFCopySearchPathForDirectoriesInDomains(kCFLibraryDirectory, kCFSystemDomainMask | kCFNetworkDomainMask | kCFLocalDomainMask, true); CFMutableArrayRef tmpStrings; CFIndex i; if (NULL == tmpPaths) return NULL; // No directories to search?!?! tmpStrings = CFArrayCreateMutable(NULL, CFArrayGetCount(tmpPaths), NULL); if (tmpStrings == NULL) goto done; for (i = 0; i < CFArrayGetCount(tmpPaths); i++) { CFStringRef tStr; CFURLRef tURL; char path[PATH_MAX + 1]; CFTypeRef tobject = CFArrayGetValueAtIndex(tmpPaths, i); if (CFGetTypeID(tobject) == CFURLGetTypeID()) { if (false == CFURLGetFileSystemRepresentation( tobject, false, (UInt8*)path, sizeof(path))) { goto done; } } else if (CFGetTypeID(tobject) == CFStringGetTypeID()) { CFStringGetCString(tobject, path, sizeof(path), kCFStringEncodingUTF8); } else { goto done; } strlcat(path, "/Filesystems", sizeof(path)); tStr = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); if (tStr == NULL) goto done; tURL = CFURLCreateWithFileSystemPath(NULL, tStr, kCFURLPOSIXPathStyle, true); if (tURL) { CFArrayAppendValue(tmpStrings, tURL); } CFRelease(tStr); } searchPaths = CFArrayCreateCopy(NULL, tmpStrings); done: CFRelease(tmpStrings); CFRelease(tmpPaths); if (searchPaths == NULL) return NULL; } for (indx = 0; indx < CFArrayGetCount(searchPaths); indx++) { CFURLRef libRef = CFArrayGetValueAtIndex(searchPaths, indx); fsTypeName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.fs"), fsType); bundleURL = CFURLCreateWithFileSystemPathRelativeToBase(NULL, fsTypeName, kCFURLPOSIXPathStyle, true, libRef); bundle = CFBundleCreate(NULL, bundleURL); CFRelease(fsTypeName); CFRelease(bundleURL); if (NULL != bundle) { break; } } if (NULL != bundle) { // the bundle exists at path CFDictionaryRef localPersonalities = NULL; // Access the Info dictionary in the bundle CFDictionaryRef bundleDict = CFBundleGetInfoDictionary(bundle); // Get localized FSPersonalities only if we want localized name if (localized == true) { localPersonalities = CFBundleGetValueForInfoDictionaryKey(bundle, KEY_FS_PERSONALITIES); //NSLog(CFSTR("localPersonalities = %@\n"), localPersonalities); } /* Get global FSPersonalities. We need to access this since FSSubType exists only * in global FSPersonalities */ CFDictionaryRef globalPersonalities = CFDictionaryGetValue(bundleDict, (const void *) KEY_FS_PERSONALITIES); //NSLog(CFSTR("globalPersonalities = %@\n"), globalPersonalities); CFIndex numPersonalities; if (((NULL != localPersonalities) || (localized == false)) && // localPersonalities or we don't want localizations (NULL != globalPersonalities) && ((numPersonalities = CFDictionaryGetCount(globalPersonalities)) > 0)) { // read all FSPersonalities keys and values CFDictionaryRef valuesBuffer[MAX_FS_SUBTYPES]; CFStringRef keysBuffer[MAX_FS_SUBTYPES]; CFDictionaryRef *values = ((numPersonalities > MAX_FS_SUBTYPES) ? (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef) * numPersonalities) : valuesBuffer); CFStringRef *keys = ((numPersonalities > MAX_FS_SUBTYPES) ? (CFStringRef *)malloc(sizeof(CFStringRef) * numPersonalities) : keysBuffer); CFDictionaryGetKeysAndValues(globalPersonalities, (const void **)keys, (const void **)values); // create CFNumberRef for the FSSubType CFNumberRef subTypeID = CFNumberCreate(NULL, kCFNumberSInt16Type, (const void *)&fsSubtype); CFStringRef FSNameKey = NULL; // search for valid FSSubType - we will use its key from global FSPersonalties to // access FSName from localized FSPersonalities CFIndex index; CFNumberRef readSubTypeID; for (index = 0; index < numPersonalities; index++) { if ((true == CFDictionaryGetValueIfPresent(values[index], (const void *)KEY_FS_SUBTYPE, (const void **)&readSubTypeID)) && (CFNumberCompare(subTypeID, readSubTypeID, NULL) == 0)) { FSNameKey = keys[index]; break; } } CFRelease(subTypeID); /* If a personality hasn't been found, use the last value in the dictionary (note the content of CFDictionary is unordered so choosing the last doesn't produce consistent result) */ if (NULL == FSNameKey) { FSNameKey = keys[numPersonalities - 1]; // is selecting the last entry right ? } // Get FSName from the FSPersonalities entry CFDictionaryRef FSNameDict; if (localized == true) { FSNameDict = CFDictionaryGetValue(localPersonalities, FSNameKey); } else { FSNameDict = CFDictionaryGetValue(globalPersonalities, FSNameKey); } if (NULL != FSNameDict) { CFStringRef tempName = CFDictionaryGetValue(FSNameDict, (const void *)KEY_FS_NAME); CFStringRef encrName = CFDictionaryGetValue(FSNameDict, CFSTR(kFSCoreStorageEncryptNameKey)); if (tempName) { if (encrName) { formatName = (void*)CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (formatName != NULL) { (void)CFDictionarySetValue((void*)formatName, tempName, encrName); } } else { formatName = tempName; } } } if (values != valuesBuffer) free(values); if (keys != keysBuffer) free(keys); } } // If all failed, return "Unknown format (f_fstypename)" as the last resort if (NULL == formatName) { static CFStringRef unknownTypeString = NULL; CFStringRef unknownFSNameString = NULL; // This should use the framework bundle this code resides. CarbonCore ??? */ if (NULL == unknownTypeString) unknownTypeString = CFCopyLocalizedString(UNKNOWN_FS_NAME, "This string is displayed when localized file system name cannot be determined."); unknownFSNameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)"), unknownTypeString, fsType); formatName = (void*)unknownFSNameString; } // Cache the result OSSpinLockLock(&__FSLocalizedNameTableLock); if (NULL == __FSLocalizedNameTable) __FSLocalizedNameTable = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // NSLog(CFSTR("Setting value %@ for key %@\n"), formatName, formatNameTableKey); CFDictionarySetValue(__FSLocalizedNameTable, (const void *)formatNameTableKey, (const void *)formatName); OSSpinLockUnlock(&__FSLocalizedNameTableLock); // NSLog(CFSTR("Localized Name Table = %@\n"), __FSLocalizedNameTable); if (NULL != bundle) CFRelease(bundle); // it has to be released here since formatName might be owned by the bundle } CFRelease(formatNameTableKey); if (CFGetTypeID(formatName) == CFStringGetTypeID()) { return CFRetain(formatName); } else if (CFGetTypeID(formatName) == CFDictionaryGetTypeID()) { // Dictionary with the (possibly localized) name as the key, and the encrypted name as the value // If we want the encrypted name, we return the value, else we return the key size_t numEntries = CFDictionaryGetCount((void*)formatName); void *keyNames[numEntries]; void *values[numEntries]; CFDictionaryGetKeysAndValues((void*)formatName, (const void**)&keyNames, (const void**)&values); if (encrypted) return CFRetain(values[0]); else return CFRetain(keyNames[0]); } return CFRetain(formatName); } CFStringRef _FSCopyLocalizedNameForVolumeFormatAtURL(CFURLRef url) { CFStringRef formatName = NULL; uint8_t buffer[MAXPATHLEN + 1]; if ((NULL != url) && CFURLGetFileSystemRepresentation(url, true, buffer, MAXPATHLEN)) { struct statfs fsInfo; bool encrypted = false; if (statfs((char *)buffer, &fsInfo) == 0) { CFStringRef fsType = CFStringCreateWithCString(NULL, fsInfo.f_fstypename, kCFStringEncodingASCII); encrypted = IsEncrypted(fsInfo.f_mntfromname); #ifdef _DARWIN_FEATURE_64_BIT_INODE formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_fssubtype, true, encrypted); #else formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_reserved1, true, encrypted); #endif CFRelease(fsType); } } return formatName; } CFStringRef _FSCopyNameForVolumeFormatAtURL(CFURLRef url) { CFStringRef formatName = NULL; uint8_t buffer[MAXPATHLEN + 1]; if ((NULL != url) && CFURLGetFileSystemRepresentation(url, true, buffer, MAXPATHLEN)) { struct statfs fsInfo; if (statfs((char *)buffer, &fsInfo) == 0) { CFStringRef fsType = CFStringCreateWithCString(NULL, fsInfo.f_fstypename, kCFStringEncodingASCII); bool encrypted = false; encrypted = IsEncrypted(fsInfo.f_mntfromname); #ifdef _DARWIN_FEATURE_64_BIT_INODE formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_fssubtype, false, encrypted); #else formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_reserved1, false, encrypted); #endif CFRelease(fsType); } } return formatName; } CFStringRef _FSCopyLocalizedNameForVolumeFormatAtNode(CFStringRef devnode) { CFStringRef formatName = NULL; char devnodename[MAXPATHLEN + 1]; char fsname[MAX_FSNAME]; int fssubtype = 0; /* convert CFStringRef devnode to CSTR */ if (true == CFStringGetCString(devnode, devnodename, MAXPATHLEN + 1, kCFStringEncodingUTF8)) { bool encrypted = false; encrypted = IsEncrypted(devnodename); /* get fsname and fssubtype */ memset(fsname, MAX_FSNAME, 0); if (getfstype(devnodename, fsname, &fssubtype) == true) { /* get unlocalized string */ CFStringRef fsType = CFStringCreateWithCString(NULL, fsname, kCFStringEncodingASCII); formatName = FSCopyFormatNameForFSType(fsType, fssubtype, true, encrypted); CFRelease(fsType); } } return formatName; } CFStringRef _FSCopyNameForVolumeFormatAtNode(CFStringRef devnode) { CFStringRef formatName = NULL; char devnodename[MAXPATHLEN + 1]; char fsname[MAX_FSNAME]; int fssubtype = 0; /* convert CFStringRef devnode to CSTR */ if (true == CFStringGetCString(devnode, devnodename, MAXPATHLEN + 1, kCFStringEncodingUTF8)) { bool encrypted; encrypted = IsEncrypted(devnodename); /* get fsname and fssubtype */ memset(fsname, MAX_FSNAME, 0); if (getfstype(devnodename, fsname, &fssubtype) == true) { /* get unlocalized string */ CFStringRef fsType = CFStringCreateWithCString(NULL, fsname, kCFStringEncodingASCII); formatName = FSCopyFormatNameForFSType(fsType, fssubtype, false, encrypted); CFRelease(fsType); } } return formatName; } /* Return the fsname and subtype number for devnode */ bool getfstype(char *devnode, char *fsname, int *fssubtype) { /* Check if HFS */ if (is_hfs(devnode, fssubtype) == true) { strcpy(fsname, HFS_NAME); return true; } /* Check if MSDOS */ if (is_msdos(devnode, fssubtype) == true) { strcpy(fsname, MSDOS_NAME); return true; } return false; } #define SW16(x) OSSwapBigToHostInt16(x) #define SW32(x) OSSwapBigToHostInt32(x) /* Check if the disk is HFS disk and return its subtypes */ bool is_hfs(char *devnode, int *fssubtype) { HFSPlusVolumeHeader *vhp; off_t offset = 0; char *buffer = NULL; int fd = 0; bool retval = false; /* default fssubtype to non-existing value */ *fssubtype = -1; buffer = (char *)malloc(MAX_HFS_BLOCK_READ); if (!buffer) { goto out; } /* open the device */ fd = open(devnode, O_RDONLY | O_NDELAY, 0); if (fd <= 0) { goto out; } /* read volume header (512 bytes, block 2) from the device */ if (getblk(fd, 2, MAX_HFS_BLOCK_READ, buffer) < MAX_HFS_BLOCK_READ) { goto out; } /* Check if it is a HFS volume */ if (getwrapper((HFSMasterDirectoryBlock *)buffer, &offset)) { if (getblk(fd, 2 + (offset/MAX_HFS_BLOCK_READ), MAX_HFS_BLOCK_READ, buffer) < MAX_HFS_BLOCK_READ) { goto out; } } vhp = (HFSPlusVolumeHeader *)buffer; /* Validate signature */ switch (SW16(vhp->signature)) { case kHFSPlusSigWord: { if (SW16(vhp->version) != kHFSPlusVersion) { goto out; } break; } case kHFSXSigWord: { if (SW16(vhp->version) != kHFSXVersion) { goto out; } break; } case kHFSSigWord: { /* HFS */ *fssubtype = kHFSSubType; retval = true; goto out; } default: { goto out; } }; if ((vhp->journalInfoBlock != 0) && (SW32(vhp->attributes) & kHFSVolumeJournaledMask)) { /* Journaled */ *fssubtype = kHFSJSubType; } if (SW16(vhp->signature) == kHFSXSigWord) { BTHeaderRec * bthp; off_t foffset; foffset = (off_t)SW32(vhp->catalogFile.extents[0].startBlock) * (off_t)SW32(vhp->blockSize); if (getblk(fd, (offset/MAX_HFS_BLOCK_READ) + (foffset/MAX_HFS_BLOCK_READ) , MAX_HFS_BLOCK_READ, buffer) < MAX_HFS_BLOCK_READ) { goto out; } bthp = (BTHeaderRec *)&buffer[sizeof(BTNodeDescriptor)]; if ((SW16(bthp->maxKeyLength) == kHFSPlusCatalogKeyMaximumLength) && (bthp->keyCompareType == kHFSBinaryCompare)) { /* HFSX */ if (*fssubtype == kHFSJSubType) { /* Journaled HFSX */ *fssubtype = kHFSXJSubType; } else { /* HFSX */ *fssubtype = kHFSXSubType; } } } if (*fssubtype < 0) { /* default HFS Plus */ *fssubtype = kHFSPlusSubType; } retval = true; out: if (buffer) { free(buffer); } if (fd > 0) { close(fd); } return retval; } /* Check if the disk is MSDOS disk and return its subtypes */ bool is_msdos(char *devnode, int *fssubtype) { union bootsector *bsp; struct byte_bpb710 *b710; u_int32_t FATSectors; u_int32_t TotalSectors; u_int32_t countOfClusters; u_int32_t DataSectors; u_int32_t RootDirSectors; u_int16_t BytesPerSector; u_int8_t SectorsPerCluster; char *buffer = NULL; int fd = 0; bool retval = false; /* default fssubtype to non-existing value */ *fssubtype = -1; buffer = (char *)malloc(MAX_DOS_BLOCKSIZE); if (!buffer) { goto out; } /* open the device */ fd = open(devnode, O_RDONLY | O_NDELAY, 0); if (fd <= 0) { goto out; } /* read the block */ if (getblk(fd, 0, MAX_DOS_BLOCKSIZE, buffer) < MAX_DOS_BLOCKSIZE) { goto out; } bsp = (union bootsector *)buffer; b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; /* The first three bytes are an Intel x86 jump instruction. It should be one * of the following forms: * 0xE9 0x?? 0x?? * 0xEC 0x?? 0x90 * where 0x?? means any byte value is OK. */ if (bsp->bs50.bsJump[0] != 0xE9 && (bsp->bs50.bsJump[0] != 0xEB || bsp->bs50.bsJump[2] != 0x90)) { goto out; } /* We only work with 512, 1024, and 2048 byte sectors */ BytesPerSector = getushort(b710->bpbBytesPerSec); if ((BytesPerSector < 0x200) || (BytesPerSector & (BytesPerSector - 1)) || (BytesPerSector > 0x800)) { goto out; } /* Check to make sure valid sectors per cluster */ SectorsPerCluster = b710->bpbSecPerClust; if ((SectorsPerCluster == 0 ) || (SectorsPerCluster & (SectorsPerCluster - 1))) { goto out; } RootDirSectors = ((getushort(b710->bpbRootDirEnts) * 32) + (BytesPerSector - 1)) / BytesPerSector; if (getushort(b710->bpbFATsecs)) { FATSectors = getushort(b710->bpbFATsecs); } else { FATSectors = getulong(b710->bpbBigFATsecs); } if (getushort(b710->bpbSectors)) { TotalSectors = getushort(b710->bpbSectors); } else { TotalSectors = getulong(b710->bpbHugeSectors); } DataSectors = TotalSectors - (getushort(b710->bpbResSectors) + (b710->bpbFATs * FATSectors) + RootDirSectors); countOfClusters = DataSectors/(b710->bpbSecPerClust); if (countOfClusters < 4085) { /* FAT12 */ *fssubtype = 0; } else if (countOfClusters < 65525) { /* FAT16 */ *fssubtype = 1; } else { /* FAT32 */ *fssubtype = 2; } retval = true; out: if (buffer) { free(buffer); } if (fd > 0) { close(fd); } return retval; } /* read raw block from disk */ static int getblk(int fd, unsigned long blknum, int blksize, char* buf) { off_t offset; int bytes_read; offset = (off_t)blknum * (off_t)blksize; if ((bytes_read = pread(fd, buf, blksize, offset)) != blksize) { return (-1); } return (bytes_read); } /* get HFS wrapper information */ static int getwrapper(const HFSMasterDirectoryBlock *mdbp, off_t *offset) { if ((SW16(mdbp->drSigWord) != kHFSSigWord) || (SW16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) { return(0); } *offset = SW16(mdbp->drAlBlSt) * 512; *offset += (u_int64_t)SW16(mdbp->drEmbedExtent.startBlock) * (u_int64_t)SW32(mdbp->drAlBlkSiz); return (1); } static bool IsEncrypted(const char *bsdname) { bool retval = false; const char *diskname = NULL; CFMutableDictionaryRef ioMatch; //IOServiceGetMatchingService() releases:! io_object_t ioObj = IO_OBJECT_NULL; CFBooleanRef lvfIsEncr = NULL; if (strncmp(bsdname, _PATH_DEV, strlen(_PATH_DEV)) == 0) { diskname = bsdname + strlen(_PATH_DEV); } if (diskname == NULL) goto finish; if (strncmp(diskname, "rdisk", 5) == 0) diskname++; //look up the IOMedia object ioMatch = IOBSDNameMatching(kIOMasterPortDefault, 0, diskname); if (!ioMatch) goto finish; // Setting this allows a fast-path lookup to happen // see 10248763 CFDictionarySetValue(ioMatch, CFSTR(kIOProviderClassKey), CFSTR(kIOMediaClass)); ioObj = IOServiceGetMatchingService(kIOMasterPortDefault, ioMatch); ioMatch = NULL; //IOServiceGetMatching() released ioMatch if (ioObj == IO_OBJECT_NULL) { goto finish; } lvfIsEncr = IORegistryEntryCreateCFProperty(ioObj, CFSTR(kCoreStorageIsEncryptedKey), nil, 0); if (lvfIsEncr == NULL) retval = false; else retval = CFBooleanGetValue(lvfIsEncr); finish: if (lvfIsEncr) CFRelease(lvfIsEncr); if (ioObj != IO_OBJECT_NULL) { IOObjectRelease(ioObj); } return retval; }