]> git.saurik.com Git - apple/cf.git/blob - CFFileUtilities.c
CF-476.17.tar.gz
[apple/cf.git] / CFFileUtilities.c
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 /* CFFileUtilities.c
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #include "CFInternal.h"
29 #include "CFPriv.h"
30 #include <string.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <pwd.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <stdio.h>
39
40 #define CF_OPENFLGS (0)
41
42
43 __private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) {
44 return (abstractType ? (CFStringRef)CFRetain(abstractType) : NULL);
45 }
46
47
48 __private_extern__ Boolean _CFCreateDirectory(const char *path) {
49 #if 0 || 0
50 return CreateDirectoryA(path, (LPSECURITY_ATTRIBUTES)NULL);
51 #else
52 int no_hang_fd = open("/dev/autofs_nowait", 0);
53 int ret = ((mkdir(path, 0777) == 0) ? true : false);
54 close(no_hang_fd);
55 return ret;
56 #endif
57 }
58
59 __private_extern__ Boolean _CFRemoveDirectory(const char *path) {
60 #if 0 || 0
61 return RemoveDirectoryA(path);
62 #else
63 int no_hang_fd = open("/dev/autofs_nowait", 0);
64 int ret = ((rmdir(path) == 0) ? true : false);
65 close(no_hang_fd);
66 return ret;
67 #endif
68 }
69
70 __private_extern__ Boolean _CFDeleteFile(const char *path) {
71 #if 0 || 0
72 return DeleteFileA(path);
73 #else
74 int no_hang_fd = open("/dev/autofs_nowait", 0);
75 int ret = unlink(path) == 0;
76 close(no_hang_fd);
77 return ret;
78 #endif
79 }
80
81 __private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength) {
82 // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
83 struct stat statBuf;
84 int fd = -1;
85 char path[CFMaxPathSize];
86 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) {
87 return false;
88 }
89
90 *bytes = NULL;
91
92
93 #if 0 || 0
94 fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD);
95 #else
96 int no_hang_fd = open("/dev/autofs_nowait", 0);
97 fd = open(path, O_RDONLY|CF_OPENFLGS, 0666);
98 #endif
99 if (fd < 0) {
100 close(no_hang_fd);
101 return false;
102 }
103 if (fstat(fd, &statBuf) < 0) {
104 int saveerr = thread_errno();
105 close(fd);
106 close(no_hang_fd);
107 thread_set_errno(saveerr);
108 return false;
109 }
110 if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
111 close(fd);
112 close(no_hang_fd);
113 thread_set_errno(EACCES);
114 return false;
115 }
116 if (statBuf.st_size == 0) {
117 *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed!
118 if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)");
119 *length = 0;
120 } else {
121 CFIndex desiredLength;
122 if ((maxLength >= statBuf.st_size) || (maxLength == 0)) {
123 desiredLength = statBuf.st_size;
124 } else {
125 desiredLength = maxLength;
126 }
127 *bytes = CFAllocatorAllocate(alloc, desiredLength, 0);
128 if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)");
129 // fcntl(fd, F_NOCACHE, 1);
130 if (read(fd, *bytes, desiredLength) < 0) {
131 CFAllocatorDeallocate(alloc, *bytes);
132 close(fd);
133 close(no_hang_fd);
134 return false;
135 }
136 *length = desiredLength;
137 }
138 close(fd);
139 close(no_hang_fd);
140 return true;
141 }
142
143 __private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) {
144 struct stat statBuf;
145 int fd = -1;
146 int mode;
147 char path[CFMaxPathSize];
148 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) {
149 return false;
150 }
151
152 #if 0 || 0
153 mode = 0666;
154 if (0 == stat(path, &statBuf)) {
155 mode = statBuf.st_mode;
156 } else if (thread_errno() != ENOENT) {
157 return false;
158 }
159 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666|_S_IWRITE);
160 if (fd < 0) {
161 return false;
162 }
163 if (length && write(fd, bytes, length) != length) {
164 int saveerr = thread_errno();
165 close(fd);
166 thread_set_errno(saveerr);
167 return false;
168 }
169 FlushFileBuffers((HANDLE)_get_osfhandle(fd));
170 close(fd);
171 #else
172 int no_hang_fd = open("/dev/autofs_nowait", 0);
173 mode = 0666;
174 if (0 == stat(path, &statBuf)) {
175 mode = statBuf.st_mode;
176 } else if (thread_errno() != ENOENT) {
177 close(no_hang_fd);
178 return false;
179 }
180 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666);
181 if (fd < 0) {
182 close(no_hang_fd);
183 return false;
184 }
185 if (length && write(fd, bytes, length) != length) {
186 int saveerr = thread_errno();
187 close(fd);
188 close(no_hang_fd);
189 thread_set_errno(saveerr);
190 return false;
191 }
192 fsync(fd);
193 close(fd);
194 close(no_hang_fd);
195 #endif
196 return true;
197 }
198
199
200 /* On Mac OS 8/9, one of dirSpec and dirURL must be non-NULL. On all other platforms, one of path and dirURL must be non-NULL
201 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */
202 /* Lately, dirSpec appears to be (rightfully) unused. */
203 __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) {
204 CFMutableArrayRef files = NULL;
205 Boolean releaseBase = false;
206 CFIndex pathLength = dirPath ? strlen(dirPath) : 0;
207 // MF:!!! Need to use four-letter type codes where appropriate.
208 CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL);
209 CFIndex extLen = (extension ? CFStringGetLength(extension) : 0);
210 uint8_t extBuff[CFMaxPathSize];
211
212 if (extLen > 0) {
213 CFStringGetBytes(extension, CFRangeMake(0, extLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathLength, &extLen);
214 extBuff[extLen] = '\0';
215 }
216
217 uint8_t pathBuf[CFMaxPathSize];
218
219 if (!dirPath) {
220 if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) {
221 if (extension) CFRelease(extension);
222 return NULL;
223 } else {
224 dirPath = (char *)pathBuf;
225 pathLength = strlen(dirPath);
226 }
227 }
228
229 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
230 struct dirent buffer;
231 struct dirent *dp;
232 int err;
233
234 int no_hang_fd = open("/dev/autofs_nowait", 0);
235
236 DIR *dirp = opendir(dirPath);
237 if (!dirp) {
238 if (extension) {
239 CFRelease(extension);
240 }
241 close(no_hang_fd);
242 return NULL;
243 // raiseErrno("opendir", path);
244 }
245 files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks);
246
247 while((0 == readdir_r(dirp, &buffer, &dp)) && dp) {
248 CFURLRef fileURL;
249 unsigned namelen = strlen(dp->d_name);
250
251 // skip . & ..; they cause descenders to go berserk
252 if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) {
253 continue;
254 }
255
256 if (extLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match.
257
258 if (extLen > 0) {
259 // Check to see if it matches the extension we're looking for.
260 if (strncmp(&(dp->d_name[namelen - extLen]), (char *)extBuff, extLen) != 0) {
261 continue;
262 }
263 }
264 if (dirURL == NULL) {
265 dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (uint8_t *)dirPath, pathLength, true);
266 releaseBase = true;
267 }
268 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) {
269 Boolean isDir = (dp->d_type == DT_DIR);
270 if (!isDir) {
271 // Ugh; must stat.
272 char subdirPath[CFMaxPathLength];
273 struct stat statBuf;
274 strlcpy(subdirPath, dirPath, sizeof(subdirPath));
275 strlcat(subdirPath, "/", sizeof(subdirPath));
276 strlcat(subdirPath, dp->d_name, sizeof(subdirPath));
277 if (stat(subdirPath, &statBuf) == 0) {
278 isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
279 }
280 }
281 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, dp->d_namlen, isDir, dirURL);
282 } else {
283 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, dp->d_namlen, false, dirURL);
284 }
285 CFArrayAppendValue(files, fileURL);
286 CFRelease(fileURL);
287 }
288 err = closedir(dirp);
289 close(no_hang_fd);
290 if (err != 0) {
291 CFRelease(files);
292 if (releaseBase) {
293 CFRelease(dirURL);
294 }
295 if (extension) {
296 CFRelease(extension);
297 }
298 return NULL;
299 }
300
301 #else
302
303 #error _CFContentsOfDirectory() unknown architechture, not implemented
304
305 #endif
306
307 if (extension) {
308 CFRelease(extension);
309 }
310 if (releaseBase) {
311 CFRelease(dirURL);
312 }
313 return files;
314 }
315
316 __private_extern__ SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) {
317 Boolean fileExists;
318 Boolean isDirectory = false;
319
320 struct stat64 statBuf;
321 char path[CFMaxPathSize];
322
323 if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) {
324 // Nothing to do.
325 return 0;
326 }
327
328 if (!CFURLGetFileSystemRepresentation(pathURL, true, (uint8_t *)path, CFMaxPathLength)) {
329 return -1;
330 }
331
332 if (stat64(path, &statBuf) != 0) {
333 // stat failed, but why?
334 if (thread_errno() == ENOENT) {
335 fileExists = false;
336 } else {
337 return thread_errno();
338 }
339 } else {
340 fileExists = true;
341 isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
342 }
343
344
345 if (exists != NULL) {
346 *exists = fileExists;
347 }
348
349 if (posixMode != NULL) {
350 if (fileExists) {
351
352 *posixMode = statBuf.st_mode;
353
354 } else {
355 *posixMode = 0;
356 }
357 }
358
359 if (size != NULL) {
360 if (fileExists) {
361
362 *size = statBuf.st_size;
363
364 } else {
365 *size = 0;
366 }
367 }
368
369 if (modTime != NULL) {
370 if (fileExists) {
371 CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtimespec.tv_sec - kCFAbsoluteTimeIntervalSince1970;
372 theTime += (CFAbsoluteTime)statBuf.st_mtimespec.tv_nsec / 1000000000.0;
373 *modTime = CFDateCreate(alloc, theTime);
374 } else {
375 *modTime = NULL;
376 }
377 }
378
379 if (ownerID != NULL) {
380 if (fileExists) {
381
382 *ownerID = statBuf.st_uid;
383
384 } else {
385 *ownerID = -1;
386 }
387 }
388
389 if (dirContents != NULL) {
390 if (fileExists && isDirectory) {
391
392 CFMutableArrayRef contents = _CFContentsOfDirectory(alloc, path, NULL, pathURL, NULL);
393
394 if (contents) {
395 *dirContents = contents;
396 } else {
397 *dirContents = NULL;
398 }
399 } else {
400 *dirContents = NULL;
401 }
402 }
403 return 0;
404 }
405
406
407 // MF:!!! Should pull in the rest of the UniChar based path utils from Foundation.
408 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
409 #define UNIX_PATH_SEMANTICS
410 #elif defined(__WIN32__)
411 #define WINDOWS_PATH_SEMANTICS
412 #else
413 #error Unknown platform
414 #endif
415
416 #if defined(WINDOWS_PATH_SEMANTICS)
417 #define CFPreferredSlash ((UniChar)'\\')
418 #elif defined(UNIX_PATH_SEMANTICS)
419 #define CFPreferredSlash ((UniChar)'/')
420 #elif defined(HFS_PATH_SEMANTICS)
421 #define CFPreferredSlash ((UniChar)':')
422 #else
423 #error Cannot define NSPreferredSlash on this platform
424 #endif
425
426 #if defined(HFS_PATH_SEMANTICS)
427 #define HAS_DRIVE(S) (false)
428 #define HAS_NET(S) (false)
429 #else
430 #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z')))
431 #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\')
432 #endif
433
434 #if defined(WINDOWS_PATH_SEMANTICS)
435 #define IS_SLASH(C) ((C) == '\\' || (C) == '/')
436 #elif defined(UNIX_PATH_SEMANTICS)
437 #define IS_SLASH(C) ((C) == '/')
438 #elif defined(HFS_PATH_SEMANTICS)
439 #define IS_SLASH(C) ((C) == ':')
440 #endif
441
442 __private_extern__ Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) {
443 if (length < 1) {
444 return false;
445 }
446 #if defined(WINDOWS_PATH_SEMANTICS)
447 if (unichars[0] == '~') {
448 return true;
449 }
450 if (length < 2) {
451 return false;
452 }
453 if (HAS_NET(unichars)) {
454 return true;
455 }
456 if (length < 3) {
457 return false;
458 }
459 if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) {
460 return true;
461 }
462 #elif defined(HFS_PATH_SEMANTICS)
463 return !IS_SLASH(unichars[0]);
464 #else
465 if (unichars[0] == '~') {
466 return true;
467 }
468 if (IS_SLASH(unichars[0])) {
469 return true;
470 }
471 #endif
472 return false;
473 }
474
475 __private_extern__ Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) {
476 Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars);
477 CFIndex oldLength = *length;
478 while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) {
479 (*length)--;
480 }
481 return (oldLength != *length);
482 }
483
484 __private_extern__ Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) {
485 if (0 == componentLength) {
486 return true;
487 }
488 if (maxLength < *length + 1 + componentLength) {
489 return false;
490 }
491 switch (*length) {
492 case 0:
493 break;
494 case 1:
495 if (!IS_SLASH(unichars[0])) {
496 unichars[(*length)++] = CFPreferredSlash;
497 }
498 break;
499 case 2:
500 if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) {
501 unichars[(*length)++] = CFPreferredSlash;
502 }
503 break;
504 default:
505 unichars[(*length)++] = CFPreferredSlash;
506 break;
507 }
508 memmove(unichars + *length, component, componentLength * sizeof(UniChar));
509 *length += componentLength;
510 return true;
511 }
512
513 __private_extern__ Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) {
514 if (maxLength < *length + 1 + extensionLength) {
515 return false;
516 }
517 if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) {
518 return false;
519 }
520 _CFStripTrailingPathSlashes(unichars, length);
521 switch (*length) {
522 case 0:
523 return false;
524 case 1:
525 if (IS_SLASH(unichars[0]) || unichars[0] == '~') {
526 return false;
527 }
528 break;
529 case 2:
530 if (HAS_DRIVE(unichars) || HAS_NET(unichars)) {
531 return false;
532 }
533 break;
534 case 3:
535 if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) {
536 return false;
537 }
538 break;
539 }
540 if (0 < *length && unichars[0] == '~') {
541 CFIndex idx;
542 Boolean hasSlash = false;
543 for (idx = 1; idx < *length; idx++) {
544 if (IS_SLASH(unichars[idx])) {
545 hasSlash = true;
546 break;
547 }
548 }
549 if (!hasSlash) {
550 return false;
551 }
552 }
553 unichars[(*length)++] = '.';
554 memmove(unichars + *length, extension, extensionLength * sizeof(UniChar));
555 *length += extensionLength;
556 return true;
557 }
558
559 __private_extern__ Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) {
560 CFIndex didx, sidx, scnt = *length;
561 sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0;
562 didx = sidx;
563 while (sidx < scnt) {
564 if (IS_SLASH(unichars[sidx])) {
565 unichars[didx++] = replSlash;
566 for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++);
567 } else {
568 unichars[didx++] = unichars[sidx++];
569 }
570 }
571 *length = didx;
572 return (scnt != didx);
573 }
574
575 __private_extern__ CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) {
576 CFIndex idx;
577 if (length < 2) {
578 return 0;
579 }
580 for (idx = length - 1; idx; idx--) {
581 if (IS_SLASH(unichars[idx - 1])) {
582 return idx;
583 }
584 }
585 if ((2 < length) && HAS_DRIVE(unichars)) {
586 return 2;
587 }
588 return 0;
589 }
590
591 __private_extern__ CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) {
592 CFIndex idx;
593 if (length < 2) {
594 return 0;
595 }
596 for (idx = length - 1; idx; idx--) {
597 if (IS_SLASH(unichars[idx - 1])) {
598 if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) {
599 return idx - 1;
600 }
601 return idx;
602 }
603 }
604 if ((2 < length) && HAS_DRIVE(unichars)) {
605 return 2;
606 }
607 return 0;
608 }
609
610 __private_extern__ CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) {
611 CFIndex idx;
612 if (length < 2) {
613 return 0;
614 }
615 for (idx = length - 1; idx; idx--) {
616 if (IS_SLASH(unichars[idx - 1])) {
617 return 0;
618 }
619 if (unichars[idx] != '.') {
620 continue;
621 }
622 if (idx == 2 && HAS_DRIVE(unichars)) {
623 return 0;
624 }
625 return idx;
626 }
627 return 0;
628 }
629
630 __private_extern__ CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) {
631 CFIndex start = _CFStartOfPathExtension(unichars, length);
632 return ((0 < start) ? start : length);
633 }
634
635 #undef CF_OPENFLGS
636 #undef UNIX_PATH_SEMANTICS
637 #undef WINDOWS_PATH_SEMANTICS
638 #undef HFS_PATH_SEMANTICS
639 #undef CFPreferredSlash
640 #undef HAS_DRIVE
641 #undef HAS_NET
642 #undef IS_SLASH
643