/core/sdk-objc/GMUserFileSystem.m
Objective C | 2484 lines | 2062 code | 255 blank | 167 comment | 352 complexity | 1e8431709af237e9cc9653a90d46f833 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1// ================================================================ 2// Copyright (c) 2007, Google Inc. 3// All rights reserved. 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30// ================================================================ 31// 32// GMUserFileSystem.m 33// 34// Created by ted on 12/29/07. 35// Based on FUSEFileSystem originally by alcor. 36// 37#import "GMUserFileSystem.h" 38 39#define FUSE_USE_VERSION 26 40#include <fuse.h> 41#include <fuse/fuse_darwin.h> 42 43#include <string.h> 44#include <errno.h> 45#include <fcntl.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <unistd.h> 49#include <sys/param.h> 50#include <sys/mount.h> 51#include <sys/ioctl.h> 52#include <sys/sysctl.h> 53#include <sys/utsname.h> 54 55#import <Foundation/Foundation.h> 56#import "GMAppleDouble.h" 57#import "GMFinderInfo.h" 58#import "GMResourceFork.h" 59#import "GMDataBackedFileDelegate.h" 60 61#import "GMDTrace.h" 62 63#define GM_EXPORT __attribute__((visibility("default"))) 64 65// Creates a dtrace-ready string with any newlines removed. 66#define DTRACE_STRING(s) \ 67((char *)[[s stringByReplacingOccurrencesOfString:@"\n" withString:@" "] UTF8String]) 68 69// Notifications 70GM_EXPORT NSString* const kGMUserFileSystemErrorDomain = @"GMUserFileSystemErrorDomain"; 71GM_EXPORT NSString* const kGMUserFileSystemMountPathKey = @"mountPath"; 72GM_EXPORT NSString* const kGMUserFileSystemErrorKey = @"error"; 73GM_EXPORT NSString* const kGMUserFileSystemMountFailed = @"kGMUserFileSystemMountFailed"; 74GM_EXPORT NSString* const kGMUserFileSystemDidMount = @"kGMUserFileSystemDidMount"; 75GM_EXPORT NSString* const kGMUserFileSystemDidUnmount = @"kGMUserFileSystemDidUnmount"; 76 77// Attribute keys 78GM_EXPORT NSString* const kGMUserFileSystemFileFlagsKey = @"kGMUserFileSystemFileFlagsKey"; 79GM_EXPORT NSString* const kGMUserFileSystemFileAccessDateKey = @"kGMUserFileSystemFileAccessDateKey"; 80GM_EXPORT NSString* const kGMUserFileSystemFileChangeDateKey = @"kGMUserFileSystemFileChangeDateKey"; 81GM_EXPORT NSString* const kGMUserFileSystemFileBackupDateKey = @"kGMUserFileSystemFileBackupDateKey"; 82GM_EXPORT NSString* const kGMUserFileSystemVolumeSupportsExtendedDatesKey = @"kGMUserFileSystemVolumeSupportsExtendedDatesKey"; 83 84// TODO: Remove comment on EXPORT if/when setvolname is supported. 85/* GM_EXPORT */ NSString* const kGMUserFileSystemVolumeSupportsSetVolumeNameKey = @"kGMUserFileSystemVolumeSupportsSetVolumeNameKey"; 86/* GM_EXPORT */ NSString* const kGMUserFileSystemVolumeNameKey = @"kGMUserFileSystemVolumeNameKey"; 87 88// FinderInfo and ResourceFork keys 89GM_EXPORT NSString* const kGMUserFileSystemFinderFlagsKey = @"kGMUserFileSystemFinderFlagsKey"; 90GM_EXPORT NSString* const kGMUserFileSystemFinderExtendedFlagsKey = @"kGMUserFileSystemFinderExtendedFlagsKey"; 91GM_EXPORT NSString* const kGMUserFileSystemCustomIconDataKey = @"kGMUserFileSystemCustomIconDataKey"; 92GM_EXPORT NSString* const kGMUserFileSystemWeblocURLKey = @"kGMUserFileSystemWeblocURLKey"; 93 94// Used for time conversions to/from tv_nsec. 95static const double kNanoSecondsPerSecond = 1000000000.0; 96 97typedef enum { 98 // Unable to unmount a dead FUSE files system located at mount point. 99 GMUserFileSystem_ERROR_UNMOUNT_DEADFS = 1000, 100 101 // Gave up waiting for system removal of existing dir in /Volumes/x after 102 // unmounting a dead FUSE file system. 103 GMUserFileSystem_ERROR_UNMOUNT_DEADFS_RMDIR = 1001, 104 105 // The mount point did not exist, and we were unable to mkdir it. 106 GMUserFileSystem_ERROR_MOUNT_MKDIR = 1002, 107 108 // fuse_main returned while trying to mount and don't know why. 109 GMUserFileSystem_ERROR_MOUNT_FUSE_MAIN_INTERNAL = 1003, 110} GMUserFileSystemErrorCode; 111 112typedef enum { 113 GMUserFileSystem_NOT_MOUNTED, // Not mounted. 114 GMUserFileSystem_MOUNTING, // In the process of mounting. 115 GMUserFileSystem_INITIALIZING, // Almost done mounting. 116 GMUserFileSystem_MOUNTED, // Confirmed to be mounted. 117 GMUserFileSystem_UNMOUNTING, // In the process of unmounting. 118 GMUserFileSystem_FAILURE, // Failed state; probably a mount failure. 119} GMUserFileSystemStatus; 120 121@interface GMUserFileSystemInternal : NSObject { 122 NSString* mountPath_; 123 GMUserFileSystemStatus status_; 124 BOOL isTiger_; // Are we running on Tiger? 125 BOOL shouldCheckForResource_; // Try to handle FinderInfo/Resource Forks? 126 BOOL isThreadSafe_; // Is the delegate thread-safe? 127 BOOL supportsExtendedTimes_; // Delegate supports create and backup times? 128 BOOL supportsSetVolumeName_; // Delegate supports setvolname? 129 BOOL isReadOnly_; // Is this mounted read-only? 130 id delegate_; 131} 132- (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe; 133- (void)setDelegate:(id)delegate; 134@end 135 136@implementation GMUserFileSystemInternal 137 138- (id)init { 139 return [self initWithDelegate:nil isThreadSafe:NO]; 140} 141 142- (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe { 143 if ((self = [super init])) { 144 status_ = GMUserFileSystem_NOT_MOUNTED; 145 isThreadSafe_ = isThreadSafe; 146 supportsExtendedTimes_ = NO; 147 supportsSetVolumeName_ = NO; 148 isReadOnly_ = NO; 149 [self setDelegate:delegate]; 150 151 // Version 10.4 requires ._ to appear in directory listings. 152 long version = fuse_os_version_major_np(); 153 isTiger_ = (version < 9); 154 } 155 return self; 156} 157- (void)dealloc { 158 [mountPath_ release]; 159 [super dealloc]; 160} 161 162- (NSString *)mountPath { return mountPath_; } 163- (void)setMountPath:(NSString *)mountPath { 164 [mountPath_ autorelease]; 165 mountPath_ = [mountPath copy]; 166} 167- (GMUserFileSystemStatus)status { return status_; } 168- (void)setStatus:(GMUserFileSystemStatus)status { status_ = status; } 169- (BOOL)isThreadSafe { return isThreadSafe_; } 170- (BOOL)supportsExtendedTimes { return supportsExtendedTimes_; } 171- (void)setSupportsExtendedTimes:(BOOL)val { supportsExtendedTimes_ = val; } 172- (BOOL)supportsSetVolumeName { return supportsSetVolumeName_; } 173- (void)setSupportsSetVolumeName:(BOOL)val { supportsSetVolumeName_ = val; } 174- (BOOL)isTiger { return isTiger_; } 175- (BOOL)shouldCheckForResource { return shouldCheckForResource_; } 176- (BOOL)isReadOnly { return isReadOnly_; } 177- (void)setIsReadOnly:(BOOL)val { isReadOnly_ = val; } 178- (id)delegate { return delegate_; } 179- (void)setDelegate:(id)delegate { 180 delegate_ = delegate; 181 shouldCheckForResource_ = 182 [delegate_ respondsToSelector:@selector(finderAttributesAtPath:error:)] || 183 [delegate_ respondsToSelector:@selector(resourceAttributesAtPath:error:)] || 184 [delegate_ respondsToSelector:@selector(finderFlagsAtPath:)] || 185 [delegate_ respondsToSelector:@selector(iconDataAtPath:)] || 186 [delegate_ respondsToSelector:@selector(URLOfWeblocAtPath:)]; 187 188 // Check for deprecated methods. 189 SEL deprecatedMethods[] = { 190 @selector(valueOfExtendedAttribute:ofItemAtPath:error:), 191 @selector(setExtendedAttribute:ofItemAtPath:value:flags:error:), 192 @selector(finderFlagsAtPath:), 193 @selector(iconDataAtPath:), 194 @selector(URLOfWeblocAtPath:), 195 @selector(truncateFileAtPath:offset:error:), 196 @selector(attributesOfItemAtPath:error:), 197 @selector(setAttributes:ofItemAtPath:error:), 198 @selector(openFileAtPath:mode:fileDelegate:error:), 199 @selector(createFileAtPath:attributes:fileDelegate:error:), 200 @selector(releaseFileAtPath:fileDelegate:), 201 @selector(readFileAtPath:fileDelegate:buffer:size:offset:error:), 202 @selector(writeFileAtPath:fileDelegate:buffer:size:offset:error:), 203 }; 204 int i; 205 for (i = 0; i < sizeof(deprecatedMethods)/sizeof(deprecatedMethods[0]); ++i) { 206 SEL sel = deprecatedMethods[i]; 207 if ([delegate_ respondsToSelector:sel]) { 208 NSLog(@"*** WARNING: GMUserFileSystem delegate implements deprecated " 209 @"selector: %@", NSStringFromSelector(sel)); 210 } 211 } 212} 213 214@end 215 216// Deprecated delegate methods that we still support for backward compatibility 217// with previously compiled file systems. This will be actively trimmed as 218// new releases occur. 219@interface NSObject (GMUserFileSystemDeprecated) 220- (NSData *)valueOfExtendedAttribute:(NSString *)name 221 ofItemAtPath:(NSString *)path 222 error:(NSError **)error; 223- (BOOL)setExtendedAttribute:(NSString *)name 224 ofItemAtPath:(NSString *)path 225 value:(NSData *)value 226 flags:(int)flags 227 error:(NSError **)error; 228- (UInt16)finderFlagsAtPath:(NSString *)path; 229- (NSData *)iconDataAtPath:(NSString *)path; 230- (NSURL *)URLOfWeblocAtPath:(NSString *)path; 231- (BOOL)truncateFileAtPath:(NSString *)path 232 offset:(off_t)offset 233 error:(NSError **)error; 234- (NSDictionary *)attributesOfItemAtPath:(NSString *)path 235 error:(NSError **)error; 236- (BOOL)setAttributes:(NSDictionary *)attributes 237 ofItemAtPath:(NSString *)path 238 error:(NSError **)error; 239- (BOOL)openFileAtPath:(NSString *)path 240 mode:(int)mode 241 fileDelegate:(id *)fileDelegate 242 error:(NSError **)error; 243- (BOOL)createFileAtPath:(NSString *)path 244 attributes:(NSDictionary *)attributes 245 fileDelegate:(id *)fileDelegate 246 error:(NSError **)error; 247- (void)releaseFileAtPath:(NSString *)path fileDelegate:(id)fileDelegate; 248- (int)readFileAtPath:(NSString *)path 249 fileDelegate:(id)fileDelegate 250 buffer:(char *)buffer 251 size:(size_t)size 252 offset:(off_t)offset 253 error:(NSError **)error; 254- (int)writeFileAtPath:(NSString *)path 255 fileDelegate:(id)fileDelegate 256 buffer:(const char *)buffer 257 size:(size_t)size 258 offset:(off_t)offset 259 error:(NSError **)error; 260@end 261 262@interface GMUserFileSystem (GMUserFileSystemPrivate) 263 264// The filesystem for the current thread. Valid only during a fuse callback. 265+ (GMUserFileSystem *)currentFS; 266 267// Convenience method to creates an autoreleased NSError in the 268// NSPOSIXErrorDomain. Filesystem errors returned by the delegate must be 269// standard posix errno values. 270+ (NSError *)errorWithCode:(int)code; 271 272- (void)mount:(NSDictionary *)args; 273- (void)waitUntilMounted; 274 275- (NSDictionary *)finderAttributesAtPath:(NSString *)path; 276- (NSDictionary *)resourceAttributesAtPath:(NSString *)path; 277 278- (BOOL)hasCustomIconAtPath:(NSString *)path; 279- (BOOL)isDirectoryIconAtPath:(NSString *)path dirPath:(NSString **)dirPath; 280- (BOOL)isAppleDoubleAtPath:(NSString *)path realPath:(NSString **)realPath; 281- (NSData *)finderDataForAttributes:(NSDictionary *)attributes; 282- (NSData *)resourceDataForAttributes:(NSDictionary *)attributes; 283- (NSData *)appleDoubleContentsAtPath:(NSString *)path; 284 285- (NSDictionary *)defaultAttributesOfItemAtPath:(NSString *)path 286 userData:userData 287 error:(NSError **)error; 288- (BOOL)fillStatBuffer:(struct stat *)stbuf 289 forPath:(NSString *)path 290 fileDelegate:(id)fileDelegate 291 error:(NSError **)error; 292- (BOOL)fillStatvfsBuffer:(struct statvfs *)stbuf 293 forPath:(NSString *)path 294 error:(NSError **)error; 295 296- (void)fuseInit; 297- (void)fuseDestroy; 298 299@end 300 301@implementation GMUserFileSystem 302 303- (id)init { 304 return [self initWithDelegate:nil isThreadSafe:NO]; 305} 306 307- (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe { 308 if ((self = [super init])) { 309 internal_ = [[GMUserFileSystemInternal alloc] initWithDelegate:delegate 310 isThreadSafe:isThreadSafe]; 311 } 312 return self; 313} 314 315- (void)dealloc { 316 [internal_ release]; 317 [super dealloc]; 318} 319 320- (void)setDelegate:(id)delegate { 321 [internal_ setDelegate:delegate]; 322} 323- (id)delegate { 324 return [internal_ delegate]; 325} 326 327- (BOOL)enableExtendedTimes { 328 return [internal_ supportsExtendedTimes]; 329} 330- (BOOL)enableSetVolumeName { 331 return [internal_ supportsSetVolumeName]; 332} 333 334- (void)mountAtPath:(NSString *)mountPath 335 withOptions:(NSArray *)options { 336 [self mountAtPath:mountPath 337 withOptions:options 338 shouldForeground:YES 339 detachNewThread:YES]; 340} 341 342- (void)mountAtPath:(NSString *)mountPath 343 withOptions:(NSArray *)options 344 shouldForeground:(BOOL)shouldForeground 345 detachNewThread:(BOOL)detachNewThread { 346 [internal_ setMountPath:mountPath]; 347 NSMutableArray* optionsCopy = [NSMutableArray array]; 348 for (int i = 0; i < [options count]; ++i) { 349 NSString* option = [options objectAtIndex:i]; 350 if ([option caseInsensitiveCompare:@"rdonly"] == NSOrderedSame || 351 [option caseInsensitiveCompare:@"ro"] == NSOrderedSame) { 352 [internal_ setIsReadOnly:YES]; 353 } 354 [optionsCopy addObject:[[option copy] autorelease]]; 355 } 356 NSDictionary* args = 357 [[NSDictionary alloc] initWithObjectsAndKeys: 358 optionsCopy, @"options", 359 [NSNumber numberWithBool:shouldForeground], @"shouldForeground", 360 nil, nil]; 361 if (detachNewThread) { 362 [NSThread detachNewThreadSelector:@selector(mount:) 363 toTarget:self 364 withObject:args]; 365 } else { 366 [self mount:args]; 367 } 368} 369 370- (void)unmount { 371 if ([internal_ status] == GMUserFileSystem_MOUNTED) { 372 NSArray* args = [NSArray arrayWithObjects:@"-v", [internal_ mountPath], nil]; 373 NSTask* unmountTask = [NSTask launchedTaskWithLaunchPath:@"/sbin/umount" 374 arguments:args]; 375 [unmountTask waitUntilExit]; 376 } 377} 378 379+ (NSError *)errorWithCode:(int)code { 380 return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil]; 381} 382 383+ (GMUserFileSystem *)currentFS { 384 struct fuse_context* context = fuse_get_context(); 385 assert(context); 386 return (GMUserFileSystem *)context->private_data; 387} 388 389#define FUSEDEVIOCGETHANDSHAKECOMPLETE _IOR('F', 2, u_int32_t) 390static const int kMaxWaitForMountTries = 50; 391static const int kWaitForMountUSleepInterval = 100000; // 100 ms 392- (void)waitUntilMounted { 393 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 394 395 for (int i = 0; i < kMaxWaitForMountTries; ++i) { 396 UInt32 handShakeComplete = 0; 397 int ret = ioctl(fuse_device_fd_np([[internal_ mountPath] UTF8String]), 398 FUSEDEVIOCGETHANDSHAKECOMPLETE, 399 &handShakeComplete); 400 if (ret == 0 && handShakeComplete) { 401 [internal_ setStatus:GMUserFileSystem_MOUNTED]; 402 403 // Successfully mounted, so post notification. 404 NSDictionary* userInfo = 405 [NSDictionary dictionaryWithObjectsAndKeys: 406 [internal_ mountPath], kGMUserFileSystemMountPathKey, 407 nil, nil]; 408 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 409 [center postNotificationName:kGMUserFileSystemDidMount object:self 410 userInfo:userInfo]; 411 [pool release]; 412 return; 413 } 414 usleep(kWaitForMountUSleepInterval); 415 } 416 417 // Tried for a long time and no luck :-( 418 // Unmount and report failure? 419 [pool release]; 420} 421 422- (void)fuseInit { 423 [internal_ setStatus:GMUserFileSystem_INITIALIZING]; 424 425 NSError* error = nil; 426 NSDictionary* attribs = [self attributesOfFileSystemForPath:@"/" error:&error]; 427 if (attribs) { 428 NSNumber* supports; 429 supports = [attribs objectForKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey]; 430 if (supports && [supports boolValue]) { 431 [internal_ setSupportsExtendedTimes:YES]; 432 } 433 supports = [attribs objectForKey:kGMUserFileSystemVolumeSupportsSetVolumeNameKey]; 434 if (supports && [supports boolValue]) { 435 [internal_ setSupportsSetVolumeName:YES]; 436 } 437 } 438 439 // The mount point won't actually show up until this winds its way 440 // back through the kernel after this routine returns. In order to post 441 // the kGMUserFileSystemDidMount notification we start a new thread that will 442 // poll until it is mounted. 443 [NSThread detachNewThreadSelector:@selector(waitUntilMounted) 444 toTarget:self 445 withObject:nil]; 446} 447 448- (void)fuseDestroy { 449 if ([[internal_ delegate] respondsToSelector:@selector(willUnmount)]) { 450 [[internal_ delegate] willUnmount]; 451 } 452 [internal_ setStatus:GMUserFileSystem_UNMOUNTING]; 453 454 NSDictionary* userInfo = 455 [NSDictionary dictionaryWithObjectsAndKeys: 456 [internal_ mountPath], kGMUserFileSystemMountPathKey, 457 nil, nil]; 458 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 459 [center postNotificationName:kGMUserFileSystemDidUnmount object:self 460 userInfo:userInfo]; 461 [internal_ setStatus:GMUserFileSystem_NOT_MOUNTED]; 462} 463 464#pragma mark Finder Info, Resource Forks and HFS headers 465 466- (NSDictionary *)finderAttributesAtPath:(NSString *)path { 467 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 468 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 469 } 470 471 UInt16 flags = 0; 472 473 // If a directory icon, we'll make invisible and update the path to parent. 474 if ([self isDirectoryIconAtPath:path dirPath:&path]) { 475 flags |= kIsInvisible; 476 } 477 478 id delegate = [internal_ delegate]; 479 if ([delegate respondsToSelector:@selector(finderAttributesAtPath:error:)]) { 480 NSError* error = nil; 481 NSDictionary* dict = [delegate finderAttributesAtPath:path error:&error]; 482 if (dict != nil) { 483 if ([dict objectForKey:kGMUserFileSystemCustomIconDataKey]) { 484 // They have custom icon data, so make sure the FinderFlags bit is set. 485 flags |= kHasCustomIcon; 486 } 487 if (flags != 0) { 488 // May need to update kGMUserFileSystemFinderFlagsKey if different. 489 NSNumber* finderFlags = [dict objectForKey:kGMUserFileSystemFinderFlagsKey]; 490 if (finderFlags != nil) { 491 UInt16 tmp = (UInt16)[finderFlags longValue]; 492 if (flags == tmp) { 493 return dict; // They already have our desired flags. 494 } 495 flags |= tmp; 496 } 497 // Doh! We need to create a new dict with the updated flags key. 498 NSMutableDictionary* newDict = 499 [NSMutableDictionary dictionaryWithDictionary:dict]; 500 [newDict setObject:[NSNumber numberWithLong:flags] 501 forKey:kGMUserFileSystemFinderFlagsKey]; 502 return newDict; 503 } 504 return dict; 505 } 506 // Fall through and create dictionary based on flags if necessary. 507 } else if ([delegate respondsToSelector:@selector(finderFlagsAtPath:)]) { 508 flags |= [delegate finderFlagsAtPath:path]; 509 } else if ([delegate respondsToSelector:@selector(iconDataAtPath:)] && 510 [delegate iconDataAtPath:path] != nil) { 511 flags |= kHasCustomIcon; 512 } 513 if (flags != 0) { 514 return [NSDictionary dictionaryWithObject:[NSNumber numberWithLong:flags] 515 forKey:kGMUserFileSystemFinderFlagsKey]; 516 } 517 return nil; 518} 519 520- (NSDictionary *)resourceAttributesAtPath:(NSString *)path { 521 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 522 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 523 } 524 525 id delegate = [internal_ delegate]; 526 if ([delegate respondsToSelector:@selector(resourceAttributesAtPath:error:)]) { 527 NSError* error = nil; 528 return [delegate resourceAttributesAtPath:path error:&error]; 529 } 530 531 // Support for deprecated selectors. 532 NSURL* url = nil; 533 if ([path hasSuffix:@".webloc"] && 534 [delegate respondsToSelector:@selector(URLOfWeblocAtPath:)]) { 535 url = [delegate URLOfWeblocAtPath:path]; 536 } 537 NSData* imageData = nil; 538 if ([delegate respondsToSelector:@selector(iconDataAtPath:)]) { 539 imageData = [delegate iconDataAtPath:path]; 540 } 541 if (imageData || url) { 542 NSMutableDictionary* dict = [NSMutableDictionary dictionary]; 543 if (imageData) { 544 [dict setObject:imageData forKey:kGMUserFileSystemCustomIconDataKey]; 545 } 546 if (url) { 547 [dict setObject:url forKey:kGMUserFileSystemWeblocURLKey]; 548 } 549 return dict; 550 } 551 return nil; 552} 553 554- (BOOL)hasCustomIconAtPath:(NSString *)path { 555 if ([path isEqualToString:@"/"]) { 556 return NO; // For a volume icon they should use the volicon= option. 557 } 558 NSDictionary* finderAttribs = [self finderAttributesAtPath:path]; 559 if (finderAttribs) { 560 NSNumber* finderFlags = 561 [finderAttribs objectForKey:kGMUserFileSystemFinderFlagsKey]; 562 if (finderFlags) { 563 UInt16 flags = (UInt16)[finderFlags longValue]; 564 return (flags & kHasCustomIcon) == kHasCustomIcon; 565 } 566 } 567 return NO; 568 } 569 570- (BOOL)isDirectoryIconAtPath:(NSString *)path dirPath:(NSString **)dirPath { 571 NSString* name = [path lastPathComponent]; 572 if ([name isEqualToString:@"Icon\r"]) { 573 if (dirPath) { 574 *dirPath = [path stringByDeletingLastPathComponent]; 575 } 576 return YES; 577 } 578 return NO; 579} 580 581- (BOOL)isAppleDoubleAtPath:(NSString *)path realPath:(NSString **)realPath { 582 NSString* name = [path lastPathComponent]; 583 if ([name hasPrefix:@"._"]) { 584 if (realPath) { 585 name = [name substringFromIndex:2]; 586 *realPath = [path stringByDeletingLastPathComponent]; 587 *realPath = [*realPath stringByAppendingPathComponent:name]; 588 } 589 return YES; 590 } 591 return NO; 592} 593 594// If the given attribs dictionary contains any FinderInfo attributes then 595// returns NSData for FinderInfo; otherwise returns nil. 596- (NSData *)finderDataForAttributes:(NSDictionary *)attribs { 597 if (!attribs) { 598 return nil; 599 } 600 601 GMFinderInfo* info = [GMFinderInfo finderInfo]; 602 BOOL attributeFound = NO; // Have we found at least one relevant attribute? 603 604 NSNumber* flags = [attribs objectForKey:kGMUserFileSystemFinderFlagsKey]; 605 if (flags) { 606 attributeFound = YES; 607 [info setFlags:(UInt16)[flags longValue]]; 608 } 609 610 NSNumber* extendedFlags = 611 [attribs objectForKey:kGMUserFileSystemFinderExtendedFlagsKey]; 612 if (extendedFlags) { 613 attributeFound = YES; 614 [info setExtendedFlags:(UInt16)[extendedFlags longValue]]; 615 } 616 617 NSNumber* typeCode = [attribs objectForKey:NSFileHFSTypeCode]; 618 if (typeCode) { 619 attributeFound = YES; 620 [info setTypeCode:(OSType)[typeCode longValue]]; 621 } 622 623 NSNumber* creatorCode = [attribs objectForKey:NSFileHFSCreatorCode]; 624 if (creatorCode) { 625 attributeFound = YES; 626 [info setCreatorCode:(OSType)[creatorCode longValue]]; 627 } 628 629 return attributeFound ? [info data] : nil; 630} 631 632// If the given attribs dictionary contains any ResourceFork attributes then 633// returns NSData for the ResourceFork; otherwise returns nil. 634- (NSData *)resourceDataForAttributes:(NSDictionary *)attribs { 635 if (!attribs) { 636 return nil; 637 } 638 639 GMResourceFork* fork = [GMResourceFork resourceFork]; 640 BOOL attributeFound = NO; // Have we found at least one relevant attribute? 641 642 NSData* imageData = [attribs objectForKey:kGMUserFileSystemCustomIconDataKey]; 643 if (imageData) { 644 attributeFound = YES; 645 [fork addResourceWithType:'icns' 646 resID:kCustomIconResource // -16455 647 name:nil 648 data:imageData]; 649 } 650 NSURL* url = [attribs objectForKey:kGMUserFileSystemWeblocURLKey]; 651 if (url) { 652 attributeFound = YES; 653 NSString* urlString = [url absoluteString]; 654 NSData* data = [urlString dataUsingEncoding:NSUTF8StringEncoding]; 655 [fork addResourceWithType:'url ' 656 resID:256 657 name:nil 658 data:data]; 659 } 660 return attributeFound ? [fork data] : nil; 661} 662 663// Returns the AppleDouble file contents, if any, for the given path. You should 664// call this with the realPath out-param from a call to isAppleDoubleAtPath:. 665// 666// On 10.5 and (hopefully) above, the Finder will end up using the extended 667// attributes and so we won't need to serve ._ files. 668- (NSData *)appleDoubleContentsAtPath:(NSString *)path { 669 NSDictionary* finderAttributes = [self finderAttributesAtPath:path]; 670 NSData* finderData = [self finderDataForAttributes:finderAttributes]; 671 672 // We treat the ._ for a directory and it's ._Icon\r file the same. This means 673 // that we'll put extra resource-fork information in directory's ._ file even 674 // though it isn't needed. It's worth it given that it only affects 10.4. 675 [self isDirectoryIconAtPath:path dirPath:&path]; 676 677 NSDictionary* resourceAttributes = [self resourceAttributesAtPath:path]; 678 NSData* resourceData = [self resourceDataForAttributes:resourceAttributes]; 679 if (finderData != nil || resourceData != nil) { 680 GMAppleDouble* doubleFile = [GMAppleDouble appleDouble]; 681 if (finderData) { 682 [doubleFile addEntryWithID:DoubleEntryFinderInfo data:finderData]; 683 } 684 if (resourceData) { 685 [doubleFile addEntryWithID:DoubleEntryResourceFork 686 data:resourceData]; 687 } 688 return [doubleFile data]; 689 } 690 return nil; 691} 692 693#pragma mark Internal Stat Operations 694 695- (BOOL)fillStatvfsBuffer:(struct statvfs *)stbuf 696 forPath:(NSString *)path 697 error:(NSError **)error { 698 NSDictionary* attributes = [self attributesOfFileSystemForPath:path error:error]; 699 if (!attributes) { 700 return NO; 701 } 702 703 // Maximum length of filenames 704 // TODO: Create our own key so that a fileSystem can override this. 705 stbuf->f_namemax = 255; 706 707 // Block size 708 // TODO: Create our own key so that a fileSystem can override this. 709 stbuf->f_bsize = stbuf->f_frsize = 4096; 710 711 // Size in blocks 712 NSNumber* size = [attributes objectForKey:NSFileSystemSize]; 713 assert(size); 714 stbuf->f_blocks = (fsblkcnt_t)([size longLongValue] / stbuf->f_frsize); 715 716 // Number of free / available blocks 717 NSNumber* freeSize = [attributes objectForKey:NSFileSystemFreeSize]; 718 assert(freeSize); 719 stbuf->f_bfree = stbuf->f_bavail = 720 (fsblkcnt_t)([freeSize longLongValue] / stbuf->f_frsize); 721 722 // Number of nodes 723 NSNumber* numNodes = [attributes objectForKey:NSFileSystemNodes]; 724 assert(numNodes); 725 stbuf->f_files = (fsfilcnt_t)[numNodes longLongValue]; 726 727 // Number of free / available nodes 728 NSNumber* freeNodes = [attributes objectForKey:NSFileSystemFreeNodes]; 729 assert(freeNodes); 730 stbuf->f_ffree = stbuf->f_favail = (fsfilcnt_t)[freeNodes longLongValue]; 731 732 return YES; 733} 734 735- (BOOL)fillStatBuffer:(struct stat *)stbuf 736 forPath:(NSString *)path 737 userData:(id)userData 738 error:(NSError **)error { 739 NSDictionary* attributes = [self defaultAttributesOfItemAtPath:path 740 userData:userData 741 error:error]; 742 if (!attributes) { 743 return NO; 744 } 745 746 // Inode 747 NSNumber* inode = [attributes objectForKey:NSFileSystemFileNumber]; 748 if (inode) { 749 stbuf->st_ino = [inode longLongValue]; 750 } 751 752 // Permissions (mode) 753 NSNumber* perm = [attributes objectForKey:NSFilePosixPermissions]; 754 stbuf->st_mode = [perm longValue]; 755 NSString* fileType = [attributes objectForKey:NSFileType]; 756 if ([fileType isEqualToString:NSFileTypeDirectory ]) { 757 stbuf->st_mode |= S_IFDIR; 758 } else if ([fileType isEqualToString:NSFileTypeRegular]) { 759 stbuf->st_mode |= S_IFREG; 760 } else if ([fileType isEqualToString:NSFileTypeSymbolicLink]) { 761 stbuf->st_mode |= S_IFLNK; 762 } else { 763 *error = [GMUserFileSystem errorWithCode:EFTYPE]; 764 return NO; 765 } 766 767 // Owner and Group 768 // Note that if the owner or group IDs are not specified, the effective 769 // user and group IDs for the current process are used as defaults. 770 NSNumber* uid = [attributes objectForKey:NSFileOwnerAccountID]; 771 NSNumber* gid = [attributes objectForKey:NSFileGroupOwnerAccountID]; 772 stbuf->st_uid = uid ? [uid longValue] : geteuid(); 773 stbuf->st_gid = gid ? [gid longValue] : getegid(); 774 775 // nlink 776 NSNumber* nlink = [attributes objectForKey:NSFileReferenceCount]; 777 stbuf->st_nlink = [nlink longValue]; 778 779 // flags 780 NSNumber* flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey]; 781 if (flags) { 782 stbuf->st_flags = [flags longValue]; 783 } else { 784 // Just in case they tried to use NSFileImmutable or NSFileAppendOnly 785 NSNumber* immutableFlag = [attributes objectForKey:NSFileImmutable]; 786 if (immutableFlag && [immutableFlag boolValue]) { 787 stbuf->st_flags |= UF_IMMUTABLE; 788 } 789 NSNumber* appendFlag = [attributes objectForKey:NSFileAppendOnly]; 790 if (appendFlag && [appendFlag boolValue]) { 791 stbuf->st_flags |= UF_APPEND; 792 } 793 } 794 795 // NOTE: We default atime,ctime to mtime if it is provided. 796 NSDate* mdate = [attributes objectForKey:NSFileModificationDate]; 797 if (mdate) { 798 const double seconds_dp = [mdate timeIntervalSince1970]; 799 const time_t t_sec = (time_t) seconds_dp; 800 const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond); 801 const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0; 802 803 stbuf->st_mtimespec.tv_sec = t_sec; 804 stbuf->st_mtimespec.tv_nsec = t_nsec; 805 stbuf->st_atimespec = stbuf->st_mtimespec; // Default to mtime 806 stbuf->st_ctimespec = stbuf->st_mtimespec; // Default to mtime 807 } 808 NSDate* adate = [attributes objectForKey:kGMUserFileSystemFileAccessDateKey]; 809 if (adate) { 810 const double seconds_dp = [adate timeIntervalSince1970]; 811 const time_t t_sec = (time_t) seconds_dp; 812 const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond); 813 const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0; 814 stbuf->st_atimespec.tv_sec = t_sec; 815 stbuf->st_atimespec.tv_nsec = t_nsec; 816 } 817 NSDate* cdate = [attributes objectForKey:kGMUserFileSystemFileChangeDateKey]; 818 if (cdate) { 819 const double seconds_dp = [cdate timeIntervalSince1970]; 820 const time_t t_sec = (time_t) seconds_dp; 821 const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond); 822 const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0; 823 stbuf->st_ctimespec.tv_sec = t_sec; 824 stbuf->st_ctimespec.tv_nsec = t_nsec; 825 } 826 827#if __DARWIN_64_BIT_INO_T 828 NSDate* bdate = [attributes objectForKey:NSFileCreationDate]; 829 if (bdate) { 830 const double seconds_dp = [bdate timeIntervalSince1970]; 831 const time_t t_sec = (time_t) seconds_dp; 832 const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond); 833 const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0; 834 stbuf->st_birthtimespec.tv_sec = t_sec; 835 stbuf->st_birthtimespec.tv_nsec = t_nsec; 836 } 837#endif 838 839 // Size for regular files. 840 // TODO: Revisit size for directories. 841 if (![fileType isEqualToString:NSFileTypeDirectory]) { 842 NSNumber* size = [attributes objectForKey:NSFileSize]; 843 if (size) { 844 stbuf->st_size = [size longLongValue]; 845 } 846 } 847 848 // Set the number of blocks used so that Finder will display size on disk 849 // properly. The man page says that this is in terms of 512 byte blocks. 850 if (stbuf->st_size > 0) { 851 stbuf->st_blocks = stbuf->st_size / 512; 852 if (stbuf->st_size % 512) { 853 ++(stbuf->st_blocks); 854 } 855 } 856 857 return YES; 858} 859 860#pragma mark Moving an Item 861 862- (BOOL)moveItemAtPath:(NSString *)source 863 toPath:(NSString *)destination 864 error:(NSError **)error { 865 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 866 NSString* traceinfo = 867 [NSString stringWithFormat:@"%@ -> %@", source, destination]; 868 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 869 } 870 871 if ([[internal_ delegate] respondsToSelector:@selector(moveItemAtPath:toPath:error:)]) { 872 return [[internal_ delegate] moveItemAtPath:source toPath:destination error:error]; 873 } 874 875 *error = [GMUserFileSystem errorWithCode:EACCES]; 876 return NO; 877} 878 879#pragma mark Removing an Item 880 881- (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error { 882 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 883 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 884 } 885 886 if ([[internal_ delegate] respondsToSelector:@selector(removeDirectoryAtPath:error:)]) { 887 return [[internal_ delegate] removeDirectoryAtPath:path error:error]; 888 } 889 return [self removeItemAtPath:path error:error]; 890} 891 892- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error { 893 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 894 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 895 } 896 897 if ([[internal_ delegate] respondsToSelector:@selector(removeItemAtPath:error:)]) { 898 return [[internal_ delegate] removeItemAtPath:path error:error]; 899 } 900 901 *error = [GMUserFileSystem errorWithCode:EACCES]; 902 return NO; 903} 904 905#pragma mark Creating an Item 906 907- (BOOL)createDirectoryAtPath:(NSString *)path 908 attributes:(NSDictionary *)attributes 909 error:(NSError **)error { 910 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 911 NSMutableString* traceinfo = 912 [NSMutableString stringWithFormat:@"%@ [%@]", path, attributes]; 913 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 914 } 915 916 if ([[internal_ delegate] respondsToSelector:@selector(createDirectoryAtPath:attributes:error:)]) { 917 return [[internal_ delegate] createDirectoryAtPath:path attributes:attributes error:error]; 918 } 919 920 *error = [GMUserFileSystem errorWithCode:EACCES]; 921 return NO; 922} 923 924- (BOOL)createFileAtPath:(NSString *)path 925 attributes:(NSDictionary *)attributes 926 userData:(id *)userData 927 error:(NSError **)error { 928 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 929 NSString* traceinfo = [NSString stringWithFormat:@"%@ [%@]", path, attributes]; 930 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 931 } 932 933 if ([[internal_ delegate] respondsToSelector:@selector(createFileAtPath:attributes:userData:error:)]) { 934 return [[internal_ delegate] createFileAtPath:path attributes:attributes 935 userData:userData error:error]; 936 } else if ([[internal_ delegate] respondsToSelector:@selector(createFileAtPath:attributes:fileDelegate:error:)]) { 937 // NOTE: For backward compatibility with version 1.7 and prior. 938 return [[internal_ delegate] createFileAtPath:path attributes:attributes 939 fileDelegate:userData error:error]; 940 } 941 942 *error = [GMUserFileSystem errorWithCode:EACCES]; 943 return NO; 944} 945 946 947#pragma mark Linking an Item 948 949- (BOOL)linkItemAtPath:(NSString *)path 950 toPath:(NSString *)otherPath 951 error:(NSError **)error { 952 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 953 NSString* traceinfo = [NSString stringWithFormat:@"%@ -> %@", path, otherPath]; 954 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 955 } 956 957 if ([[internal_ delegate] respondsToSelector:@selector(linkItemAtPath:toPath:error:)]) { 958 return [[internal_ delegate] linkItemAtPath:path toPath:otherPath error:error]; 959 } 960 961 *error = [GMUserFileSystem errorWithCode:ENOTSUP]; // Note: error not in man page. 962 return NO; 963} 964 965#pragma mark Symbolic Links 966 967- (BOOL)createSymbolicLinkAtPath:(NSString *)path 968 withDestinationPath:(NSString *)otherPath 969 error:(NSError **)error { 970 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 971 NSString* traceinfo = [NSString stringWithFormat:@"%@ -> %@", path, otherPath]; 972 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 973 } 974 975 if ([[internal_ delegate] respondsToSelector:@selector(createSymbolicLinkAtPath:withDestinationPath:error:)]) { 976 return [[internal_ delegate] createSymbolicLinkAtPath:path 977 withDestinationPath:otherPath 978 error:error]; 979 } 980 981 *error = [GMUserFileSystem errorWithCode:ENOTSUP]; // Note: error not in man page. 982 return NO; 983} 984 985- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path 986 error:(NSError **)error { 987 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 988 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 989 } 990 991 if ([[internal_ delegate] respondsToSelector:@selector(destinationOfSymbolicLinkAtPath:error:)]) { 992 return [[internal_ delegate] destinationOfSymbolicLinkAtPath:path error:error]; 993 } 994 995 *error = [GMUserFileSystem errorWithCode:ENOENT]; 996 return nil; 997} 998 999#pragma mark File Contents 1000 1001// NOTE: Only call this if the delegate does indeed support this method. 1002- (NSData *)contentsAtPath:(NSString *)path { 1003 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1004 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 1005 } 1006 1007 id delegate = [internal_ delegate]; 1008 return [delegate contentsAtPath:path]; 1009} 1010 1011- (BOOL)openFileAtPath:(NSString *)path 1012 mode:(int)mode 1013 userData:(id *)userData 1014 error:(NSError **)error { 1015 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1016 NSString* traceinfo = [NSString stringWithFormat:@"%@, mode=0x%x", path, mode]; 1017 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1018 } 1019 1020 id delegate = [internal_ delegate]; 1021 if ([delegate respondsToSelector:@selector(contentsAtPath:)]) { 1022 NSData* data = [self contentsAtPath:path]; 1023 if (data != nil) { 1024 *userData = [GMDataBackedFileDelegate fileDelegateWithData:data]; 1025 return YES; 1026 } 1027 } else if ([delegate respondsToSelector:@selector(openFileAtPath:mode:userData:error:)]) { 1028 if ([delegate openFileAtPath:path 1029 mode:mode 1030 userData:userData 1031 error:error]) { 1032 return YES; // They handled it. 1033 } 1034 } else if ([delegate respondsToSelector:@selector(openFileAtPath:mode:fileDelegate:error:)]) { 1035 if ([delegate openFileAtPath:path 1036 mode:mode 1037 fileDelegate:userData 1038 error:error]) { 1039 // NOTE: For backward compatibility with version 1.7 and prior. 1040 return YES; // They handled it. 1041 } 1042 } 1043 1044 // Still unable to open the file; maybe it is an Icon\r or AppleDouble? 1045 if ([internal_ shouldCheckForResource]) { 1046 NSData* data = nil; // Synthesized data that we provide a file delegate for. 1047 1048 // Is it an Icon\r file that we handle? 1049 if ([self isDirectoryIconAtPath:path dirPath:nil]) { 1050 data = [NSData data]; // The Icon\r file is empty. 1051 } 1052 1053 // (Tiger Only): Maybe it is an AppleDouble file that we handle? 1054 if ([internal_ isTiger]) { 1055 NSString* realPath; 1056 if ([self isAppleDoubleAtPath:path realPath:&realPath]) { 1057 data = [self appleDoubleContentsAtPath:realPath]; 1058 } 1059 } 1060 if (data != nil) { 1061 if ((mode & O_ACCMODE) == O_RDONLY) { 1062 *userData = [GMDataBackedFileDelegate fileDelegateWithData:data]; 1063 } else { 1064 NSMutableData* mutableData = [NSMutableData dataWithData:data]; 1065 *userData = 1066 [GMMutableDataBackedFileDelegate fileDelegateWithData:mutableData]; 1067 } 1068 return YES; // Handled by a synthesized file delegate. 1069 } 1070 } 1071 1072 if (*error == nil) { 1073 *error = [GMUserFileSystem errorWithCode:ENOENT]; 1074 } 1075 return NO; 1076} 1077 1078- (void)releaseFileAtPath:(NSString *)path userData:(id)userData { 1079 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1080 NSString* traceinfo = 1081 [NSString stringWithFormat:@"%@, userData=%p", path, userData]; 1082 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1083 } 1084 1085 if (userData != nil && 1086 [userData isKindOfClass:[GMDataBackedFileDelegate class]]) { 1087 return; // Don't report releaseFileAtPath for internal file. 1088 } 1089 if ([[internal_ delegate] respondsToSelector:@selector(releaseFileAtPath:userData:)]) { 1090 [[internal_ delegate] releaseFileAtPath:path userData:userData]; 1091 } else if ([[internal_ delegate] respondsToSelector:@selector(releaseFileAtPath:fileDelegate:)]) { 1092 // NOTE: For backward compatibility with version 1.7 and prior. 1093 [[internal_ delegate] releaseFileAtPath:path fileDelegate:userData]; 1094 } 1095} 1096 1097- (int)readFileAtPath:(NSString *)path 1098 userData:(id)userData 1099 buffer:(char *)buffer 1100 size:(size_t)size 1101 offset:(off_t)offset 1102 error:(NSError **)error { 1103 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1104 NSString* traceinfo = 1105 [NSString stringWithFormat:@"%@, userData=%p, offset=%lld, size=%d", 1106 path, userData, offset, size]; 1107 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1108 } 1109 1110 if (userData != nil && 1111 [userData respondsToSelector:@selector(readToBuffer:size:offset:error:)]) { 1112 return [userData readToBuffer:buffer size:size offset:offset error:error]; 1113 } else if ([[internal_ delegate] respondsToSelector:@selector(readFileAtPath:userData:buffer:size:offset:error:)]) { 1114 return [[internal_ delegate] readFileAtPath:path 1115 userData:userData 1116 buffer:buffer 1117 size:size 1118 offset:offset 1119 error:error]; 1120 } else if ([[internal_ delegate] respondsToSelector:@selector(readFileAtPath:fileDelegate:buffer:size:offset:error:)]) { 1121 // NOTE: For backward compatibility with version 1.7 and prior. 1122 return [[internal_ delegate] readFileAtPath:path 1123 fileDelegate:userData 1124 buffer:buffer 1125 size:size 1126 offset:offset 1127 error:error]; 1128 } 1129 *error = [GMUserFileSystem errorWithCode:EACCES]; 1130 return -1; 1131} 1132 1133- (int)writeFileAtPath:(NSString *)path 1134 userData:(id)userData 1135 buffer:(const char *)buffer 1136 size:(size_t)size 1137 offset:(off_t)offset 1138 error:(NSError **)error { 1139 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1140 NSString* traceinfo = 1141 [NSString stringWithFormat:@"%@, userData=%p, offset=%lld, size=%d", 1142 path, userData, offset, size]; 1143 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1144 } 1145 1146 if (userData != nil && 1147 [userData respondsToSelector:@selector(writeFromBuffer:size:offset:error:)]) { 1148 return [userData writeFromBuffer:buffer size:size offset:offset error:error]; 1149 } else if ([[internal_ delegate] respondsToSelector:@selector(writeFileAtPath:userData:buffer:size:offset:error:)]) { 1150 return [[internal_ delegate] writeFileAtPath:path 1151 userData:userData 1152 buffer:buffer 1153 size:size 1154 offset:offset 1155 error:error]; 1156 } else if ([[internal_ delegate] respondsToSelector:@selector(writeFileAtPath:fileDelegate:buffer:size:offset:error:)]) { 1157 // NOTE: For backward compatibility with version 1.7 and prior. 1158 return [[internal_ delegate] writeFileAtPath:path 1159 fileDelegate:userData 1160 buffer:buffer 1161 size:size 1162 offset:offset 1163 error:error]; 1164 } 1165 *error = [GMUserFileSystem errorWithCode:EACCES]; 1166 return -1; 1167} 1168 1169// NOTE: For backward compatibility with version 1.7 and prior. 1170- (BOOL)truncateFileAtPath:(NSString *)path 1171 fileDelegate:(id)fileDelegate 1172 offset:(off_t)offset 1173 error:(NSError **)error 1174 handled:(BOOL*)handled { 1175 if (fileDelegate != nil && 1176 [fileDelegate respondsToSelector:@selector(truncateToOffset:error:)]) { 1177 *handled = YES; 1178 return [fileDelegate truncateToOffset:offset error:error]; 1179 } else if ([[internal_ delegate] respondsToSelector:@selector(truncateFileAtPath:offset:error:)]) { 1180 *handled = YES; 1181 return [[internal_ delegate] truncateFileAtPath:path 1182 offset:offset 1183 error:error]; 1184 } 1185 *handled = NO; 1186 return NO; 1187} 1188 1189- (BOOL)exchangeDataOfItemAtPath:(NSString *)path1 1190 withItemAtPath:(NSString *)path2 1191 error:(NSError **)error { 1192 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1193 NSString* traceinfo = [NSString stringWithFormat:@"%@ <-> %@", path1, path2]; 1194 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1195 } 1196 1197 if ([[internal_ delegate] respondsToSelector:@selector(exchangeDataOfItemAtPath:withItemAtPath:error:)]) { 1198 return [[internal_ delegate] exchangeDataOfItemAtPath:path1 1199 withItemAtPath:path2 1200 error:error]; 1201 } 1202 *error = [GMUserFileSystem errorWithCode:ENOSYS]; 1203 return NO; 1204} 1205 1206#pragma mark Directory Contents 1207 1208- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error { 1209 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1210 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path)); 1211 } 1212 1213 NSArray* contents = nil; 1214 if ([[internal_ delegate] respondsToSelector:@selector(contentsOfDirectoryAtPath:error:)]) { 1215 contents = [[internal_ delegate] contentsOfDirectoryAtPath:path error:error]; 1216 } else if ([path isEqualToString:@"/"]) { 1217 contents = [NSArray array]; // Give them an empty root directory for free. 1218 } 1219 if (contents != nil && 1220 [internal_ isTiger] && 1221 [internal_ shouldCheckForResource]) { 1222 // Note: Tiger (10.4) requires that the ._ file are explicitly listed in 1223 // the directory contents if you want a custom icon to show up. If they 1224 // don't provide their own ._ file and they have a custom icon, then we'll 1225 // add the ._ file to the directory contents. 1226 NSMutableSet* fullContents = [NSMutableSet setWithArray:contents]; 1227 for (int i = 0; i < [contents count]; ++i) { 1228 NSString* name = [contents objectAtIndex:i]; 1229 if ([name hasPrefix:@"._"]) { 1230 continue; // Skip over any AppleDouble that they provide. 1231 } 1232 NSString* doubleName = [NSString stringWithFormat:@"._%@", name]; 1233 if ([fullContents containsObject:doubleName]) { 1234 continue; // They provided their own AppleDouble for 'name'. 1235 } 1236 NSString* pathPlusName = [path stringByAppendingPathComponent:name]; 1237 if ([self hasCustomIconAtPath:pathPlusName]) { 1238 [fullContents addObject:doubleName]; 1239 } 1240 } 1241 if ([self hasCustomIconAtPath:path]) { 1242 [fullContents addObject:@"Icon\r"]; 1243 [fullContents addObject:@"._Icon\r"]; 1244 } 1245 contents = [fullContents allObjects]; 1246 } 1247 return contents; 1248} 1249 1250#pragma mark Getting and Setting Attributes 1251 1252- (BOOL)supportsAttributesOfItemAtPath { 1253 id delegate = [internal_ delegate]; 1254 return [delegate respondsToSelector:@selector(attributesOfItemAtPath:userData:error:)] || 1255 [delegate respondsToSelector:@selector(attributesOfItemAtPath:error:)]; 1256} 1257 1258- (NSDictionary *)attributesOfItemAtPath:(NSString *)path 1259 userData:userData 1260 error:(NSError **)error { 1261 if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) { 1262 NSString* traceinfo = 1263 [NSString stringWithFormat:@"%@, userData=%p", path, userData]; 1264 MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo)); 1265 } 1266 1267 id delegate = [internal_ delegate]; 1268 if ([delegate respondsToSelector:@selector(attributesOfItemAtPath:userData:error:)]) { 1269 return [delegate attributesOfItemAtPath:path userData:userData error:error]; 1270 } else if ([delegate respondsToSelector:@selector(attributesOfItemAtPath:error:)]) { 1271 return [delegate attributesOfItemAtPath:path error:error]; 1272 } 1273 return nil; 1274} 1275 1276// Get attributesOfItemAtPath from the delegate with default values. 1277- (NSDictionary *)defaultAttributesOfItemAtPath:(NSString *)path 1278 userData:userData 1279 error:(NSError **)error { 1280 // Set up default item attributes. 1281 NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; 1282 BOOL isReadOnly = [internal_ isReadOnly]; 1283 [attributes setObject:[NSNumber numberWithLong:(isReadOnly ? 0555 : 0775)] 1284 forKey:NSFilePosixPermissions]; 1285 [attributes setObject:[NSNumber numberWithLong:1] 1286 forKey:NSFileReferenceCount]; // 1 means "don't know" 1287 if ([path isEqualToString:@"/"]) { 1288 [attributes setObject:NSFileTypeDirectory forKey:NSFileType]; 1289 } else { 1290 [attributes setObject:NSFileTypeRegular forKey:NSFileType]; 1291 } 1292 1293 id delegate = [internal_ delegate]; 1294 BOOL isAppleDouble = NO; // May only be set to YES on Tiger. 1295 BOOL isDirectoryIcon = NO; 1296 1297 // The delegate can override any of the above defaults by implementing the 1298 // attributesOfItemAtPath: selector and returning a custom dictionary. 1299 NSDictionary* customAttribs = nil; 1300 BOOL supportsAttributesSelector = [self supportsAttributesOfItemAtPath]; 1301 if (supportsAttribute…
Large files files are truncated, but you can click here to view the full file