/core/sdk-objc/GMUserFileSystem.m
http://macfuse.googlecode.com/ · Objective C · 2484 lines · 2062 code · 255 blank · 167 comment · 352 complexity · 1e8431709af237e9cc9653a90d46f833 MD5 · raw file
Large files are truncated click here to view the full file
- // ================================================================
- // Copyright (c) 2007, Google Inc.
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- // ================================================================
- //
- // GMUserFileSystem.m
- //
- // Created by ted on 12/29/07.
- // Based on FUSEFileSystem originally by alcor.
- //
- #import "GMUserFileSystem.h"
- #define FUSE_USE_VERSION 26
- #include <fuse.h>
- #include <fuse/fuse_darwin.h>
- #include <string.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/param.h>
- #include <sys/mount.h>
- #include <sys/ioctl.h>
- #include <sys/sysctl.h>
- #include <sys/utsname.h>
- #import <Foundation/Foundation.h>
- #import "GMAppleDouble.h"
- #import "GMFinderInfo.h"
- #import "GMResourceFork.h"
- #import "GMDataBackedFileDelegate.h"
- #import "GMDTrace.h"
- #define GM_EXPORT __attribute__((visibility("default")))
- // Creates a dtrace-ready string with any newlines removed.
- #define DTRACE_STRING(s) \
- ((char *)[[s stringByReplacingOccurrencesOfString:@"\n" withString:@" "] UTF8String])
- // Notifications
- GM_EXPORT NSString* const kGMUserFileSystemErrorDomain = @"GMUserFileSystemErrorDomain";
- GM_EXPORT NSString* const kGMUserFileSystemMountPathKey = @"mountPath";
- GM_EXPORT NSString* const kGMUserFileSystemErrorKey = @"error";
- GM_EXPORT NSString* const kGMUserFileSystemMountFailed = @"kGMUserFileSystemMountFailed";
- GM_EXPORT NSString* const kGMUserFileSystemDidMount = @"kGMUserFileSystemDidMount";
- GM_EXPORT NSString* const kGMUserFileSystemDidUnmount = @"kGMUserFileSystemDidUnmount";
- // Attribute keys
- GM_EXPORT NSString* const kGMUserFileSystemFileFlagsKey = @"kGMUserFileSystemFileFlagsKey";
- GM_EXPORT NSString* const kGMUserFileSystemFileAccessDateKey = @"kGMUserFileSystemFileAccessDateKey";
- GM_EXPORT NSString* const kGMUserFileSystemFileChangeDateKey = @"kGMUserFileSystemFileChangeDateKey";
- GM_EXPORT NSString* const kGMUserFileSystemFileBackupDateKey = @"kGMUserFileSystemFileBackupDateKey";
- GM_EXPORT NSString* const kGMUserFileSystemVolumeSupportsExtendedDatesKey = @"kGMUserFileSystemVolumeSupportsExtendedDatesKey";
- // TODO: Remove comment on EXPORT if/when setvolname is supported.
- /* GM_EXPORT */ NSString* const kGMUserFileSystemVolumeSupportsSetVolumeNameKey = @"kGMUserFileSystemVolumeSupportsSetVolumeNameKey";
- /* GM_EXPORT */ NSString* const kGMUserFileSystemVolumeNameKey = @"kGMUserFileSystemVolumeNameKey";
- // FinderInfo and ResourceFork keys
- GM_EXPORT NSString* const kGMUserFileSystemFinderFlagsKey = @"kGMUserFileSystemFinderFlagsKey";
- GM_EXPORT NSString* const kGMUserFileSystemFinderExtendedFlagsKey = @"kGMUserFileSystemFinderExtendedFlagsKey";
- GM_EXPORT NSString* const kGMUserFileSystemCustomIconDataKey = @"kGMUserFileSystemCustomIconDataKey";
- GM_EXPORT NSString* const kGMUserFileSystemWeblocURLKey = @"kGMUserFileSystemWeblocURLKey";
- // Used for time conversions to/from tv_nsec.
- static const double kNanoSecondsPerSecond = 1000000000.0;
- typedef enum {
- // Unable to unmount a dead FUSE files system located at mount point.
- GMUserFileSystem_ERROR_UNMOUNT_DEADFS = 1000,
-
- // Gave up waiting for system removal of existing dir in /Volumes/x after
- // unmounting a dead FUSE file system.
- GMUserFileSystem_ERROR_UNMOUNT_DEADFS_RMDIR = 1001,
-
- // The mount point did not exist, and we were unable to mkdir it.
- GMUserFileSystem_ERROR_MOUNT_MKDIR = 1002,
-
- // fuse_main returned while trying to mount and don't know why.
- GMUserFileSystem_ERROR_MOUNT_FUSE_MAIN_INTERNAL = 1003,
- } GMUserFileSystemErrorCode;
- typedef enum {
- GMUserFileSystem_NOT_MOUNTED, // Not mounted.
- GMUserFileSystem_MOUNTING, // In the process of mounting.
- GMUserFileSystem_INITIALIZING, // Almost done mounting.
- GMUserFileSystem_MOUNTED, // Confirmed to be mounted.
- GMUserFileSystem_UNMOUNTING, // In the process of unmounting.
- GMUserFileSystem_FAILURE, // Failed state; probably a mount failure.
- } GMUserFileSystemStatus;
- @interface GMUserFileSystemInternal : NSObject {
- NSString* mountPath_;
- GMUserFileSystemStatus status_;
- BOOL isTiger_; // Are we running on Tiger?
- BOOL shouldCheckForResource_; // Try to handle FinderInfo/Resource Forks?
- BOOL isThreadSafe_; // Is the delegate thread-safe?
- BOOL supportsExtendedTimes_; // Delegate supports create and backup times?
- BOOL supportsSetVolumeName_; // Delegate supports setvolname?
- BOOL isReadOnly_; // Is this mounted read-only?
- id delegate_;
- }
- - (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe;
- - (void)setDelegate:(id)delegate;
- @end
- @implementation GMUserFileSystemInternal
- - (id)init {
- return [self initWithDelegate:nil isThreadSafe:NO];
- }
- - (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe {
- if ((self = [super init])) {
- status_ = GMUserFileSystem_NOT_MOUNTED;
- isThreadSafe_ = isThreadSafe;
- supportsExtendedTimes_ = NO;
- supportsSetVolumeName_ = NO;
- isReadOnly_ = NO;
- [self setDelegate:delegate];
- // Version 10.4 requires ._ to appear in directory listings.
- long version = fuse_os_version_major_np();
- isTiger_ = (version < 9);
- }
- return self;
- }
- - (void)dealloc {
- [mountPath_ release];
- [super dealloc];
- }
- - (NSString *)mountPath { return mountPath_; }
- - (void)setMountPath:(NSString *)mountPath {
- [mountPath_ autorelease];
- mountPath_ = [mountPath copy];
- }
- - (GMUserFileSystemStatus)status { return status_; }
- - (void)setStatus:(GMUserFileSystemStatus)status { status_ = status; }
- - (BOOL)isThreadSafe { return isThreadSafe_; }
- - (BOOL)supportsExtendedTimes { return supportsExtendedTimes_; }
- - (void)setSupportsExtendedTimes:(BOOL)val { supportsExtendedTimes_ = val; }
- - (BOOL)supportsSetVolumeName { return supportsSetVolumeName_; }
- - (void)setSupportsSetVolumeName:(BOOL)val { supportsSetVolumeName_ = val; }
- - (BOOL)isTiger { return isTiger_; }
- - (BOOL)shouldCheckForResource { return shouldCheckForResource_; }
- - (BOOL)isReadOnly { return isReadOnly_; }
- - (void)setIsReadOnly:(BOOL)val { isReadOnly_ = val; }
- - (id)delegate { return delegate_; }
- - (void)setDelegate:(id)delegate {
- delegate_ = delegate;
- shouldCheckForResource_ =
- [delegate_ respondsToSelector:@selector(finderAttributesAtPath:error:)] ||
- [delegate_ respondsToSelector:@selector(resourceAttributesAtPath:error:)] ||
- [delegate_ respondsToSelector:@selector(finderFlagsAtPath:)] ||
- [delegate_ respondsToSelector:@selector(iconDataAtPath:)] ||
- [delegate_ respondsToSelector:@selector(URLOfWeblocAtPath:)];
-
- // Check for deprecated methods.
- SEL deprecatedMethods[] = {
- @selector(valueOfExtendedAttribute:ofItemAtPath:error:),
- @selector(setExtendedAttribute:ofItemAtPath:value:flags:error:),
- @selector(finderFlagsAtPath:),
- @selector(iconDataAtPath:),
- @selector(URLOfWeblocAtPath:),
- @selector(truncateFileAtPath:offset:error:),
- @selector(attributesOfItemAtPath:error:),
- @selector(setAttributes:ofItemAtPath:error:),
- @selector(openFileAtPath:mode:fileDelegate:error:),
- @selector(createFileAtPath:attributes:fileDelegate:error:),
- @selector(releaseFileAtPath:fileDelegate:),
- @selector(readFileAtPath:fileDelegate:buffer:size:offset:error:),
- @selector(writeFileAtPath:fileDelegate:buffer:size:offset:error:),
- };
- int i;
- for (i = 0; i < sizeof(deprecatedMethods)/sizeof(deprecatedMethods[0]); ++i) {
- SEL sel = deprecatedMethods[i];
- if ([delegate_ respondsToSelector:sel]) {
- NSLog(@"*** WARNING: GMUserFileSystem delegate implements deprecated "
- @"selector: %@", NSStringFromSelector(sel));
- }
- }
- }
- @end
- // Deprecated delegate methods that we still support for backward compatibility
- // with previously compiled file systems. This will be actively trimmed as
- // new releases occur.
- @interface NSObject (GMUserFileSystemDeprecated)
- - (NSData *)valueOfExtendedAttribute:(NSString *)name
- ofItemAtPath:(NSString *)path
- error:(NSError **)error;
- - (BOOL)setExtendedAttribute:(NSString *)name
- ofItemAtPath:(NSString *)path
- value:(NSData *)value
- flags:(int)flags
- error:(NSError **)error;
- - (UInt16)finderFlagsAtPath:(NSString *)path;
- - (NSData *)iconDataAtPath:(NSString *)path;
- - (NSURL *)URLOfWeblocAtPath:(NSString *)path;
- - (BOOL)truncateFileAtPath:(NSString *)path
- offset:(off_t)offset
- error:(NSError **)error;
- - (NSDictionary *)attributesOfItemAtPath:(NSString *)path
- error:(NSError **)error;
- - (BOOL)setAttributes:(NSDictionary *)attributes
- ofItemAtPath:(NSString *)path
- error:(NSError **)error;
- - (BOOL)openFileAtPath:(NSString *)path
- mode:(int)mode
- fileDelegate:(id *)fileDelegate
- error:(NSError **)error;
- - (BOOL)createFileAtPath:(NSString *)path
- attributes:(NSDictionary *)attributes
- fileDelegate:(id *)fileDelegate
- error:(NSError **)error;
- - (void)releaseFileAtPath:(NSString *)path fileDelegate:(id)fileDelegate;
- - (int)readFileAtPath:(NSString *)path
- fileDelegate:(id)fileDelegate
- buffer:(char *)buffer
- size:(size_t)size
- offset:(off_t)offset
- error:(NSError **)error;
- - (int)writeFileAtPath:(NSString *)path
- fileDelegate:(id)fileDelegate
- buffer:(const char *)buffer
- size:(size_t)size
- offset:(off_t)offset
- error:(NSError **)error;
- @end
- @interface GMUserFileSystem (GMUserFileSystemPrivate)
- // The filesystem for the current thread. Valid only during a fuse callback.
- + (GMUserFileSystem *)currentFS;
- // Convenience method to creates an autoreleased NSError in the
- // NSPOSIXErrorDomain. Filesystem errors returned by the delegate must be
- // standard posix errno values.
- + (NSError *)errorWithCode:(int)code;
- - (void)mount:(NSDictionary *)args;
- - (void)waitUntilMounted;
- - (NSDictionary *)finderAttributesAtPath:(NSString *)path;
- - (NSDictionary *)resourceAttributesAtPath:(NSString *)path;
- - (BOOL)hasCustomIconAtPath:(NSString *)path;
- - (BOOL)isDirectoryIconAtPath:(NSString *)path dirPath:(NSString **)dirPath;
- - (BOOL)isAppleDoubleAtPath:(NSString *)path realPath:(NSString **)realPath;
- - (NSData *)finderDataForAttributes:(NSDictionary *)attributes;
- - (NSData *)resourceDataForAttributes:(NSDictionary *)attributes;
- - (NSData *)appleDoubleContentsAtPath:(NSString *)path;
- - (NSDictionary *)defaultAttributesOfItemAtPath:(NSString *)path
- userData:userData
- error:(NSError **)error;
- - (BOOL)fillStatBuffer:(struct stat *)stbuf
- forPath:(NSString *)path
- fileDelegate:(id)fileDelegate
- error:(NSError **)error;
- - (BOOL)fillStatvfsBuffer:(struct statvfs *)stbuf
- forPath:(NSString *)path
- error:(NSError **)error;
- - (void)fuseInit;
- - (void)fuseDestroy;
- @end
- @implementation GMUserFileSystem
- - (id)init {
- return [self initWithDelegate:nil isThreadSafe:NO];
- }
- - (id)initWithDelegate:(id)delegate isThreadSafe:(BOOL)isThreadSafe {
- if ((self = [super init])) {
- internal_ = [[GMUserFileSystemInternal alloc] initWithDelegate:delegate
- isThreadSafe:isThreadSafe];
- }
- return self;
- }
- - (void)dealloc {
- [internal_ release];
- [super dealloc];
- }
- - (void)setDelegate:(id)delegate {
- [internal_ setDelegate:delegate];
- }
- - (id)delegate {
- return [internal_ delegate];
- }
- - (BOOL)enableExtendedTimes {
- return [internal_ supportsExtendedTimes];
- }
- - (BOOL)enableSetVolumeName {
- return [internal_ supportsSetVolumeName];
- }
- - (void)mountAtPath:(NSString *)mountPath
- withOptions:(NSArray *)options {
- [self mountAtPath:mountPath
- withOptions:options
- shouldForeground:YES
- detachNewThread:YES];
- }
- - (void)mountAtPath:(NSString *)mountPath
- withOptions:(NSArray *)options
- shouldForeground:(BOOL)shouldForeground
- detachNewThread:(BOOL)detachNewThread {
- [internal_ setMountPath:mountPath];
- NSMutableArray* optionsCopy = [NSMutableArray array];
- for (int i = 0; i < [options count]; ++i) {
- NSString* option = [options objectAtIndex:i];
- if ([option caseInsensitiveCompare:@"rdonly"] == NSOrderedSame ||
- [option caseInsensitiveCompare:@"ro"] == NSOrderedSame) {
- [internal_ setIsReadOnly:YES];
- }
- [optionsCopy addObject:[[option copy] autorelease]];
- }
- NSDictionary* args =
- [[NSDictionary alloc] initWithObjectsAndKeys:
- optionsCopy, @"options",
- [NSNumber numberWithBool:shouldForeground], @"shouldForeground",
- nil, nil];
- if (detachNewThread) {
- [NSThread detachNewThreadSelector:@selector(mount:)
- toTarget:self
- withObject:args];
- } else {
- [self mount:args];
- }
- }
- - (void)unmount {
- if ([internal_ status] == GMUserFileSystem_MOUNTED) {
- NSArray* args = [NSArray arrayWithObjects:@"-v", [internal_ mountPath], nil];
- NSTask* unmountTask = [NSTask launchedTaskWithLaunchPath:@"/sbin/umount"
- arguments:args];
- [unmountTask waitUntilExit];
- }
- }
- + (NSError *)errorWithCode:(int)code {
- return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil];
- }
- + (GMUserFileSystem *)currentFS {
- struct fuse_context* context = fuse_get_context();
- assert(context);
- return (GMUserFileSystem *)context->private_data;
- }
- #define FUSEDEVIOCGETHANDSHAKECOMPLETE _IOR('F', 2, u_int32_t)
- static const int kMaxWaitForMountTries = 50;
- static const int kWaitForMountUSleepInterval = 100000; // 100 ms
- - (void)waitUntilMounted {
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- for (int i = 0; i < kMaxWaitForMountTries; ++i) {
- UInt32 handShakeComplete = 0;
- int ret = ioctl(fuse_device_fd_np([[internal_ mountPath] UTF8String]),
- FUSEDEVIOCGETHANDSHAKECOMPLETE,
- &handShakeComplete);
- if (ret == 0 && handShakeComplete) {
- [internal_ setStatus:GMUserFileSystem_MOUNTED];
-
- // Successfully mounted, so post notification.
- NSDictionary* userInfo =
- [NSDictionary dictionaryWithObjectsAndKeys:
- [internal_ mountPath], kGMUserFileSystemMountPathKey,
- nil, nil];
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
- [center postNotificationName:kGMUserFileSystemDidMount object:self
- userInfo:userInfo];
- [pool release];
- return;
- }
- usleep(kWaitForMountUSleepInterval);
- }
-
- // Tried for a long time and no luck :-(
- // Unmount and report failure?
- [pool release];
- }
- - (void)fuseInit {
- [internal_ setStatus:GMUserFileSystem_INITIALIZING];
- NSError* error = nil;
- NSDictionary* attribs = [self attributesOfFileSystemForPath:@"/" error:&error];
- if (attribs) {
- NSNumber* supports;
- supports = [attribs objectForKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey];
- if (supports && [supports boolValue]) {
- [internal_ setSupportsExtendedTimes:YES];
- }
- supports = [attribs objectForKey:kGMUserFileSystemVolumeSupportsSetVolumeNameKey];
- if (supports && [supports boolValue]) {
- [internal_ setSupportsSetVolumeName:YES];
- }
- }
-
- // The mount point won't actually show up until this winds its way
- // back through the kernel after this routine returns. In order to post
- // the kGMUserFileSystemDidMount notification we start a new thread that will
- // poll until it is mounted.
- [NSThread detachNewThreadSelector:@selector(waitUntilMounted)
- toTarget:self
- withObject:nil];
- }
- - (void)fuseDestroy {
- if ([[internal_ delegate] respondsToSelector:@selector(willUnmount)]) {
- [[internal_ delegate] willUnmount];
- }
- [internal_ setStatus:GMUserFileSystem_UNMOUNTING];
- NSDictionary* userInfo =
- [NSDictionary dictionaryWithObjectsAndKeys:
- [internal_ mountPath], kGMUserFileSystemMountPathKey,
- nil, nil];
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
- [center postNotificationName:kGMUserFileSystemDidUnmount object:self
- userInfo:userInfo];
- [internal_ setStatus:GMUserFileSystem_NOT_MOUNTED];
- }
- #pragma mark Finder Info, Resource Forks and HFS headers
- - (NSDictionary *)finderAttributesAtPath:(NSString *)path {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
- UInt16 flags = 0;
- // If a directory icon, we'll make invisible and update the path to parent.
- if ([self isDirectoryIconAtPath:path dirPath:&path]) {
- flags |= kIsInvisible;
- }
- id delegate = [internal_ delegate];
- if ([delegate respondsToSelector:@selector(finderAttributesAtPath:error:)]) {
- NSError* error = nil;
- NSDictionary* dict = [delegate finderAttributesAtPath:path error:&error];
- if (dict != nil) {
- if ([dict objectForKey:kGMUserFileSystemCustomIconDataKey]) {
- // They have custom icon data, so make sure the FinderFlags bit is set.
- flags |= kHasCustomIcon;
- }
- if (flags != 0) {
- // May need to update kGMUserFileSystemFinderFlagsKey if different.
- NSNumber* finderFlags = [dict objectForKey:kGMUserFileSystemFinderFlagsKey];
- if (finderFlags != nil) {
- UInt16 tmp = (UInt16)[finderFlags longValue];
- if (flags == tmp) {
- return dict; // They already have our desired flags.
- }
- flags |= tmp;
- }
- // Doh! We need to create a new dict with the updated flags key.
- NSMutableDictionary* newDict =
- [NSMutableDictionary dictionaryWithDictionary:dict];
- [newDict setObject:[NSNumber numberWithLong:flags]
- forKey:kGMUserFileSystemFinderFlagsKey];
- return newDict;
- }
- return dict;
- }
- // Fall through and create dictionary based on flags if necessary.
- } else if ([delegate respondsToSelector:@selector(finderFlagsAtPath:)]) {
- flags |= [delegate finderFlagsAtPath:path];
- } else if ([delegate respondsToSelector:@selector(iconDataAtPath:)] &&
- [delegate iconDataAtPath:path] != nil) {
- flags |= kHasCustomIcon;
- }
- if (flags != 0) {
- return [NSDictionary dictionaryWithObject:[NSNumber numberWithLong:flags]
- forKey:kGMUserFileSystemFinderFlagsKey];
- }
- return nil;
- }
- - (NSDictionary *)resourceAttributesAtPath:(NSString *)path {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
-
- id delegate = [internal_ delegate];
- if ([delegate respondsToSelector:@selector(resourceAttributesAtPath:error:)]) {
- NSError* error = nil;
- return [delegate resourceAttributesAtPath:path error:&error];
- }
- // Support for deprecated selectors.
- NSURL* url = nil;
- if ([path hasSuffix:@".webloc"] &&
- [delegate respondsToSelector:@selector(URLOfWeblocAtPath:)]) {
- url = [delegate URLOfWeblocAtPath:path];
- }
- NSData* imageData = nil;
- if ([delegate respondsToSelector:@selector(iconDataAtPath:)]) {
- imageData = [delegate iconDataAtPath:path];
- }
- if (imageData || url) {
- NSMutableDictionary* dict = [NSMutableDictionary dictionary];
- if (imageData) {
- [dict setObject:imageData forKey:kGMUserFileSystemCustomIconDataKey];
- }
- if (url) {
- [dict setObject:url forKey:kGMUserFileSystemWeblocURLKey];
- }
- return dict;
- }
- return nil;
- }
- - (BOOL)hasCustomIconAtPath:(NSString *)path {
- if ([path isEqualToString:@"/"]) {
- return NO; // For a volume icon they should use the volicon= option.
- }
- NSDictionary* finderAttribs = [self finderAttributesAtPath:path];
- if (finderAttribs) {
- NSNumber* finderFlags =
- [finderAttribs objectForKey:kGMUserFileSystemFinderFlagsKey];
- if (finderFlags) {
- UInt16 flags = (UInt16)[finderFlags longValue];
- return (flags & kHasCustomIcon) == kHasCustomIcon;
- }
- }
- return NO;
- }
- - (BOOL)isDirectoryIconAtPath:(NSString *)path dirPath:(NSString **)dirPath {
- NSString* name = [path lastPathComponent];
- if ([name isEqualToString:@"Icon\r"]) {
- if (dirPath) {
- *dirPath = [path stringByDeletingLastPathComponent];
- }
- return YES;
- }
- return NO;
- }
- - (BOOL)isAppleDoubleAtPath:(NSString *)path realPath:(NSString **)realPath {
- NSString* name = [path lastPathComponent];
- if ([name hasPrefix:@"._"]) {
- if (realPath) {
- name = [name substringFromIndex:2];
- *realPath = [path stringByDeletingLastPathComponent];
- *realPath = [*realPath stringByAppendingPathComponent:name];
- }
- return YES;
- }
- return NO;
- }
- // If the given attribs dictionary contains any FinderInfo attributes then
- // returns NSData for FinderInfo; otherwise returns nil.
- - (NSData *)finderDataForAttributes:(NSDictionary *)attribs {
- if (!attribs) {
- return nil;
- }
- GMFinderInfo* info = [GMFinderInfo finderInfo];
- BOOL attributeFound = NO; // Have we found at least one relevant attribute?
- NSNumber* flags = [attribs objectForKey:kGMUserFileSystemFinderFlagsKey];
- if (flags) {
- attributeFound = YES;
- [info setFlags:(UInt16)[flags longValue]];
- }
-
- NSNumber* extendedFlags =
- [attribs objectForKey:kGMUserFileSystemFinderExtendedFlagsKey];
- if (extendedFlags) {
- attributeFound = YES;
- [info setExtendedFlags:(UInt16)[extendedFlags longValue]];
- }
-
- NSNumber* typeCode = [attribs objectForKey:NSFileHFSTypeCode];
- if (typeCode) {
- attributeFound = YES;
- [info setTypeCode:(OSType)[typeCode longValue]];
- }
- NSNumber* creatorCode = [attribs objectForKey:NSFileHFSCreatorCode];
- if (creatorCode) {
- attributeFound = YES;
- [info setCreatorCode:(OSType)[creatorCode longValue]];
- }
- return attributeFound ? [info data] : nil;
- }
- // If the given attribs dictionary contains any ResourceFork attributes then
- // returns NSData for the ResourceFork; otherwise returns nil.
- - (NSData *)resourceDataForAttributes:(NSDictionary *)attribs {
- if (!attribs) {
- return nil;
- }
- GMResourceFork* fork = [GMResourceFork resourceFork];
- BOOL attributeFound = NO; // Have we found at least one relevant attribute?
-
- NSData* imageData = [attribs objectForKey:kGMUserFileSystemCustomIconDataKey];
- if (imageData) {
- attributeFound = YES;
- [fork addResourceWithType:'icns'
- resID:kCustomIconResource // -16455
- name:nil
- data:imageData];
- }
- NSURL* url = [attribs objectForKey:kGMUserFileSystemWeblocURLKey];
- if (url) {
- attributeFound = YES;
- NSString* urlString = [url absoluteString];
- NSData* data = [urlString dataUsingEncoding:NSUTF8StringEncoding];
- [fork addResourceWithType:'url '
- resID:256
- name:nil
- data:data];
- }
- return attributeFound ? [fork data] : nil;
- }
- // Returns the AppleDouble file contents, if any, for the given path. You should
- // call this with the realPath out-param from a call to isAppleDoubleAtPath:.
- //
- // On 10.5 and (hopefully) above, the Finder will end up using the extended
- // attributes and so we won't need to serve ._ files.
- - (NSData *)appleDoubleContentsAtPath:(NSString *)path {
- NSDictionary* finderAttributes = [self finderAttributesAtPath:path];
- NSData* finderData = [self finderDataForAttributes:finderAttributes];
-
- // We treat the ._ for a directory and it's ._Icon\r file the same. This means
- // that we'll put extra resource-fork information in directory's ._ file even
- // though it isn't needed. It's worth it given that it only affects 10.4.
- [self isDirectoryIconAtPath:path dirPath:&path];
- NSDictionary* resourceAttributes = [self resourceAttributesAtPath:path];
- NSData* resourceData = [self resourceDataForAttributes:resourceAttributes];
- if (finderData != nil || resourceData != nil) {
- GMAppleDouble* doubleFile = [GMAppleDouble appleDouble];
- if (finderData) {
- [doubleFile addEntryWithID:DoubleEntryFinderInfo data:finderData];
- }
- if (resourceData) {
- [doubleFile addEntryWithID:DoubleEntryResourceFork
- data:resourceData];
- }
- return [doubleFile data];
- }
- return nil;
- }
- #pragma mark Internal Stat Operations
- - (BOOL)fillStatvfsBuffer:(struct statvfs *)stbuf
- forPath:(NSString *)path
- error:(NSError **)error {
- NSDictionary* attributes = [self attributesOfFileSystemForPath:path error:error];
- if (!attributes) {
- return NO;
- }
-
- // Maximum length of filenames
- // TODO: Create our own key so that a fileSystem can override this.
- stbuf->f_namemax = 255;
-
- // Block size
- // TODO: Create our own key so that a fileSystem can override this.
- stbuf->f_bsize = stbuf->f_frsize = 4096;
-
- // Size in blocks
- NSNumber* size = [attributes objectForKey:NSFileSystemSize];
- assert(size);
- stbuf->f_blocks = (fsblkcnt_t)([size longLongValue] / stbuf->f_frsize);
-
- // Number of free / available blocks
- NSNumber* freeSize = [attributes objectForKey:NSFileSystemFreeSize];
- assert(freeSize);
- stbuf->f_bfree = stbuf->f_bavail =
- (fsblkcnt_t)([freeSize longLongValue] / stbuf->f_frsize);
-
- // Number of nodes
- NSNumber* numNodes = [attributes objectForKey:NSFileSystemNodes];
- assert(numNodes);
- stbuf->f_files = (fsfilcnt_t)[numNodes longLongValue];
-
- // Number of free / available nodes
- NSNumber* freeNodes = [attributes objectForKey:NSFileSystemFreeNodes];
- assert(freeNodes);
- stbuf->f_ffree = stbuf->f_favail = (fsfilcnt_t)[freeNodes longLongValue];
-
- return YES;
- }
- - (BOOL)fillStatBuffer:(struct stat *)stbuf
- forPath:(NSString *)path
- userData:(id)userData
- error:(NSError **)error {
- NSDictionary* attributes = [self defaultAttributesOfItemAtPath:path
- userData:userData
- error:error];
- if (!attributes) {
- return NO;
- }
- // Inode
- NSNumber* inode = [attributes objectForKey:NSFileSystemFileNumber];
- if (inode) {
- stbuf->st_ino = [inode longLongValue];
- }
-
- // Permissions (mode)
- NSNumber* perm = [attributes objectForKey:NSFilePosixPermissions];
- stbuf->st_mode = [perm longValue];
- NSString* fileType = [attributes objectForKey:NSFileType];
- if ([fileType isEqualToString:NSFileTypeDirectory ]) {
- stbuf->st_mode |= S_IFDIR;
- } else if ([fileType isEqualToString:NSFileTypeRegular]) {
- stbuf->st_mode |= S_IFREG;
- } else if ([fileType isEqualToString:NSFileTypeSymbolicLink]) {
- stbuf->st_mode |= S_IFLNK;
- } else {
- *error = [GMUserFileSystem errorWithCode:EFTYPE];
- return NO;
- }
-
- // Owner and Group
- // Note that if the owner or group IDs are not specified, the effective
- // user and group IDs for the current process are used as defaults.
- NSNumber* uid = [attributes objectForKey:NSFileOwnerAccountID];
- NSNumber* gid = [attributes objectForKey:NSFileGroupOwnerAccountID];
- stbuf->st_uid = uid ? [uid longValue] : geteuid();
- stbuf->st_gid = gid ? [gid longValue] : getegid();
- // nlink
- NSNumber* nlink = [attributes objectForKey:NSFileReferenceCount];
- stbuf->st_nlink = [nlink longValue];
- // flags
- NSNumber* flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey];
- if (flags) {
- stbuf->st_flags = [flags longValue];
- } else {
- // Just in case they tried to use NSFileImmutable or NSFileAppendOnly
- NSNumber* immutableFlag = [attributes objectForKey:NSFileImmutable];
- if (immutableFlag && [immutableFlag boolValue]) {
- stbuf->st_flags |= UF_IMMUTABLE;
- }
- NSNumber* appendFlag = [attributes objectForKey:NSFileAppendOnly];
- if (appendFlag && [appendFlag boolValue]) {
- stbuf->st_flags |= UF_APPEND;
- }
- }
- // NOTE: We default atime,ctime to mtime if it is provided.
- NSDate* mdate = [attributes objectForKey:NSFileModificationDate];
- if (mdate) {
- const double seconds_dp = [mdate timeIntervalSince1970];
- const time_t t_sec = (time_t) seconds_dp;
- const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond);
- const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0;
- stbuf->st_mtimespec.tv_sec = t_sec;
- stbuf->st_mtimespec.tv_nsec = t_nsec;
- stbuf->st_atimespec = stbuf->st_mtimespec; // Default to mtime
- stbuf->st_ctimespec = stbuf->st_mtimespec; // Default to mtime
- }
- NSDate* adate = [attributes objectForKey:kGMUserFileSystemFileAccessDateKey];
- if (adate) {
- const double seconds_dp = [adate timeIntervalSince1970];
- const time_t t_sec = (time_t) seconds_dp;
- const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond);
- const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0;
- stbuf->st_atimespec.tv_sec = t_sec;
- stbuf->st_atimespec.tv_nsec = t_nsec;
- }
- NSDate* cdate = [attributes objectForKey:kGMUserFileSystemFileChangeDateKey];
- if (cdate) {
- const double seconds_dp = [cdate timeIntervalSince1970];
- const time_t t_sec = (time_t) seconds_dp;
- const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond);
- const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0;
- stbuf->st_ctimespec.tv_sec = t_sec;
- stbuf->st_ctimespec.tv_nsec = t_nsec;
- }
- #if __DARWIN_64_BIT_INO_T
- NSDate* bdate = [attributes objectForKey:NSFileCreationDate];
- if (bdate) {
- const double seconds_dp = [bdate timeIntervalSince1970];
- const time_t t_sec = (time_t) seconds_dp;
- const double nanoseconds_dp = ((seconds_dp - t_sec) * kNanoSecondsPerSecond);
- const long t_nsec = (nanoseconds_dp > 0 ) ? nanoseconds_dp : 0;
- stbuf->st_birthtimespec.tv_sec = t_sec;
- stbuf->st_birthtimespec.tv_nsec = t_nsec;
- }
- #endif
- // Size for regular files.
- // TODO: Revisit size for directories.
- if (![fileType isEqualToString:NSFileTypeDirectory]) {
- NSNumber* size = [attributes objectForKey:NSFileSize];
- if (size) {
- stbuf->st_size = [size longLongValue];
- }
- }
- // Set the number of blocks used so that Finder will display size on disk
- // properly. The man page says that this is in terms of 512 byte blocks.
- if (stbuf->st_size > 0) {
- stbuf->st_blocks = stbuf->st_size / 512;
- if (stbuf->st_size % 512) {
- ++(stbuf->st_blocks);
- }
- }
- return YES;
- }
- #pragma mark Moving an Item
- - (BOOL)moveItemAtPath:(NSString *)source
- toPath:(NSString *)destination
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo =
- [NSString stringWithFormat:@"%@ -> %@", source, destination];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(moveItemAtPath:toPath:error:)]) {
- return [[internal_ delegate] moveItemAtPath:source toPath:destination error:error];
- }
-
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return NO;
- }
- #pragma mark Removing an Item
- - (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(removeDirectoryAtPath:error:)]) {
- return [[internal_ delegate] removeDirectoryAtPath:path error:error];
- }
- return [self removeItemAtPath:path error:error];
- }
- - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(removeItemAtPath:error:)]) {
- return [[internal_ delegate] removeItemAtPath:path error:error];
- }
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return NO;
- }
- #pragma mark Creating an Item
- - (BOOL)createDirectoryAtPath:(NSString *)path
- attributes:(NSDictionary *)attributes
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSMutableString* traceinfo =
- [NSMutableString stringWithFormat:@"%@ [%@]", path, attributes];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
-
- if ([[internal_ delegate] respondsToSelector:@selector(createDirectoryAtPath:attributes:error:)]) {
- return [[internal_ delegate] createDirectoryAtPath:path attributes:attributes error:error];
- }
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return NO;
- }
- - (BOOL)createFileAtPath:(NSString *)path
- attributes:(NSDictionary *)attributes
- userData:(id *)userData
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo = [NSString stringWithFormat:@"%@ [%@]", path, attributes];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(createFileAtPath:attributes:userData:error:)]) {
- return [[internal_ delegate] createFileAtPath:path attributes:attributes
- userData:userData error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(createFileAtPath:attributes:fileDelegate:error:)]) {
- // NOTE: For backward compatibility with version 1.7 and prior.
- return [[internal_ delegate] createFileAtPath:path attributes:attributes
- fileDelegate:userData error:error];
- }
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return NO;
- }
- #pragma mark Linking an Item
- - (BOOL)linkItemAtPath:(NSString *)path
- toPath:(NSString *)otherPath
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo = [NSString stringWithFormat:@"%@ -> %@", path, otherPath];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(linkItemAtPath:toPath:error:)]) {
- return [[internal_ delegate] linkItemAtPath:path toPath:otherPath error:error];
- }
- *error = [GMUserFileSystem errorWithCode:ENOTSUP]; // Note: error not in man page.
- return NO;
- }
- #pragma mark Symbolic Links
- - (BOOL)createSymbolicLinkAtPath:(NSString *)path
- withDestinationPath:(NSString *)otherPath
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo = [NSString stringWithFormat:@"%@ -> %@", path, otherPath];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
-
- if ([[internal_ delegate] respondsToSelector:@selector(createSymbolicLinkAtPath:withDestinationPath:error:)]) {
- return [[internal_ delegate] createSymbolicLinkAtPath:path
- withDestinationPath:otherPath
- error:error];
- }
- *error = [GMUserFileSystem errorWithCode:ENOTSUP]; // Note: error not in man page.
- return NO;
- }
- - (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
-
- if ([[internal_ delegate] respondsToSelector:@selector(destinationOfSymbolicLinkAtPath:error:)]) {
- return [[internal_ delegate] destinationOfSymbolicLinkAtPath:path error:error];
- }
- *error = [GMUserFileSystem errorWithCode:ENOENT];
- return nil;
- }
- #pragma mark File Contents
- // NOTE: Only call this if the delegate does indeed support this method.
- - (NSData *)contentsAtPath:(NSString *)path {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
- id delegate = [internal_ delegate];
- return [delegate contentsAtPath:path];
- }
- - (BOOL)openFileAtPath:(NSString *)path
- mode:(int)mode
- userData:(id *)userData
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo = [NSString stringWithFormat:@"%@, mode=0x%x", path, mode];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- id delegate = [internal_ delegate];
- if ([delegate respondsToSelector:@selector(contentsAtPath:)]) {
- NSData* data = [self contentsAtPath:path];
- if (data != nil) {
- *userData = [GMDataBackedFileDelegate fileDelegateWithData:data];
- return YES;
- }
- } else if ([delegate respondsToSelector:@selector(openFileAtPath:mode:userData:error:)]) {
- if ([delegate openFileAtPath:path
- mode:mode
- userData:userData
- error:error]) {
- return YES; // They handled it.
- }
- } else if ([delegate respondsToSelector:@selector(openFileAtPath:mode:fileDelegate:error:)]) {
- if ([delegate openFileAtPath:path
- mode:mode
- fileDelegate:userData
- error:error]) {
- // NOTE: For backward compatibility with version 1.7 and prior.
- return YES; // They handled it.
- }
- }
- // Still unable to open the file; maybe it is an Icon\r or AppleDouble?
- if ([internal_ shouldCheckForResource]) {
- NSData* data = nil; // Synthesized data that we provide a file delegate for.
- // Is it an Icon\r file that we handle?
- if ([self isDirectoryIconAtPath:path dirPath:nil]) {
- data = [NSData data]; // The Icon\r file is empty.
- }
- // (Tiger Only): Maybe it is an AppleDouble file that we handle?
- if ([internal_ isTiger]) {
- NSString* realPath;
- if ([self isAppleDoubleAtPath:path realPath:&realPath]) {
- data = [self appleDoubleContentsAtPath:realPath];
- }
- }
- if (data != nil) {
- if ((mode & O_ACCMODE) == O_RDONLY) {
- *userData = [GMDataBackedFileDelegate fileDelegateWithData:data];
- } else {
- NSMutableData* mutableData = [NSMutableData dataWithData:data];
- *userData =
- [GMMutableDataBackedFileDelegate fileDelegateWithData:mutableData];
- }
- return YES; // Handled by a synthesized file delegate.
- }
- }
-
- if (*error == nil) {
- *error = [GMUserFileSystem errorWithCode:ENOENT];
- }
- return NO;
- }
- - (void)releaseFileAtPath:(NSString *)path userData:(id)userData {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo =
- [NSString stringWithFormat:@"%@, userData=%p", path, userData];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
-
- if (userData != nil &&
- [userData isKindOfClass:[GMDataBackedFileDelegate class]]) {
- return; // Don't report releaseFileAtPath for internal file.
- }
- if ([[internal_ delegate] respondsToSelector:@selector(releaseFileAtPath:userData:)]) {
- [[internal_ delegate] releaseFileAtPath:path userData:userData];
- } else if ([[internal_ delegate] respondsToSelector:@selector(releaseFileAtPath:fileDelegate:)]) {
- // NOTE: For backward compatibility with version 1.7 and prior.
- [[internal_ delegate] releaseFileAtPath:path fileDelegate:userData];
- }
- }
- - (int)readFileAtPath:(NSString *)path
- userData:(id)userData
- buffer:(char *)buffer
- size:(size_t)size
- offset:(off_t)offset
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo =
- [NSString stringWithFormat:@"%@, userData=%p, offset=%lld, size=%d",
- path, userData, offset, size];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if (userData != nil &&
- [userData respondsToSelector:@selector(readToBuffer:size:offset:error:)]) {
- return [userData readToBuffer:buffer size:size offset:offset error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(readFileAtPath:userData:buffer:size:offset:error:)]) {
- return [[internal_ delegate] readFileAtPath:path
- userData:userData
- buffer:buffer
- size:size
- offset:offset
- error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(readFileAtPath:fileDelegate:buffer:size:offset:error:)]) {
- // NOTE: For backward compatibility with version 1.7 and prior.
- return [[internal_ delegate] readFileAtPath:path
- fileDelegate:userData
- buffer:buffer
- size:size
- offset:offset
- error:error];
- }
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return -1;
- }
- - (int)writeFileAtPath:(NSString *)path
- userData:(id)userData
- buffer:(const char *)buffer
- size:(size_t)size
- offset:(off_t)offset
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo =
- [NSString stringWithFormat:@"%@, userData=%p, offset=%lld, size=%d",
- path, userData, offset, size];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if (userData != nil &&
- [userData respondsToSelector:@selector(writeFromBuffer:size:offset:error:)]) {
- return [userData writeFromBuffer:buffer size:size offset:offset error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(writeFileAtPath:userData:buffer:size:offset:error:)]) {
- return [[internal_ delegate] writeFileAtPath:path
- userData:userData
- buffer:buffer
- size:size
- offset:offset
- error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(writeFileAtPath:fileDelegate:buffer:size:offset:error:)]) {
- // NOTE: For backward compatibility with version 1.7 and prior.
- return [[internal_ delegate] writeFileAtPath:path
- fileDelegate:userData
- buffer:buffer
- size:size
- offset:offset
- error:error];
- }
- *error = [GMUserFileSystem errorWithCode:EACCES];
- return -1;
- }
- // NOTE: For backward compatibility with version 1.7 and prior.
- - (BOOL)truncateFileAtPath:(NSString *)path
- fileDelegate:(id)fileDelegate
- offset:(off_t)offset
- error:(NSError **)error
- handled:(BOOL*)handled {
- if (fileDelegate != nil &&
- [fileDelegate respondsToSelector:@selector(truncateToOffset:error:)]) {
- *handled = YES;
- return [fileDelegate truncateToOffset:offset error:error];
- } else if ([[internal_ delegate] respondsToSelector:@selector(truncateFileAtPath:offset:error:)]) {
- *handled = YES;
- return [[internal_ delegate] truncateFileAtPath:path
- offset:offset
- error:error];
- }
- *handled = NO;
- return NO;
- }
- - (BOOL)exchangeDataOfItemAtPath:(NSString *)path1
- withItemAtPath:(NSString *)path2
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo = [NSString stringWithFormat:@"%@ <-> %@", path1, path2];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- if ([[internal_ delegate] respondsToSelector:@selector(exchangeDataOfItemAtPath:withItemAtPath:error:)]) {
- return [[internal_ delegate] exchangeDataOfItemAtPath:path1
- withItemAtPath:path2
- error:error];
- }
- *error = [GMUserFileSystem errorWithCode:ENOSYS];
- return NO;
- }
- #pragma mark Directory Contents
- - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(path));
- }
- NSArray* contents = nil;
- if ([[internal_ delegate] respondsToSelector:@selector(contentsOfDirectoryAtPath:error:)]) {
- contents = [[internal_ delegate] contentsOfDirectoryAtPath:path error:error];
- } else if ([path isEqualToString:@"/"]) {
- contents = [NSArray array]; // Give them an empty root directory for free.
- }
- if (contents != nil &&
- [internal_ isTiger] &&
- [internal_ shouldCheckForResource]) {
- // Note: Tiger (10.4) requires that the ._ file are explicitly listed in
- // the directory contents if you want a custom icon to show up. If they
- // don't provide their own ._ file and they have a custom icon, then we'll
- // add the ._ file to the directory contents.
- NSMutableSet* fullContents = [NSMutableSet setWithArray:contents];
- for (int i = 0; i < [contents count]; ++i) {
- NSString* name = [contents objectAtIndex:i];
- if ([name hasPrefix:@"._"]) {
- continue; // Skip over any AppleDouble that they provide.
- }
- NSString* doubleName = [NSString stringWithFormat:@"._%@", name];
- if ([fullContents containsObject:doubleName]) {
- continue; // They provided their own AppleDouble for 'name'.
- }
- NSString* pathPlusName = [path stringByAppendingPathComponent:name];
- if ([self hasCustomIconAtPath:pathPlusName]) {
- [fullContents addObject:doubleName];
- }
- }
- if ([self hasCustomIconAtPath:path]) {
- [fullContents addObject:@"Icon\r"];
- [fullContents addObject:@"._Icon\r"];
- }
- contents = [fullContents allObjects];
- }
- return contents;
- }
- #pragma mark Getting and Setting Attributes
- - (BOOL)supportsAttributesOfItemAtPath {
- id delegate = [internal_ delegate];
- return [delegate respondsToSelector:@selector(attributesOfItemAtPath:userData:error:)] ||
- [delegate respondsToSelector:@selector(attributesOfItemAtPath:error:)];
- }
- - (NSDictionary *)attributesOfItemAtPath:(NSString *)path
- userData:userData
- error:(NSError **)error {
- if (MACFUSE_OBJC_DELEGATE_ENTRY_ENABLED()) {
- NSString* traceinfo =
- [NSString stringWithFormat:@"%@, userData=%p", path, userData];
- MACFUSE_OBJC_DELEGATE_ENTRY(DTRACE_STRING(traceinfo));
- }
- id delegate = [internal_ delegate];
- if ([delegate respondsToSelector:@selector(attributesOfItemAtPath:userData:error:)]) {
- return [delegate attributesOfItemAtPath:path userData:userData error:error];
- } else if ([delegate respondsToSelector:@selector(attributesOfItemAtPath:error:)]) {
- return [delegate attributesOfItemAtPath:path error:error];
- }
- return nil;
- }
- // Get attributesOfItemAtPath from the delegate with default values.
- - (NSDictionary *)defaultAttributesOfItemAtPath:(NSString *)path
- userData:userData
- error:(NSError **)error {
- // Set up default item attributes.
- NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
- BOOL isReadOnly = [internal_ isReadOnly];
- [attributes setObject:[NSNumber numberWithLong:(isReadOnly ? 0555 : 0775)]
- forKey:NSFilePosixPermissions];
- [attributes setObject:[NSNumber numberWithLong:1]
- forKey:NSFileReferenceCount]; // 1 means "don't know"
- if ([path isEqualToString:@"/"]) {
- [attributes setObject:NSFileTypeDirectory forKey:NSFileType];
- } else {
- [attributes setObject:NSFileTypeRegular forKey:NSFileType];
- }
-
- id delegate = [internal_ delegate];
- BOOL isAppleDouble = NO; // May only be set to YES on Tiger.
- BOOL isDirectoryIcon = NO;
- // The delegate can override any of the above defaults by implementing the
- // attributesOfItemAtPath: selector and returning a custom dictionary.
- NSDictionary* customAttribs = nil;
- BOOL supportsAttributesSelector = [self supportsAttributesOfItemAtPath];
- if (supportsAttribute…