/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. #import <sys/xattr.h>
  30. #import <sys/stat.h>
  31. #import "LoopbackFS.h"
  32. #import <MacFUSE/MacFUSE.h>
  33. #import "NSError+POSIX.h"
  34. @implementation LoopbackFS
  35. - (id)initWithRootPath:(NSString *)rootPath {
  36. if ((self = [super init])) {
  37. rootPath_ = [rootPath retain];
  38. }
  39. return self;
  40. }
  41. - (void) dealloc {
  42. [rootPath_ release];
  43. [super dealloc];
  44. }
  45. #pragma mark Moving an Item
  46. - (BOOL)moveItemAtPath:(NSString *)source
  47. toPath:(NSString *)destination
  48. error:(NSError **)error {
  49. // We use rename directly here since NSFileManager can sometimes fail to
  50. // rename and return non-posix error codes.
  51. NSString* p_src = [rootPath_ stringByAppendingString:source];
  52. NSString* p_dst = [rootPath_ stringByAppendingString:destination];
  53. int ret = rename([p_src UTF8String], [p_dst UTF8String]);
  54. if ( ret < 0 ) {
  55. *error = [NSError errorWithPOSIXCode:errno];
  56. }
  57. return YES;
  58. }
  59. #pragma mark Removing an Item
  60. - (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error {
  61. // We need to special-case directories here and use the bsd API since
  62. // NSFileManager will happily do a recursive remove :-(
  63. NSString* p = [rootPath_ stringByAppendingString:path];
  64. int ret = rmdir([p UTF8String]);
  65. if (ret < 0) {
  66. *error = [NSError errorWithPOSIXCode:errno];
  67. return NO;
  68. }
  69. return YES;
  70. }
  71. - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error {
  72. // NOTE: If removeDirectoryAtPath is commented out, then this may be called
  73. // with a directory, in which case NSFileManager will recursively remove all
  74. // subdirectories. So be careful!
  75. NSString* p = [rootPath_ stringByAppendingString:path];
  76. return [[NSFileManager defaultManager] removeItemAtPath:p error:error];
  77. }
  78. #pragma mark Creating an Item
  79. - (BOOL)createDirectoryAtPath:(NSString *)path
  80. attributes:(NSDictionary *)attributes
  81. error:(NSError **)error {
  82. NSString* p = [rootPath_ stringByAppendingString:path];
  83. return [[NSFileManager defaultManager] createDirectoryAtPath:p
  84. withIntermediateDirectories:NO
  85. attributes:attributes
  86. error:error];
  87. }
  88. - (BOOL)createFileAtPath:(NSString *)path
  89. attributes:(NSDictionary *)attributes
  90. userData:(id *)userData
  91. error:(NSError **)error {
  92. NSString* p = [rootPath_ stringByAppendingString:path];
  93. mode_t mode = [[attributes objectForKey:NSFilePosixPermissions] longValue];
  94. int fd = creat([p UTF8String], mode);
  95. if ( fd < 0 ) {
  96. *error = [NSError errorWithPOSIXCode:errno];
  97. return NO;
  98. }
  99. *userData = [NSNumber numberWithLong:fd];
  100. return YES;
  101. }
  102. #pragma mark Linking an Item
  103. - (BOOL)linkItemAtPath:(NSString *)path
  104. toPath:(NSString *)otherPath
  105. error:(NSError **)error {
  106. NSString* p_path = [rootPath_ stringByAppendingString:path];
  107. NSString* p_otherPath = [rootPath_ stringByAppendingString:otherPath];
  108. // We use link rather than the NSFileManager equivalent because it will copy
  109. // the file rather than hard link if part of the root path is a symlink.
  110. int rc = link([p_path UTF8String], [p_otherPath UTF8String]);
  111. if ( rc < 0 ) {
  112. *error = [NSError errorWithPOSIXCode:errno];
  113. return NO;
  114. }
  115. return YES;
  116. }
  117. #pragma mark Symbolic Links
  118. - (BOOL)createSymbolicLinkAtPath:(NSString *)path
  119. withDestinationPath:(NSString *)otherPath
  120. error:(NSError **)error {
  121. NSString* p_src = [rootPath_ stringByAppendingString:path];
  122. NSString* p_dst = [rootPath_ stringByAppendingString:otherPath];
  123. return [[NSFileManager defaultManager] createSymbolicLinkAtPath:p_src
  124. withDestinationPath:p_dst
  125. error:error];
  126. }
  127. - (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path
  128. error:(NSError **)error {
  129. NSString* p = [rootPath_ stringByAppendingString:path];
  130. return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:p
  131. error:error];
  132. }
  133. #pragma mark File Contents
  134. - (BOOL)openFileAtPath:(NSString *)path
  135. mode:(int)mode
  136. userData:(id *)userData
  137. error:(NSError **)error {
  138. NSString* p = [rootPath_ stringByAppendingString:path];
  139. int fd = open([p UTF8String], mode);
  140. if ( fd < 0 ) {
  141. *error = [NSError errorWithPOSIXCode:errno];
  142. return NO;
  143. }
  144. *userData = [NSNumber numberWithLong:fd];
  145. return YES;
  146. }
  147. - (void)releaseFileAtPath:(NSString *)path userData:(id)userData {
  148. NSNumber* num = (NSNumber *)userData;
  149. int fd = [num longValue];
  150. close(fd);
  151. }
  152. - (int)readFileAtPath:(NSString *)path
  153. userData:(id)userData
  154. buffer:(char *)buffer
  155. size:(size_t)size
  156. offset:(off_t)offset
  157. error:(NSError **)error {
  158. NSNumber* num = (NSNumber *)userData;
  159. int fd = [num longValue];
  160. int ret = pread(fd, buffer, size, offset);
  161. if ( ret < 0 ) {
  162. *error = [NSError errorWithPOSIXCode:errno];
  163. return -1;
  164. }
  165. return ret;
  166. }
  167. - (int)writeFileAtPath:(NSString *)path
  168. userData:(id)userData
  169. buffer:(const char *)buffer
  170. size:(size_t)size
  171. offset:(off_t)offset
  172. error:(NSError **)error {
  173. NSNumber* num = (NSNumber *)userData;
  174. int fd = [num longValue];
  175. int ret = pwrite(fd, buffer, size, offset);
  176. if ( ret < 0 ) {
  177. *error = [NSError errorWithPOSIXCode:errno];
  178. return -1;
  179. }
  180. return ret;
  181. }
  182. - (BOOL)exchangeDataOfItemAtPath:(NSString *)path1
  183. withItemAtPath:(NSString *)path2
  184. error:(NSError **)error {
  185. NSString* p1 = [rootPath_ stringByAppendingString:path1];
  186. NSString* p2 = [rootPath_ stringByAppendingString:path2];
  187. int ret = exchangedata([p1 UTF8String], [p2 UTF8String], 0);
  188. if ( ret < 0 ) {
  189. *error = [NSError errorWithPOSIXCode:errno];
  190. return NO;
  191. }
  192. return YES;
  193. }
  194. #pragma mark Directory Contents
  195. - (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error {
  196. NSString* p = [rootPath_ stringByAppendingString:path];
  197. return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:error];
  198. }
  199. #pragma mark Getting and Setting Attributes
  200. - (NSDictionary *)attributesOfItemAtPath:(NSString *)path
  201. userData:(id)userData
  202. error:(NSError **)error {
  203. NSString* p = [rootPath_ stringByAppendingString:path];
  204. NSDictionary* attribs =
  205. [[NSFileManager defaultManager] attributesOfItemAtPath:p error:error];
  206. return attribs;
  207. }
  208. - (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path
  209. error:(NSError **)error {
  210. NSString* p = [rootPath_ stringByAppendingString:path];
  211. NSDictionary* d =
  212. [[NSFileManager defaultManager] attributesOfFileSystemForPath:p error:error];
  213. if (d) {
  214. NSMutableDictionary* attribs = [NSMutableDictionary dictionaryWithDictionary:d];
  215. [attribs setObject:[NSNumber numberWithBool:YES]
  216. forKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey];
  217. return attribs;
  218. }
  219. return nil;
  220. }
  221. - (BOOL)setAttributes:(NSDictionary *)attributes
  222. ofItemAtPath:(NSString *)path
  223. userData:(id)userData
  224. error:(NSError **)error {
  225. NSString* p = [rootPath_ stringByAppendingString:path];
  226. // TODO: Handle other keys not handled by NSFileManager setAttributes call.
  227. NSNumber* offset = [attributes objectForKey:NSFileSize];
  228. if ( offset ) {
  229. int ret = truncate([p UTF8String], [offset longLongValue]);
  230. if ( ret < 0 ) {
  231. *error = [NSError errorWithPOSIXCode:errno];
  232. return NO;
  233. }
  234. }
  235. NSNumber* flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey];
  236. if (flags != nil) {
  237. int rc = chflags([p UTF8String], [flags intValue]);
  238. if (rc < 0) {
  239. *error = [NSError errorWithPOSIXCode:errno];
  240. return NO;
  241. }
  242. }
  243. return [[NSFileManager defaultManager] setAttributes:attributes
  244. ofItemAtPath:p
  245. error:error];
  246. }
  247. #pragma mark Extended Attributes
  248. - (NSArray *)extendedAttributesOfItemAtPath:(NSString *)path error:(NSError **)error {
  249. NSString* p = [rootPath_ stringByAppendingString:path];
  250. ssize_t size = listxattr([p UTF8String], nil, 0, 0);
  251. if ( size < 0 ) {
  252. *error = [NSError errorWithPOSIXCode:errno];
  253. return nil;
  254. }
  255. NSMutableData* data = [NSMutableData dataWithLength:size];
  256. size = listxattr([p UTF8String], [data mutableBytes], [data length], 0);
  257. if ( size < 0 ) {
  258. *error = [NSError errorWithPOSIXCode:errno];
  259. return nil;
  260. }
  261. NSMutableArray* contents = [NSMutableArray array];
  262. char* ptr = (char *)[data bytes];
  263. while ( ptr < ((char *)[data bytes] + size) ) {
  264. NSString* s = [NSString stringWithUTF8String:ptr];
  265. [contents addObject:s];
  266. ptr += ([s length] + 1);
  267. }
  268. return contents;
  269. }
  270. - (NSData *)valueOfExtendedAttribute:(NSString *)name
  271. ofItemAtPath:(NSString *)path
  272. position:(off_t)position
  273. error:(NSError **)error {
  274. NSString* p = [rootPath_ stringByAppendingString:path];
  275. ssize_t size = getxattr([p UTF8String], [name UTF8String], nil, 0,
  276. position, 0);
  277. if ( size < 0 ) {
  278. *error = [NSError errorWithPOSIXCode:errno];
  279. return nil;
  280. }
  281. NSMutableData* data = [NSMutableData dataWithLength:size];
  282. size = getxattr([p UTF8String], [name UTF8String],
  283. [data mutableBytes], [data length],
  284. position, 0);
  285. if ( size < 0 ) {
  286. *error = [NSError errorWithPOSIXCode:errno];
  287. return nil;
  288. }
  289. return data;
  290. }
  291. - (BOOL)setExtendedAttribute:(NSString *)name
  292. ofItemAtPath:(NSString *)path
  293. value:(NSData *)value
  294. position:(off_t)position
  295. options:(int)options
  296. error:(NSError **)error {
  297. // Setting com.apple.FinderInfo happens in the kernel, so security related
  298. // bits are set in the options. We need to explicitly remove them or the call
  299. // to setxattr will fail.
  300. // TODO: Why is this necessary?
  301. options &= ~(XATTR_NOSECURITY | XATTR_NODEFAULT);
  302. NSString* p = [rootPath_ stringByAppendingString:path];
  303. int ret = setxattr([p UTF8String], [name UTF8String],
  304. [value bytes], [value length],
  305. position, options);
  306. if ( ret < 0 ) {
  307. *error = [NSError errorWithPOSIXCode:errno];
  308. return NO;
  309. }
  310. return YES;
  311. }
  312. - (BOOL)removeExtendedAttribute:(NSString *)name
  313. ofItemAtPath:(NSString *)path
  314. error:(NSError **)error {
  315. NSString* p = [rootPath_ stringByAppendingString:path];
  316. int ret = removexattr([p UTF8String], [name UTF8String], 0);
  317. if ( ret < 0 ) {
  318. *error = [NSError errorWithPOSIXCode:errno];
  319. return NO;
  320. }
  321. return YES;
  322. }
  323. @end