#include <libkern/OSByteOrder.h>
#include <stdio.h>
#include <unistd.h>
#include "FSFormatName.h"
static CFMutableDictionaryRef __FSLocalizedNameTable = NULL;
static OSSpinLock __FSLocalizedNameTableLock = 0;
CFStringRef FSCopyFormatNameForFSType(CFStringRef fsType, int16_t fsSubtype, bool localized)
{
CFStringRef formatName;
CFStringRef formatNameTableKey;
CFIndex indx;
if (NULL == fsType) return NULL;
formatNameTableKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d%@%d"), (localized ? 1 : 0), fsType, fsSubtype);
OSSpinLockLock(&__FSLocalizedNameTableLock);
formatName = ((NULL == __FSLocalizedNameTable) ? NULL : CFDictionaryGetValue(__FSLocalizedNameTable, (const void *)formatNameTableKey));
OSSpinLockUnlock(&__FSLocalizedNameTableLock);
if (NULL == formatName) { CFBundleRef bundle = NULL;
CFURLRef bundleURL;
CFStringRef fsTypeName;
static CFArrayRef searchPaths = NULL;
if (NULL == searchPaths) {
CFArrayRef tmpPaths = CFCopySearchPathForDirectoriesInDomains(kCFLibraryDirectory, kCFSystemDomainMask | kCFNetworkDomainMask | kCFLocalDomainMask, true);
CFMutableArrayRef tmpStrings;
CFIndex i;
if (NULL == tmpPaths)
return NULL;
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) { CFDictionaryRef localPersonalities = NULL;
CFDictionaryRef bundleDict = CFBundleGetInfoDictionary(bundle);
if (localized == true) {
localPersonalities = CFBundleGetValueForInfoDictionaryKey(bundle, KEY_FS_PERSONALITIES);
}
CFDictionaryRef globalPersonalities = CFDictionaryGetValue(bundleDict, (const void *) KEY_FS_PERSONALITIES);
CFIndex numPersonalities;
if (((NULL != localPersonalities) || (localized == false)) && (NULL != globalPersonalities) &&
((numPersonalities = CFDictionaryGetCount(globalPersonalities)) > 0)) {
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);
CFNumberRef subTypeID = CFNumberCreate(NULL, kCFNumberSInt16Type, (const void *)&fsSubtype);
CFStringRef FSNameKey = NULL;
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 (NULL == FSNameKey) {
FSNameKey = keys[numPersonalities - 1]; }
CFDictionaryRef FSNameDict;
if (localized == true) {
FSNameDict = CFDictionaryGetValue(localPersonalities, FSNameKey);
} else {
FSNameDict = CFDictionaryGetValue(globalPersonalities, FSNameKey);
}
if (NULL != FSNameDict) {
formatName = CFDictionaryGetValue(FSNameDict, (const void *)KEY_FS_NAME);
}
if (values != valuesBuffer) free(values);
if (keys != keysBuffer) free(keys);
}
}
if (NULL == formatName) {
static CFStringRef unknownTypeString = NULL;
CFStringRef unknownFSNameString = NULL;
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 = unknownFSNameString;
}
OSSpinLockLock(&__FSLocalizedNameTableLock);
if (NULL == __FSLocalizedNameTable) __FSLocalizedNameTable = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(__FSLocalizedNameTable, (const void *)formatNameTableKey, (const void *)formatName);
OSSpinLockUnlock(&__FSLocalizedNameTableLock);
if (NULL != bundle) CFRelease(bundle); }
CFRelease(formatNameTableKey);
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;
if (statfs((char *)buffer, &fsInfo) == 0) {
CFStringRef fsType = CFStringCreateWithCString(NULL, fsInfo.f_fstypename, kCFStringEncodingASCII);
#ifdef _DARWIN_FEATURE_64_BIT_INODE
formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_fssubtype, true);
#else
formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_reserved1, true);
#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);
#ifdef _DARWIN_FEATURE_64_BIT_INODE
formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_fssubtype, false);
#else
formatName = FSCopyFormatNameForFSType(fsType, fsInfo.f_reserved1, false);
#endif
CFRelease(fsType);
}
}
return formatName;
}
CFStringRef _FSCopyLocalizedNameForVolumeFormatAtNode(CFStringRef devnode)
{
CFStringRef formatName = NULL;
char devnodename[MAXPATHLEN + 1];
char fsname[MAX_FSNAME];
int fssubtype = 0;
if (true == CFStringGetCString(devnode, devnodename, MAXPATHLEN + 1, kCFStringEncodingUTF8)) {
memset(fsname, MAX_FSNAME, 0);
if (getfstype(devnodename, fsname, &fssubtype) == true) {
CFStringRef fsType = CFStringCreateWithCString(NULL, fsname, kCFStringEncodingASCII);
formatName = FSCopyFormatNameForFSType(fsType, fssubtype, true);
CFRelease(fsType);
}
}
return formatName;
}
CFStringRef _FSCopyNameForVolumeFormatAtNode(CFStringRef devnode)
{
CFStringRef formatName = NULL;
char devnodename[MAXPATHLEN + 1];
char fsname[MAX_FSNAME];
int fssubtype = 0;
if (true == CFStringGetCString(devnode, devnodename, MAXPATHLEN + 1, kCFStringEncodingUTF8)) {
memset(fsname, MAX_FSNAME, 0);
if (getfstype(devnodename, fsname, &fssubtype) == true) {
CFStringRef fsType = CFStringCreateWithCString(NULL, fsname, kCFStringEncodingASCII);
formatName = FSCopyFormatNameForFSType(fsType, fssubtype, false);
CFRelease(fsType);
}
}
return formatName;
}
bool getfstype(char *devnode, char *fsname, int *fssubtype)
{
if (is_hfs(devnode, fssubtype) == true) {
strcpy(fsname, HFS_NAME);
return true;
}
if (is_msdos(devnode, fssubtype) == true) {
strcpy(fsname, MSDOS_NAME);
return true;
}
return false;
}
#define SW16(x) OSSwapBigToHostInt16(x)
#define SW32(x) OSSwapBigToHostInt32(x)
bool is_hfs(char *devnode, int *fssubtype)
{
HFSPlusVolumeHeader *vhp;
off_t offset = 0;
char *buffer = NULL;
int fd = 0;
bool retval = false;
*fssubtype = -1;
buffer = (char *)malloc(MAX_HFS_BLOCK_READ);
if (!buffer) {
goto out;
}
fd = open(devnode, O_RDONLY | O_NDELAY, 0);
if (fd <= 0) {
goto out;
}
if (getblk(fd, 2, MAX_HFS_BLOCK_READ, buffer) < MAX_HFS_BLOCK_READ) {
goto out;
}
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;
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: {
*fssubtype = kHFSSubType;
retval = true;
goto out;
}
default: {
goto out;
}
};
if ((vhp->journalInfoBlock != 0) && (SW32(vhp->attributes) & kHFSVolumeJournaledMask)) {
*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)) {
if (*fssubtype == kHFSJSubType) {
*fssubtype = kHFSXJSubType;
} else {
*fssubtype = kHFSXSubType;
}
}
}
if (*fssubtype < 0) {
*fssubtype = kHFSPlusSubType;
}
retval = true;
out:
if (buffer) {
free(buffer);
}
if (fd > 0) {
close(fd);
}
return retval;
}
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;
*fssubtype = -1;
buffer = (char *)malloc(MAX_DOS_BLOCKSIZE);
if (!buffer) {
goto out;
}
fd = open(devnode, O_RDONLY | O_NDELAY, 0);
if (fd <= 0) {
goto out;
}
if (getblk(fd, 0, MAX_DOS_BLOCKSIZE, buffer) < MAX_DOS_BLOCKSIZE) {
goto out;
}
bsp = (union bootsector *)buffer;
b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
if (bsp->bs50.bsJump[0] != 0xE9
&& (bsp->bs50.bsJump[0] != 0xEB || bsp->bs50.bsJump[2] != 0x90)) {
goto out;
}
BytesPerSector = getushort(b710->bpbBytesPerSec);
if ((BytesPerSector < 0x200) || (BytesPerSector & (BytesPerSector - 1)) || (BytesPerSector > 0x800)) {
goto out;
}
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) {
*fssubtype = 0;
} else if (countOfClusters < 65525) {
*fssubtype = 1;
} else {
*fssubtype = 2;
}
retval = true;
out:
if (buffer) {
free(buffer);
}
if (fd > 0) {
close(fd);
}
return retval;
}
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);
}
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);
}