PageRenderTime 82ms CodeModel.GetById 12ms app.highlight 65ms RepoModel.GetById 2ms app.codeStats 0ms

/filesystems-objc/LoopbackFS/LoopbackFS.m

http://macfuse.googlecode.com/
Objective C | 363 lines | 281 code | 40 blank | 42 comment | 22 complexity | 9a2a07fc4b1d608d6267312dc7ba5a32 MD5 | raw file
  1// ================================================================
  2// Copyright (C) 2007 Google Inc.
  3// 
  4// Licensed under the Apache License, Version 2.0 (the "License");
  5// you may not use this file except in compliance with the License.
  6// You may obtain a copy of the License at
  7// 
  8//      http://www.apache.org/licenses/LICENSE-2.0
  9// 
 10// Unless required by applicable law or agreed to in writing, software
 11// distributed under the License is distributed on an "AS IS" BASIS,
 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13// See the License for the specific language governing permissions and
 14// limitations under the License.
 15// ================================================================
 16//
 17//  LoopbackFS.m
 18//  LoopbackFS
 19//
 20//  Created by ted on 12/12/07.
 21//
 22// This is a simple but complete example filesystem that mounts a local 
 23// directory. You can modify this to see how the Finder reacts to returning
 24// specific error codes or not implementing a particular GMUserFileSystem
 25// operation.
 26//
 27// For example, you can mount "/tmp" in /Volumes/loop. Note: It is 
 28// probably not a good idea to mount "/" through this filesystem.
 29
 30#import <sys/xattr.h>
 31#import <sys/stat.h>
 32#import "LoopbackFS.h"
 33#import <MacFUSE/MacFUSE.h>
 34#import "NSError+POSIX.h"
 35
 36@implementation LoopbackFS
 37
 38- (id)initWithRootPath:(NSString *)rootPath {
 39  if ((self = [super init])) {
 40    rootPath_ = [rootPath retain];
 41  }
 42  return self;
 43}
 44
 45- (void) dealloc {
 46  [rootPath_ release];
 47  [super dealloc];
 48}
 49
 50#pragma mark Moving an Item
 51
 52- (BOOL)moveItemAtPath:(NSString *)source 
 53                toPath:(NSString *)destination
 54                 error:(NSError **)error {
 55  // We use rename directly here since NSFileManager can sometimes fail to 
 56  // rename and return non-posix error codes.
 57  NSString* p_src = [rootPath_ stringByAppendingString:source];
 58  NSString* p_dst = [rootPath_ stringByAppendingString:destination];
 59  int ret = rename([p_src UTF8String], [p_dst UTF8String]);
 60  if ( ret < 0 ) {
 61    *error = [NSError errorWithPOSIXCode:errno];
 62  }
 63  return YES;
 64}
 65
 66#pragma mark Removing an Item
 67
 68- (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error {
 69  // We need to special-case directories here and use the bsd API since 
 70  // NSFileManager will happily do a recursive remove :-(
 71  NSString* p = [rootPath_ stringByAppendingString:path];
 72  int ret = rmdir([p UTF8String]);
 73  if (ret < 0) {
 74    *error = [NSError errorWithPOSIXCode:errno];
 75    return NO;
 76  }
 77  return YES;
 78}
 79
 80- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error {
 81  // NOTE: If removeDirectoryAtPath is commented out, then this may be called
 82  // with a directory, in which case NSFileManager will recursively remove all
 83  // subdirectories. So be careful!
 84  NSString* p = [rootPath_ stringByAppendingString:path];
 85  return [[NSFileManager defaultManager] removeItemAtPath:p error:error];
 86}
 87
 88#pragma mark Creating an Item
 89
 90- (BOOL)createDirectoryAtPath:(NSString *)path 
 91                   attributes:(NSDictionary *)attributes
 92                        error:(NSError **)error {
 93  NSString* p = [rootPath_ stringByAppendingString:path];
 94  return [[NSFileManager defaultManager] createDirectoryAtPath:p 
 95                                   withIntermediateDirectories:NO
 96                                                    attributes:attributes
 97                                                        error:error];
 98}
 99
100- (BOOL)createFileAtPath:(NSString *)path 
101              attributes:(NSDictionary *)attributes
102                userData:(id *)userData
103                   error:(NSError **)error {
104  NSString* p = [rootPath_ stringByAppendingString:path];
105  mode_t mode = [[attributes objectForKey:NSFilePosixPermissions] longValue];  
106  int fd = creat([p UTF8String], mode);
107  if ( fd < 0 ) {
108    *error = [NSError errorWithPOSIXCode:errno];
109    return NO;
110  }
111  *userData = [NSNumber numberWithLong:fd];
112  return YES;
113}
114
115#pragma mark Linking an Item
116
117- (BOOL)linkItemAtPath:(NSString *)path
118                toPath:(NSString *)otherPath
119                 error:(NSError **)error {
120  NSString* p_path = [rootPath_ stringByAppendingString:path];
121  NSString* p_otherPath = [rootPath_ stringByAppendingString:otherPath];
122
123  // We use link rather than the NSFileManager equivalent because it will copy
124  // the file rather than hard link if part of the root path is a symlink.
125  int rc = link([p_path UTF8String], [p_otherPath UTF8String]);
126  if ( rc <  0 ) {
127    *error = [NSError errorWithPOSIXCode:errno];
128    return NO;
129  }
130  return YES;
131}
132
133#pragma mark Symbolic Links
134
135- (BOOL)createSymbolicLinkAtPath:(NSString *)path 
136             withDestinationPath:(NSString *)otherPath
137                           error:(NSError **)error {
138  NSString* p_src = [rootPath_ stringByAppendingString:path];
139  NSString* p_dst = [rootPath_ stringByAppendingString:otherPath];
140  return [[NSFileManager defaultManager] createSymbolicLinkAtPath:p_src
141                                              withDestinationPath:p_dst
142                                                            error:error];  
143}
144
145- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path
146                                        error:(NSError **)error {
147  NSString* p = [rootPath_ stringByAppendingString:path];
148  return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:p
149                                                                   error:error];
150}
151
152#pragma mark File Contents
153
154- (BOOL)openFileAtPath:(NSString *)path 
155                  mode:(int)mode
156              userData:(id *)userData
157                 error:(NSError **)error {
158  NSString* p = [rootPath_ stringByAppendingString:path];
159  int fd = open([p UTF8String], mode);
160  if ( fd < 0 ) {
161    *error = [NSError errorWithPOSIXCode:errno];
162    return NO;
163  }
164  *userData = [NSNumber numberWithLong:fd];
165  return YES;
166}
167
168- (void)releaseFileAtPath:(NSString *)path userData:(id)userData {
169  NSNumber* num = (NSNumber *)userData;
170  int fd = [num longValue];
171  close(fd);
172}
173
174- (int)readFileAtPath:(NSString *)path 
175             userData:(id)userData
176               buffer:(char *)buffer 
177                 size:(size_t)size 
178               offset:(off_t)offset
179                error:(NSError **)error {
180  NSNumber* num = (NSNumber *)userData;
181  int fd = [num longValue];
182  int ret = pread(fd, buffer, size, offset);
183  if ( ret < 0 ) {
184    *error = [NSError errorWithPOSIXCode:errno];
185    return -1;
186  }
187  return ret;
188}
189
190- (int)writeFileAtPath:(NSString *)path 
191              userData:(id)userData
192                buffer:(const char *)buffer
193                  size:(size_t)size 
194                offset:(off_t)offset
195                 error:(NSError **)error {
196  NSNumber* num = (NSNumber *)userData;
197  int fd = [num longValue];
198  int ret = pwrite(fd, buffer, size, offset);
199  if ( ret < 0 ) {
200    *error = [NSError errorWithPOSIXCode:errno];
201    return -1;
202  }
203  return ret;
204}
205
206- (BOOL)exchangeDataOfItemAtPath:(NSString *)path1
207                  withItemAtPath:(NSString *)path2
208                           error:(NSError **)error {
209  NSString* p1 = [rootPath_ stringByAppendingString:path1];
210  NSString* p2 = [rootPath_ stringByAppendingString:path2];
211  int ret = exchangedata([p1 UTF8String], [p2 UTF8String], 0);
212  if ( ret < 0 ) {
213    *error = [NSError errorWithPOSIXCode:errno];
214    return NO;    
215  }
216  return YES;  
217}
218
219#pragma mark Directory Contents
220
221- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error {
222  NSString* p = [rootPath_ stringByAppendingString:path];
223  return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:error];
224}
225
226#pragma mark Getting and Setting Attributes
227
228- (NSDictionary *)attributesOfItemAtPath:(NSString *)path
229                                userData:(id)userData
230                                   error:(NSError **)error {
231  NSString* p = [rootPath_ stringByAppendingString:path];
232  NSDictionary* attribs = 
233    [[NSFileManager defaultManager] attributesOfItemAtPath:p error:error];
234  return attribs;
235}
236
237- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path
238                                          error:(NSError **)error {
239  NSString* p = [rootPath_ stringByAppendingString:path];
240  NSDictionary* d =
241    [[NSFileManager defaultManager] attributesOfFileSystemForPath:p error:error];
242  if (d) {
243    NSMutableDictionary* attribs = [NSMutableDictionary dictionaryWithDictionary:d];
244    [attribs setObject:[NSNumber numberWithBool:YES]
245                forKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey];
246    return attribs;
247  }
248  return nil;
249}
250
251- (BOOL)setAttributes:(NSDictionary *)attributes 
252         ofItemAtPath:(NSString *)path
253             userData:(id)userData
254                error:(NSError **)error {
255  NSString* p = [rootPath_ stringByAppendingString:path];
256
257  // TODO: Handle other keys not handled by NSFileManager setAttributes call.
258  
259  NSNumber* offset = [attributes objectForKey:NSFileSize];
260  if ( offset ) {
261    int ret = truncate([p UTF8String], [offset longLongValue]);
262    if ( ret < 0 ) {
263      *error = [NSError errorWithPOSIXCode:errno];
264      return NO;    
265    }
266  }
267  NSNumber* flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey];
268  if (flags != nil) {
269    int rc = chflags([p UTF8String], [flags intValue]);
270    if (rc < 0) {
271      *error = [NSError errorWithPOSIXCode:errno];
272      return NO;
273    }
274  }
275  return [[NSFileManager defaultManager] setAttributes:attributes
276                                          ofItemAtPath:p
277                                                 error:error];
278}
279
280#pragma mark Extended Attributes
281
282- (NSArray *)extendedAttributesOfItemAtPath:(NSString *)path error:(NSError **)error {
283  NSString* p = [rootPath_ stringByAppendingString:path];
284  
285  ssize_t size = listxattr([p UTF8String], nil, 0, 0);
286  if ( size < 0 ) {
287    *error = [NSError errorWithPOSIXCode:errno];
288    return nil;
289  }
290  NSMutableData* data = [NSMutableData dataWithLength:size];
291  size = listxattr([p UTF8String], [data mutableBytes], [data length], 0);
292  if ( size < 0 ) {
293    *error = [NSError errorWithPOSIXCode:errno];
294    return nil;
295  }
296  NSMutableArray* contents = [NSMutableArray array];
297  char* ptr = (char *)[data bytes];
298  while ( ptr < ((char *)[data bytes] + size) ) {
299    NSString* s = [NSString stringWithUTF8String:ptr];
300    [contents addObject:s];
301    ptr += ([s length] + 1);
302  }
303  return contents;
304}
305
306- (NSData *)valueOfExtendedAttribute:(NSString *)name 
307                        ofItemAtPath:(NSString *)path
308                            position:(off_t)position
309                               error:(NSError **)error {  
310  NSString* p = [rootPath_ stringByAppendingString:path];
311
312  ssize_t size = getxattr([p UTF8String], [name UTF8String], nil, 0,
313                         position, 0);
314  if ( size < 0 ) {
315    *error = [NSError errorWithPOSIXCode:errno];
316    return nil;
317  }
318  NSMutableData* data = [NSMutableData dataWithLength:size];
319  size = getxattr([p UTF8String], [name UTF8String], 
320                  [data mutableBytes], [data length],
321                  position, 0);
322  if ( size < 0 ) {
323    *error = [NSError errorWithPOSIXCode:errno];
324    return nil;
325  }  
326  return data;
327}
328
329- (BOOL)setExtendedAttribute:(NSString *)name 
330                ofItemAtPath:(NSString *)path 
331                       value:(NSData *)value
332                    position:(off_t)position
333                       options:(int)options
334                       error:(NSError **)error {
335  // Setting com.apple.FinderInfo happens in the kernel, so security related 
336  // bits are set in the options. We need to explicitly remove them or the call
337  // to setxattr will fail.
338  // TODO: Why is this necessary?
339  options &= ~(XATTR_NOSECURITY | XATTR_NODEFAULT);
340  NSString* p = [rootPath_ stringByAppendingString:path];
341  int ret = setxattr([p UTF8String], [name UTF8String], 
342                     [value bytes], [value length], 
343                     position, options);
344  if ( ret < 0 ) {
345    *error = [NSError errorWithPOSIXCode:errno];
346    return NO;
347  }
348  return YES;
349}
350
351- (BOOL)removeExtendedAttribute:(NSString *)name
352                   ofItemAtPath:(NSString *)path
353                          error:(NSError **)error {
354  NSString* p = [rootPath_ stringByAppendingString:path];
355  int ret = removexattr([p UTF8String], [name UTF8String], 0);
356  if ( ret < 0 ) {
357    *error = [NSError errorWithPOSIXCode:errno];
358    return NO;
359  }
360  return YES;
361}
362
363@end