1/*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include <IOKit/IOCFURLAccess.h>
24
25#include <unistd.h>
26#include <dirent.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <pwd.h>
30#include <fcntl.h>
31
32#include "IOKitInternal.h"
33
34/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
35/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
36
37#ifdef HAVE_CFURLACCESS
38
39CFTypeRef IOURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode)
40{
41    return (CFURLCreatePropertyFromResource(alloc, url, property, errorCode));
42}
43
44Boolean IOURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *resourceData, CFDictionaryRef *properties, CFArrayRef desiredProperties, SInt32 *errorCode)
45{
46    return (CFURLCreateDataAndPropertiesFromResource(alloc, url, resourceData, properties, desiredProperties, errorCode));
47}
48
49Boolean IOCFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef dataToWrite, CFDictionaryRef propertiesToWrite, SInt32 *errorCode)
50{
51    return (CFURLWriteDataAndPropertiesToResource(url, dataToWrite, propertiesToWrite, errorCode));
52}
53
54/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
55/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
56
57#else /* !HAVE_CFURLACCESS */
58
59/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
60/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
61
62#define kIOFileURLExists	CFSTR("kIOFileURLExists")
63#define kIOFileURLPOSIXMode	CFSTR("kIOFileURLPOSIXMode")
64#define kIOFileURLSize		CFSTR("kIOFileURLSize")
65
66static Boolean _IOFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode);
67static CFDictionaryRef _IOFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode);
68static Boolean _IOFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode);
69
70static CFMutableArrayRef _IOContentsOfDirectory(CFAllocatorRef alloc, char path[CFMaxPathLength], CFURLRef base, CFStringRef matchingAbstractType);
71extern Boolean _IOReadBytesFromFile(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength);
72extern Boolean _IOWriteBytesToFile(const char *path, const void *bytes, CFIndex length);
73
74CFTypeRef IOURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode) {
75    CFArrayRef array = CFArrayCreate(alloc, (const void **)&property, 1, &kCFTypeArrayCallBacks);
76    CFDictionaryRef dict;
77
78    if (IOURLCreateDataAndPropertiesFromResource(alloc, url, NULL, &dict, array, errorCode)) {
79        CFTypeRef result = CFDictionaryGetValue(dict, property);
80        if (result) CFRetain(result);
81        CFRelease(array);
82        CFRelease(dict);
83        return result;
84    } else {
85        if (dict) CFRelease(dict);
86        CFRelease(array);
87        return NULL;
88    }
89}
90
91Boolean IOURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFDictionaryRef *fetchedProperties, CFArrayRef desiredProperties, SInt32 *errorCode) {
92
93    CFStringRef scheme = CFURLCopyScheme(url);
94
95    if (!scheme) {
96        if (errorCode) *errorCode = kIOURLImproperArgumentsError;
97        if (fetchedData) *fetchedData = NULL;
98        if (fetchedProperties) *fetchedProperties = NULL;
99        return FALSE;
100    } else {
101        Boolean result;
102        if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
103            result = _IOFileURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode);
104        } else {
105            if (fetchedData) *fetchedData = NULL;
106            if (fetchedProperties) *fetchedProperties = NULL;
107            if (errorCode) *errorCode = kIOURLUnknownSchemeError;
108            result = FALSE;
109        }
110        CFRelease(scheme);
111        return result;
112    }
113}
114
115static Boolean _IOFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode) {
116    char buffer[CFMaxPathSize];
117    Boolean success = TRUE;
118
119    if (!CFURLGetFileSystemRepresentation(url, TRUE, buffer, CFMaxPathSize)) {
120        if (fetchedData) *fetchedData = NULL;
121        if (fetchedProperties) *fetchedProperties = NULL;
122        if (errorCode) *errorCode = kIOURLImproperArgumentsError;
123        return FALSE;
124    }
125
126    if (errorCode) *errorCode = 0;
127    if (fetchedData) {
128        void *bytes;
129        CFIndex length;
130        Boolean releaseAlloc = FALSE;
131
132        if (alloc == NULL) {
133            // We need a real allocator to pass to _IOReadBytesFromFile
134            alloc = CFRetain(CFAllocatorGetDefault());
135            releaseAlloc = TRUE;
136        }
137        if (!_IOReadBytesFromFile(alloc, buffer, &bytes, &length, 0)) {
138            if (errorCode) *errorCode = kIOURLUnknownError;
139            *fetchedData = NULL;
140            success = FALSE;
141        } else {
142            *fetchedData = CFDataCreateWithBytesNoCopy(alloc, bytes, length, alloc);
143        }
144        if (releaseAlloc) {
145            // Now the CFData should be hanging on to it.
146            CFRelease(alloc);
147        }
148    }
149
150    if (fetchedProperties) {
151        *fetchedProperties = _IOFileURLCreatePropertiesFromResource(alloc, url, desiredProperties, errorCode);
152        if (!*fetchedProperties) success = FALSE;
153    }
154
155    return success;
156}
157
158Boolean IOURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDictionaryRef propertyDict, SInt32 *errorCode) {
159    CFStringRef scheme = CFURLCopyScheme(url);
160    if (!scheme) {
161        if (errorCode) *errorCode = kIOURLImproperArgumentsError;
162        return FALSE;
163    } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
164        Boolean success = TRUE;
165        CFRelease(scheme);
166        if (errorCode) *errorCode = 0;
167        if (data) {
168            char cPath[CFMaxPathSize];
169            if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
170                if (errorCode) *errorCode = kIOURLImproperArgumentsError;
171                success = FALSE;
172            } else if (CFURLHasDirectoryPath(url)) {
173                // Create a directory
174                success = !mkdir(cPath, 0777);
175                if (!success && errorCode) *errorCode = kIOURLUnknownError;
176            } else {
177               // Write data
178                SInt32 length = CFDataGetLength(data);
179                const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data);
180                success = _IOWriteBytesToFile(cPath, bytes, length);
181                if (!success && errorCode) *errorCode = kIOURLUnknownError;
182            }
183        }
184        if (propertyDict) {
185            if (!_IOFileURLWritePropertiesToResource(url, propertyDict, errorCode))
186                success = FALSE;
187        }
188        return success;
189    } else {
190        if (errorCode) *errorCode = kIOURLUnknownSchemeError;
191        return FALSE;
192    }
193}
194
195static Boolean _IOFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode) {
196    CFTypeRef buffer[16];
197    void **keys;
198    void **values;
199    Boolean result = TRUE;
200    SInt32 index, count;
201    char cPath[CFMaxPathSize];
202
203    if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
204        if (errorCode) *errorCode = kIOURLImproperArgumentsError;
205        return FALSE;
206    }
207
208    count = CFDictionaryGetCount(propertyDict);
209    if (count < 8) {
210        keys = buffer;
211        values = buffer+8;
212    } else {
213        keys = CFAllocatorAllocate(CFGetAllocator(url), sizeof(void *) * count * 2, 0);
214        values = keys + count;
215    }
216    CFDictionaryGetKeysAndValues(propertyDict, keys, values);
217
218    for (index = 0; index < count; index ++) {
219        CFStringRef key = keys[index];
220        CFTypeRef value = values[index];
221        if (CFEqual(key, kIOFileURLPOSIXMode) || CFEqual(key, kIOURLFilePOSIXMode)) {
222            SInt32 mode;
223            int err;
224            if (CFEqual(key, kIOURLFilePOSIXMode)) {
225                CFNumberRef modeNum = (CFNumberRef)value;
226                CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode);
227            } else {
228                const mode_t *modePtr = (const mode_t *)CFDataGetBytePtr((CFDataRef)value);
229                mode = *modePtr;
230            }
231            err = chmod(cPath, mode);
232            if (err != 0) result = FALSE;
233        } else {
234            result = FALSE;
235        }
236    }
237
238    if (keys != &buffer[0]) CFAllocatorDeallocate(CFGetAllocator(url), keys);
239
240    if (errorCode) *errorCode = result ? 0 : kIOURLUnknownError;
241    return result;
242}
243
244static CFDictionaryRef _IOFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode) {
245    static CFArrayRef _allProps = NULL;
246    char cPath[CFMaxPathSize];
247    SInt32 index, count, statResult = 0;
248    CFMutableDictionaryRef propertyDict = NULL;
249    struct stat statBuf;
250    Boolean statCompleted = FALSE;
251
252    if (!CFURLGetFileSystemRepresentation(url, TRUE, cPath, CFMaxPathSize)) {
253        if (errorCode) *errorCode = kIOURLImproperArgumentsError;
254        return NULL;
255    }
256    if (errorCode) *errorCode = 0;
257    if (!desiredProperties) {
258        // Cheap and dirty hack to make this work for the moment; ultimately we need to do something more sophisticated.  This will result in an error return whenever a property key is defined which isn't applicable to all file URLs.  REW, 3/2/99
259        if (!_allProps) {
260            const void *values[9];
261            values[0] = kIOURLFileExists;
262            values[1] = kIOURLFilePOSIXMode;
263            values[2] = kIOURLFileDirectoryContents;
264            values[3] = kIOURLFileLength;
265            values[4] = kIOURLFileLastModificationTime;
266            values[5] = kIOURLFileOwnerID;
267            values[6] = kIOFileURLExists;
268            values[7] = kIOFileURLPOSIXMode;
269            values[8] = kIOFileURLSize;
270            _allProps = CFArrayCreate(NULL, values, 8, &kCFTypeArrayCallBacks);
271        }
272        desiredProperties = _allProps;
273    }
274
275    count = CFArrayGetCount(desiredProperties);
276    propertyDict = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
277    if (count == 0) return propertyDict;
278    for (index = 0; index < count; index ++) {
279        CFStringRef key = (CFMutableStringRef )CFArrayGetValueAtIndex(desiredProperties, index);
280        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))) {
281            statResult = stat(cPath, &statBuf);
282            if (statResult != 0) statResult = thread_errno();
283            statCompleted = TRUE;
284        } else if (errorCode) {
285            *errorCode = kIOURLUnknownError;
286        }
287        if (CFEqual(key, kIOFileURLPOSIXMode)) {
288            if (statResult == 0) {
289                CFDataRef modeData = CFDataCreate(alloc, (void *)(&(statBuf.st_mode)), sizeof(statBuf.st_mode));
290                CFDictionarySetValue(propertyDict, kIOFileURLPOSIXMode, modeData);
291                CFRelease(modeData);
292            } else if (errorCode) {
293                *errorCode = kIOURLUnknownError;
294            }
295        } else if (CFEqual(key, kIOURLFilePOSIXMode)) {
296            if (statResult == 0) {
297                SInt32 value = statBuf.st_mode;
298                CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &value);
299                CFDictionarySetValue(propertyDict, kIOURLFilePOSIXMode, num);
300                CFRelease(num);
301            } else if (errorCode) {
302                *errorCode = kIOURLUnknownError;
303            }
304        } else if (CFEqual(key, kIOURLFileDirectoryContents)) {
305            if (statResult == 0 && (statBuf.st_mode & S_IFMT) == S_IFDIR) {
306                CFMutableArrayRef contents = _IOContentsOfDirectory(alloc, cPath, url, NULL);
307                if (contents) {
308                    CFDictionarySetValue(propertyDict, kIOURLFileDirectoryContents, contents);
309                    CFRelease(contents);
310                } else if (errorCode) {
311                    *errorCode = kIOURLUnknownError;
312                }
313            } else if (errorCode) {
314                *errorCode = kIOURLUnknownError;
315            }
316        } else if (CFEqual(key, kIOFileURLSize)) {
317            if (statResult == 0) {
318                UInt64 length = statBuf.st_size;
319                CFDataRef tmpData = CFDataCreate(alloc, (void *)(&length), sizeof(UInt64));
320                CFDictionarySetValue(propertyDict, kIOFileURLSize, tmpData);
321                CFRelease(tmpData);
322            } else if (errorCode) {
323                *errorCode = kIOURLUnknownError;
324            }
325        } else if (CFEqual(key, kIOURLFileLength)) {
326            if (statResult == 0) {
327                SInt64 length = statBuf.st_size;
328                CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt64Type, &length);
329                CFDictionarySetValue(propertyDict, kIOURLFileLength, num);
330                CFRelease(num);
331            } else if (errorCode) {
332                *errorCode = kIOURLUnknownError;
333            }
334        } else if (CFEqual(key, kIOURLFileLastModificationTime)) {
335            if (statResult == 0) {
336                CFDateRef date = CFDateCreate(alloc, statBuf.st_mtime - kCFAbsoluteTimeIntervalSince1970);
337                CFDictionarySetValue(propertyDict, kIOURLFileLastModificationTime, date);
338                CFRelease(date);
339            } else if (errorCode) {
340                *errorCode = kIOURLUnknownError;
341            }
342        } else if (CFEqual(key, kIOURLFileExists)) {
343            if (statResult == 0) {
344                CFDictionarySetValue(propertyDict, kIOURLFileExists, kCFBooleanTrue);
345            } else if (statResult == ENOENT) {
346                CFDictionarySetValue(propertyDict, kIOURLFileExists, kCFBooleanFalse);
347            } else if (errorCode) {
348                *errorCode = kIOURLUnknownError;
349            }
350        } else if (CFEqual(key, kIOFileURLExists)) {
351            if (statResult == 0) {
352                CFDictionarySetValue(propertyDict, kIOFileURLExists, kIOFileURLExists);
353            } else if (statResult == ENOENT) {
354                CFDictionarySetValue(propertyDict, kIOFileURLExists, kCFBooleanFalse); // Choose any value other than kIOFileURLExists to designate non-existance.  Note that we cannot use NULL, since the dictionary has value callbacks kCFTypeDictionaryValueCallBacks, which is not tolerant of NULL values.
355            } else if (errorCode) {
356                *errorCode = kIOURLUnknownError;
357            }
358        } else if (CFEqual(key, kIOURLFileOwnerID)) {
359            if (statResult == 0) {
360                SInt32 uid = statBuf.st_uid;
361                CFNumberRef num  = CFNumberCreate(alloc, kCFNumberSInt32Type, &uid);
362                CFDictionarySetValue(propertyDict, kIOURLFileOwnerID, num);
363                CFRelease(num);
364            } else if (errorCode) {
365                *errorCode = kIOURLUnknownError;
366            }
367        // Add more properties here
368        } else if (errorCode) {
369            *errorCode = kIOURLUnknownPropertyKeyError;
370        }
371    }
372    return propertyDict;
373}
374
375
376// File Utilities
377
378// Note: as of November 2006, the matchingAbstractType isn't used at this function's only call site
379static CFMutableArrayRef _IOContentsOfDirectory(CFAllocatorRef alloc, char path[CFMaxPathLength], CFURLRef base, CFStringRef matchingAbstractType) {
380    CFMutableArrayRef files;
381    Boolean releaseBase = FALSE;
382    CFIndex pathLength = strlen(path);
383    // MF:!!! Need to use four-letter type codes where appropriate.
384    CFStringRef extension = (matchingAbstractType ? CFRetain(matchingAbstractType) : NULL);
385    CFIndex extLen = (extension ? CFStringGetLength(extension) : 0);
386    char extBuff[CFMaxPathSize];
387
388    int fd, numread;
389    long basep;
390    char dirge[8192];
391
392    if (extLen > 0) {
393	// not sure what extension might contain ... currently unused
394        CFStringGetBytes(extension, CFRangeMake(0, extLen), kCFStringEncodingMacRoman, 0, FALSE, extBuff, CFMaxPathSize, &extLen);
395        extBuff[extLen] = '\0';	    // CFStringGetBytes set extLen to number of bytes in converted string
396    }
397
398    fd = open(path, O_RDONLY, 0777);
399    if (fd < 0) {
400        if (extension) {
401            CFRelease(extension);
402        }
403        return NULL;
404    }
405    files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
406
407    while ((numread = getdirentries(fd, dirge, sizeof(dirge), &basep)) > 0) {
408        struct dirent *dent;
409        for (dent = (struct dirent *)dirge; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) {
410            CFURLRef fileURL;
411            CFIndex nameLen;
412
413            nameLen = dent->d_namlen;
414            // skip . & ..; they cause descenders to go berserk
415            if (0 == dent->d_ino /*d_fileno*/ || (dent->d_name[0] == '.' && (nameLen == 1 || (nameLen == 2 && dent->d_name[1] == '.')))) {
416                continue;
417            }
418            if (extLen > 0) {
419                // Check to see if it matches the extension we're looking for.
420                if (strncmp(&(dent->d_name[nameLen - extLen]), extBuff, extLen) != 0) {
421                    continue;
422                }
423            }
424            if (base == NULL) {
425                base = CFURLCreateFromFileSystemRepresentation(alloc, path, pathLength, TRUE);
426                releaseBase = TRUE;
427            }
428
429            if (dent->d_type == DT_DIR || dent->d_type == DT_UNKNOWN) {
430                Boolean isDir = (dent->d_type == DT_DIR);
431                if (!isDir) {
432                    // Ugh; must stat.
433                    char subdirPath[CFMaxPathLength];
434                    struct stat statBuf;
435                    strncpy(subdirPath, path, pathLength);
436                    subdirPath[pathLength] = '/';
437                    strncpy(subdirPath + pathLength + 1, dent->d_name, nameLen);
438                    subdirPath[pathLength + nameLen + 1] = '\0';
439                    if (stat(subdirPath, &statBuf) == 0) {
440                        isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
441                    }
442                }
443                fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dent->d_name, nameLen, isDir, base);
444            } else {
445                fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, dent->d_name, nameLen, FALSE, base);
446            }
447            CFArrayAppendValue(files, fileURL);
448            CFRelease(fileURL);
449        }
450    }
451    close(fd);
452    if  (-1 == numread) {
453        CFRelease(files);
454        if (releaseBase) {
455            CFRelease(base);
456        }
457        if (extension) {
458            CFRelease(extension);
459        }
460        return NULL;
461    }
462
463    if (extension) {
464        CFRelease(extension);
465    }
466    if (releaseBase) {
467        CFRelease(base);
468    }
469    return files;
470}
471
472#endif /* !HAVE_CFURLACCESS */
473/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
474/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
475
476#define CF_OPENFLGS (0)
477#define thread_set_errno(V) do {errno = (V);} while (0)
478
479/* Note: _IOReadBytesFromFile is called from PowerManagament project daemon powerd
480 *   e.g. it has references outside of this file.
481 */
482Boolean _IOReadBytesFromFile(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength) {
483    // alloc must be a valid allocator.
484    // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
485    struct stat statBuf;
486    int fd = -1;
487
488    if (!alloc) {
489        // MF:!!! This is no good.  This function needs to require a non-NULL allocator.  We should probably log or assert or something.
490        return FALSE;
491    }
492
493    *bytes = NULL;
494    fd = open(path, O_RDONLY|CF_OPENFLGS, 0666);
495    if (fd < 0) {
496        return FALSE;
497    }
498    if (fstat(fd, &statBuf) < 0) {
499        int saveerr = thread_errno();
500        close(fd);
501        thread_set_errno(saveerr);
502        return FALSE;
503    }
504    if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
505        close(fd);
506        thread_set_errno(EACCES);
507        return FALSE;
508    }
509    if (statBuf.st_size == 0) {
510        *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed!
511        *length = 0;
512    } else {
513        CFIndex desiredLength;
514        if ((maxLength >= statBuf.st_size) || (maxLength == 0)) {
515            desiredLength = statBuf.st_size;
516        } else {
517            desiredLength = maxLength;
518        }
519        *bytes = CFAllocatorAllocate(alloc, desiredLength, 0);
520        if (read(fd, *bytes, desiredLength) < 0) {
521            CFAllocatorDeallocate(alloc, *bytes);
522            *bytes = NULL;
523            close(fd);
524            return FALSE;
525        }
526        *length = desiredLength;
527    }
528    close(fd);
529    return TRUE;
530}
531
532Boolean _IOWriteBytesToFile(const char *path, const void *bytes, CFIndex length) {
533    struct stat statBuf;
534    int fd = -1;
535    int mode, mask;
536
537    mask = umask(0);
538    umask(mask);
539    mode = 0666 & ~mask;
540    if (0 == stat(path, &statBuf)) {
541        mode = statBuf.st_mode;
542    } else if (thread_errno() != ENOENT) {
543        return FALSE;
544    }
545    fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666);
546    if (fd < 0) {
547        return FALSE;
548    }
549    if (length && write(fd, bytes, length) != length) {
550        int saveerr = thread_errno();
551        close(fd);
552        thread_set_errno(saveerr);
553        return FALSE;
554    }
555    fsync(fd);
556    close(fd);
557    return TRUE;
558}
559