#include <IOKit/IOCFURLAccess.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <fcntl.h>
#include "IOKitInternal.h"
#ifdef HAVE_CFURLACCESS
CFTypeRef IOURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode)
{
return (CFURLCreatePropertyFromResource(alloc, url, property, errorCode));
}
Boolean IOURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *resourceData, CFDictionaryRef *properties, CFArrayRef desiredProperties, SInt32 *errorCode)
{
return (CFURLCreateDataAndPropertiesFromResource(alloc, url, resourceData, properties, desiredProperties, errorCode));
}
Boolean IOCFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef dataToWrite, CFDictionaryRef propertiesToWrite, SInt32 *errorCode)
{
return (CFURLWriteDataAndPropertiesToResource(url, dataToWrite, propertiesToWrite, errorCode));
}
#else
#define kIOFileURLExists CFSTR("kIOFileURLExists")
#define kIOFileURLPOSIXMode CFSTR("kIOFileURLPOSIXMode")
#define kIOFileURLSize CFSTR("kIOFileURLSize")
static Boolean _IOFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode);
static CFDictionaryRef _IOFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode);
static Boolean _IOFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode);
static CFMutableArrayRef _IOContentsOfDirectory(CFAllocatorRef alloc, char path[CFMaxPathLength], CFURLRef base, CFStringRef matchingAbstractType);
extern Boolean _IOReadBytesFromFile(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength);
extern Boolean _IOWriteBytesToFile(const char *path, const void *bytes, CFIndex length);
CFTypeRef IOURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode) {
CFArrayRef array = CFArrayCreate(alloc, (const void **)&property, 1, &kCFTypeArrayCallBacks);
CFDictionaryRef dict;
if (IOURLCreateDataAndPropertiesFromResource(alloc, url, NULL, &dict, array, errorCode)) {
CFTypeRef result = CFDictionaryGetValue(dict, property);
if (result) CFRetain(result);
CFRelease(array);
CFRelease(dict);
return result;
} else {
if (dict) CFRelease(dict);
CFRelease(array);
return NULL;
}
}
Boolean IOURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFDictionaryRef *fetchedProperties, CFArrayRef desiredProperties, SInt32 *errorCode) {
CFStringRef scheme = CFURLCopyScheme(url);
if (!scheme) {
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
if (fetchedData) *fetchedData = NULL;
if (fetchedProperties) *fetchedProperties = NULL;
return FALSE;
} else {
Boolean result;
if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
result = _IOFileURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode);
} else {
if (fetchedData) *fetchedData = NULL;
if (fetchedProperties) *fetchedProperties = NULL;
if (errorCode) *errorCode = kIOURLUnknownSchemeError;
result = FALSE;
}
CFRelease(scheme);
return result;
}
}
static Boolean _IOFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode) {
char buffer[CFMaxPathSize];
Boolean success = TRUE;
if (!CFURLGetFileSystemRepresentation(url, TRUE, buffer, CFMaxPathSize)) {
if (fetchedData) *fetchedData = NULL;
if (fetchedProperties) *fetchedProperties = NULL;
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
return FALSE;
}
if (errorCode) *errorCode = 0;
if (fetchedData) {
void *bytes;
CFIndex length;
Boolean releaseAlloc = FALSE;
if (alloc == NULL) {
alloc = CFRetain(CFAllocatorGetDefault());
releaseAlloc = TRUE;
}
if (!_IOReadBytesFromFile(alloc, buffer, &bytes, &length, 0)) {
if (errorCode) *errorCode = kIOURLUnknownError;
*fetchedData = NULL;
success = FALSE;
} else {
*fetchedData = CFDataCreateWithBytesNoCopy(alloc, bytes, length, alloc);
}
if (releaseAlloc) {
CFRelease(alloc);
}
}
if (fetchedProperties) {
*fetchedProperties = _IOFileURLCreatePropertiesFromResource(alloc, url, desiredProperties, errorCode);
if (!*fetchedProperties) success = FALSE;
}
return success;
}
Boolean IOURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDictionaryRef propertyDict, SInt32 *errorCode) {
CFStringRef scheme = CFURLCopyScheme(url);
if (!scheme) {
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
return FALSE;
} else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
Boolean success = TRUE;
CFRelease(scheme);
if (errorCode) *errorCode = 0;
if (data) {
char cPath[CFMaxPathSize];
if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
success = FALSE;
} else if (CFURLHasDirectoryPath(url)) {
success = !mkdir(cPath, 0777);
if (!success && errorCode) *errorCode = kIOURLUnknownError;
} else {
SInt32 length = CFDataGetLength(data);
const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data);
success = _IOWriteBytesToFile(cPath, bytes, length);
if (!success && errorCode) *errorCode = kIOURLUnknownError;
}
}
if (propertyDict) {
if (!_IOFileURLWritePropertiesToResource(url, propertyDict, errorCode))
success = FALSE;
}
return success;
} else {
if (errorCode) *errorCode = kIOURLUnknownSchemeError;
return FALSE;
}
}
static Boolean _IOFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode) {
CFTypeRef buffer[16];
void **keys;
void **values;
Boolean result = TRUE;
SInt32 index, count;
char cPath[CFMaxPathSize];
if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
return FALSE;
}
count = CFDictionaryGetCount(propertyDict);
if (count < 8) {
keys = buffer;
values = buffer+8;
} else {
keys = CFAllocatorAllocate(CFGetAllocator(url), sizeof(void *) * count * 2, 0);
values = keys + count;
}
CFDictionaryGetKeysAndValues(propertyDict, keys, values);
for (index = 0; index < count; index ++) {
CFStringRef key = keys[index];
CFTypeRef value = values[index];
if (CFEqual(key, kIOFileURLPOSIXMode) || CFEqual(key, kIOURLFilePOSIXMode)) {
SInt32 mode;
int err;
if (CFEqual(key, kIOURLFilePOSIXMode)) {
CFNumberRef modeNum = (CFNumberRef)value;
CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode);
} else {
const mode_t *modePtr = (const mode_t *)CFDataGetBytePtr((CFDataRef)value);
mode = *modePtr;
}
err = chmod(cPath, mode);
if (err != 0) result = FALSE;
} else {
result = FALSE;
}
}
if (keys != &buffer[0]) CFAllocatorDeallocate(CFGetAllocator(url), keys);
if (errorCode) *errorCode = result ? 0 : kIOURLUnknownError;
return result;
}
static CFDictionaryRef _IOFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode) {
static CFArrayRef _allProps = NULL;
char cPath[CFMaxPathSize];
SInt32 index, count, statResult = 0;
CFMutableDictionaryRef propertyDict = NULL;
struct stat statBuf;
Boolean statCompleted = FALSE;
if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
if (errorCode) *errorCode = kIOURLImproperArgumentsError;
return NULL;
}
if (errorCode) *errorCode = 0;
if (!desiredProperties) {
if (!_allProps) {
const void *values[9];
values[0] = kIOURLFileExists;
values[1] = kIOURLFilePOSIXMode;
values[2] = kIOURLFileDirectoryContents;
values[3] = kIOURLFileLength;
values[4] = kIOURLFileLastModificationTime;
values[5] = kIOURLFileOwnerID;
values[6] = kIOFileURLExists;
values[7] = kIOFileURLPOSIXMode;
values[8] = kIOFileURLSize;
_allProps = CFArrayCreate(NULL, values, 8, &kCFTypeArrayCallBacks);
}
desiredProperties = _allProps;
}
count = CFArrayGetCount(desiredProperties);
propertyDict = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
if (count == 0) return propertyDict;
for (index = 0; index < count; index ++) {
CFStringRef key = (CFMutableStringRef )CFArrayGetValueAtIndex(desiredProperties, index);
if (!statCompleted && (CFEqual(key, kIOURLFilePOSIXMode) || CFEqual(key, kIOURLFileDirectoryContents) || CFEqual(key, kIOURLFileLength) || CFEqual(key, kIOURLFileLastModificationTime) || CFEqual(key, kIOURLFileExists) || CFEqual(key, kIOFileURLExists) || CFEqual(key, kIOFileURLPOSIXMode) || CFEqual(key, kIOFileURLSize) || CFEqual(key, kIOURLFileOwnerID))) {
statResult = stat(cPath, &statBuf);
if (statResult != 0) statResult = thread_errno();
statCompleted = TRUE;
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
if (CFEqual(key, kIOFileURLPOSIXMode)) {
if (statResult == 0) {
CFDataRef modeData = CFDataCreate(alloc, (void *)(&(statBuf.st_mode)), sizeof(statBuf.st_mode));
CFDictionarySetValue(propertyDict, kIOFileURLPOSIXMode, modeData);
CFRelease(modeData);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFilePOSIXMode)) {
if (statResult == 0) {
SInt32 value = statBuf.st_mode;
CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &value);
CFDictionarySetValue(propertyDict, kIOURLFilePOSIXMode, num);
CFRelease(num);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFileDirectoryContents)) {
if (statResult == 0 && (statBuf.st_mode & S_IFMT) == S_IFDIR) {
CFMutableArrayRef contents = _IOContentsOfDirectory(alloc, cPath, url, NULL);
if (contents) {
CFDictionarySetValue(propertyDict, kIOURLFileDirectoryContents, contents);
CFRelease(contents);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOFileURLSize)) {
if (statResult == 0) {
UInt64 length = statBuf.st_size;
CFDataRef tmpData = CFDataCreate(alloc, (void *)(&length), sizeof(UInt64));
CFDictionarySetValue(propertyDict, kIOFileURLSize, tmpData);
CFRelease(tmpData);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFileLength)) {
if (statResult == 0) {
SInt64 length = statBuf.st_size;
CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt64Type, &length);
CFDictionarySetValue(propertyDict, kIOURLFileLength, num);
CFRelease(num);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFileLastModificationTime)) {
if (statResult == 0) {
CFDateRef date = CFDateCreate(alloc, statBuf.st_mtime - kCFAbsoluteTimeIntervalSince1970);
CFDictionarySetValue(propertyDict, kIOURLFileLastModificationTime, date);
CFRelease(date);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFileExists)) {
if (statResult == 0) {
CFDictionarySetValue(propertyDict, kIOURLFileExists, kCFBooleanTrue);
} else if (statResult == ENOENT) {
CFDictionarySetValue(propertyDict, kIOURLFileExists, kCFBooleanFalse);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOFileURLExists)) {
if (statResult == 0) {
CFDictionarySetValue(propertyDict, kIOFileURLExists, kIOFileURLExists);
} else if (statResult == ENOENT) {
CFDictionarySetValue(propertyDict, kIOFileURLExists, kCFBooleanFalse); } else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (CFEqual(key, kIOURLFileOwnerID)) {
if (statResult == 0) {
SInt32 uid = statBuf.st_uid;
CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &uid);
CFDictionarySetValue(propertyDict, kIOURLFileOwnerID, num);
CFRelease(num);
} else if (errorCode) {
*errorCode = kIOURLUnknownError;
}
} else if (errorCode) {
*errorCode = kIOURLUnknownPropertyKeyError;
}
}
return propertyDict;
}
static CFMutableArrayRef _IOContentsOfDirectory(CFAllocatorRef alloc, char path[CFMaxPathLength], CFURLRef base, CFStringRef matchingAbstractType) {
CFMutableArrayRef files;
Boolean releaseBase = FALSE;
CFIndex pathLength = strlen(path);
CFStringRef extension = (matchingAbstractType ? CFRetain(matchingAbstractType) : NULL);
CFIndex extLen = (extension ? CFStringGetLength(extension) : 0);
char extBuff[CFMaxPathSize];
int fd, numread;
long basep;
char dirge[8192];
if (extLen > 0) {
CFStringGetBytes(extension, CFRangeMake(0, extLen), kCFStringEncodingMacRoman, 0, FALSE, extBuff, CFMaxPathSize, &extLen);
extBuff[extLen] = '\0'; }
fd = open(path, O_RDONLY, 0777);
if (fd < 0) {
if (extension) {
CFRelease(extension);
}
return NULL;
}
files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
while ((numread = getdirentries(fd, dirge, sizeof(dirge), &basep)) > 0) {
struct dirent *dent;
for (dent = (struct dirent *)dirge; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) {
CFURLRef fileURL;
CFIndex nameLen;
nameLen = dent->d_namlen;
if (0 == dent->d_ino || (dent->d_name[0] == '.' && (nameLen == 1 || (nameLen == 2 && dent->d_name[1] == '.')))) {
continue;
}
if (extLen > 0) {
if (strncmp(&(dent->d_name[nameLen - extLen]), extBuff, extLen) != 0) {
continue;
}
}
if (base == NULL) {
base = CFURLCreateFromFileSystemRepresentation(alloc, path, pathLength, TRUE);
releaseBase = TRUE;
}
if (dent->d_type == DT_DIR || dent->d_type == DT_UNKNOWN) {
Boolean isDir = (dent->d_type == DT_DIR);
if (!isDir) {
char subdirPath[CFMaxPathLength];
struct stat statBuf;
strncpy(subdirPath, path, pathLength);
subdirPath[pathLength] = '/';
strncpy(subdirPath + pathLength + 1, dent->d_name, nameLen);
subdirPath[pathLength + nameLen + 1] = '\0';
if (stat(subdirPath, &statBuf) == 0) {
isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
}
}
fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dent->d_name, nameLen, isDir, base);
} else {
fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, dent->d_name, nameLen, FALSE, base);
}
CFArrayAppendValue(files, fileURL);
CFRelease(fileURL);
}
}
close(fd);
if (-1 == numread) {
CFRelease(files);
if (releaseBase) {
CFRelease(base);
}
if (extension) {
CFRelease(extension);
}
return NULL;
}
if (extension) {
CFRelease(extension);
}
if (releaseBase) {
CFRelease(base);
}
return files;
}
#endif
#define CF_OPENFLGS (0)
#define thread_set_errno(V) do {errno = (V);} while (0)
Boolean _IOReadBytesFromFile(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength) {
struct stat statBuf;
int fd = -1;
if (!alloc) {
return FALSE;
}
*bytes = NULL;
fd = open(path, O_RDONLY|CF_OPENFLGS, 0666);
if (fd < 0) {
return FALSE;
}
if (fstat(fd, &statBuf) < 0) {
int saveerr = thread_errno();
close(fd);
thread_set_errno(saveerr);
return FALSE;
}
if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
close(fd);
thread_set_errno(EACCES);
return FALSE;
}
if (statBuf.st_size == 0) {
*bytes = CFAllocatorAllocate(alloc, 4, 0); *length = 0;
} else {
CFIndex desiredLength;
if ((maxLength >= statBuf.st_size) || (maxLength == 0)) {
desiredLength = statBuf.st_size;
} else {
desiredLength = maxLength;
}
*bytes = CFAllocatorAllocate(alloc, desiredLength, 0);
if (read(fd, *bytes, desiredLength) < 0) {
CFAllocatorDeallocate(alloc, *bytes);
*bytes = NULL;
close(fd);
return FALSE;
}
*length = desiredLength;
}
close(fd);
return TRUE;
}
Boolean _IOWriteBytesToFile(const char *path, const void *bytes, CFIndex length) {
struct stat statBuf;
int fd = -1;
int mode, mask;
mask = umask(0);
umask(mask);
mode = 0666 & ~mask;
if (0 == stat(path, &statBuf)) {
mode = statBuf.st_mode;
} else if (thread_errno() != ENOENT) {
return FALSE;
}
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666);
if (fd < 0) {
return FALSE;
}
if (length && write(fd, bytes, length) != length) {
int saveerr = thread_errno();
close(fd);
thread_set_errno(saveerr);
return FALSE;
}
fsync(fd);
close(fd);
return TRUE;
}