697 lines
21 KiB
Objective-C
697 lines
21 KiB
Objective-C
// ================================================================
|
|
// Copyright (C) 2007 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// ================================================================
|
|
//
|
|
// LoopbackFS.m
|
|
// LoopbackFS
|
|
//
|
|
// Created by ted on 12/12/07.
|
|
//
|
|
// This is a simple but complete example filesystem that mounts a local
|
|
// directory. You can modify this to see how the Finder reacts to returning
|
|
// specific error codes or not implementing a particular GMUserFileSystem
|
|
// operation.
|
|
//
|
|
// For example, you can mount "/tmp" in /Volumes/loop. Note: It is
|
|
// probably not a good idea to mount "/" through this filesystem.
|
|
|
|
#import "LoopbackFS.h"
|
|
|
|
#import <macFUSE/macFUSE.h>
|
|
|
|
#import <sys/mount.h>
|
|
#import <sys/stat.h>
|
|
#import <sys/vnode.h>
|
|
#import <sys/xattr.h>
|
|
|
|
#import "NSError+POSIX.h"
|
|
|
|
#define HAVE_EXCHANE 0
|
|
|
|
@implementation LoopbackFS
|
|
|
|
- (id)initWithRootPath:(NSString *)rootPath {
|
|
if ((self = [super init])) {
|
|
rootPath_ = [rootPath retain];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc {
|
|
[rootPath_ release];
|
|
[super dealloc];
|
|
}
|
|
|
|
#pragma mark Moving an Item
|
|
|
|
- (BOOL)moveItemAtPath:(NSString *)source
|
|
toPath:(NSString *)destination
|
|
options:(GMUserFileSystemMoveOption)options
|
|
error:(NSError **)error {
|
|
// We use rename directly here since NSFileManager can sometimes fail to
|
|
// rename and return non-posix error codes.
|
|
NSString *p_src = [rootPath_ stringByAppendingString:source];
|
|
NSString *p_dst = [rootPath_ stringByAppendingString:destination];
|
|
int ret = 0;
|
|
if (options == 0) {
|
|
ret = rename([p_src UTF8String], [p_dst UTF8String]);
|
|
} else {
|
|
unsigned int flags = 0;
|
|
if (options & GMUserFileSystemMoveOptionSwap) {
|
|
flags |= RENAME_SWAP;
|
|
}
|
|
if (options & GMUserFileSystemMoveOptionExclusive) {
|
|
flags |= RENAME_EXCL;
|
|
}
|
|
ret = renamex_np([p_src UTF8String], [p_dst UTF8String], flags);
|
|
}
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark Removing an Item
|
|
|
|
- (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error {
|
|
// We need to special-case directories here and use the bsd API since
|
|
// NSFileManager will happily do a recursive remove :-(
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
int ret = rmdir([p UTF8String]);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error {
|
|
// NOTE: If removeDirectoryAtPath is commented out, then this may be called
|
|
// with a directory, in which case NSFileManager will recursively remove all
|
|
// subdirectories. So be careful!
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
return [[NSFileManager defaultManager] removeItemAtPath:p error:error];
|
|
}
|
|
|
|
#pragma mark Creating an Item
|
|
|
|
- (BOOL)createDirectoryAtPath:(NSString *)path
|
|
attributes:(NSDictionary *)attributes
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
return [[NSFileManager defaultManager] createDirectoryAtPath:p
|
|
withIntermediateDirectories:NO
|
|
attributes:attributes
|
|
error:error];
|
|
}
|
|
|
|
- (BOOL)createFileAtPath:(NSString *)path
|
|
attributes:(NSDictionary *)attributes
|
|
flags:(int)flags
|
|
userData:(id *)userData
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
mode_t mode = [[attributes objectForKey:NSFilePosixPermissions] longValue];
|
|
int fd = open([p UTF8String], flags, mode);
|
|
if (fd < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
*userData = [NSNumber numberWithLong:fd];
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark Linking an Item
|
|
|
|
- (BOOL)linkItemAtPath:(NSString *)path
|
|
toPath:(NSString *)otherPath
|
|
error:(NSError **)error {
|
|
NSString *p_path = [rootPath_ stringByAppendingString:path];
|
|
NSString *p_otherPath = [rootPath_ stringByAppendingString:otherPath];
|
|
|
|
// We use link rather than the NSFileManager equivalent because it will copy
|
|
// the file rather than hard link if part of the root path is a symlink.
|
|
int ret = link([p_path UTF8String], [p_otherPath UTF8String]);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark Symbolic Links
|
|
|
|
- (BOOL)createSymbolicLinkAtPath:(NSString *)path
|
|
withDestinationPath:(NSString *)otherPath
|
|
error:(NSError **)error {
|
|
NSString *p_src = [rootPath_ stringByAppendingString:path];
|
|
return [[NSFileManager defaultManager] createSymbolicLinkAtPath:p_src
|
|
withDestinationPath:otherPath
|
|
error:error];
|
|
}
|
|
|
|
- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:p
|
|
error:error];
|
|
}
|
|
|
|
#pragma mark File Contents
|
|
|
|
- (BOOL)openFileAtPath:(NSString *)path
|
|
mode:(int)mode
|
|
userData:(id *)userData
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
int fd = open([p UTF8String], mode);
|
|
if (fd < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
*userData = [NSNumber numberWithLong:fd];
|
|
return YES;
|
|
}
|
|
|
|
- (void)releaseFileAtPath:(NSString *)path userData:(id)userData {
|
|
NSNumber *num = (NSNumber *)userData;
|
|
int fd = [num intValue];
|
|
close(fd);
|
|
}
|
|
|
|
- (int)readFileAtPath:(NSString *)path
|
|
userData:(id)userData
|
|
buffer:(char *)buffer
|
|
size:(size_t)size
|
|
offset:(off_t)offset
|
|
error:(NSError **)error {
|
|
NSNumber *num = (NSNumber *)userData;
|
|
int fd = [num intValue];
|
|
size_t ret = pread(fd, buffer, size, offset);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return -1;
|
|
}
|
|
return (int)ret;
|
|
}
|
|
|
|
- (int)writeFileAtPath:(NSString *)path
|
|
userData:(id)userData
|
|
buffer:(const char *)buffer
|
|
size:(size_t)size
|
|
offset:(off_t)offset
|
|
error:(NSError **)error {
|
|
NSNumber *num = (NSNumber *)userData;
|
|
int fd = [num intValue];
|
|
size_t ret = pwrite(fd, buffer, size, offset);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return -1;
|
|
}
|
|
return (int)ret;
|
|
}
|
|
|
|
- (BOOL)preallocateFileAtPath:(NSString *)path
|
|
userData:(id)userData
|
|
options:(int)options
|
|
offset:(off_t)offset
|
|
length:(off_t)length
|
|
error:(NSError **)error {
|
|
NSNumber *num = (NSNumber *)userData;
|
|
int fd = [num intValue];
|
|
|
|
fstore_t fstore;
|
|
|
|
fstore.fst_flags = 0;
|
|
if (options & ALLOCATECONTIG) {
|
|
fstore.fst_flags |= F_ALLOCATECONTIG;
|
|
}
|
|
if (options & ALLOCATEALL) {
|
|
fstore.fst_flags |= F_ALLOCATEALL;
|
|
}
|
|
|
|
if (options & ALLOCATEFROMPEOF) {
|
|
fstore.fst_posmode = F_PEOFPOSMODE;
|
|
} else if (options & ALLOCATEFROMVOL) {
|
|
fstore.fst_posmode = F_VOLPOSMODE;
|
|
}
|
|
|
|
fstore.fst_offset = offset;
|
|
fstore.fst_length = length;
|
|
|
|
if (fcntl(fd, F_PREALLOCATE, &fstore) == -1) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
#if HAVE_EXCHANE
|
|
|
|
- (BOOL)exchangeDataOfItemAtPath:(NSString *)path1
|
|
withItemAtPath:(NSString *)path2
|
|
error:(NSError **)error {
|
|
NSString *p1 = [rootPath_ stringByAppendingString:path1];
|
|
NSString *p2 = [rootPath_ stringByAppendingString:path2];
|
|
int ret = exchangedata([p1 UTF8String], [p2 UTF8String], 0);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
#endif /* HAVE_EXCHANGE */
|
|
|
|
#pragma mark Directory Contents
|
|
|
|
- (NSArray<GMDirectoryEntry *> *)contentsOfDirectoryAtPath:(NSString *)path
|
|
includingAttributesForKeys:(NSArray<NSString *> *)keys
|
|
error:(NSError * _Nullable *)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
NSArray<NSString *> *contents =
|
|
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:error];
|
|
if (!contents) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *entries = [NSMutableArray array];
|
|
for (NSString *n in contents) {
|
|
NSDictionary *d =
|
|
[[NSFileManager defaultManager] attributesOfItemAtPath:[p stringByAppendingPathComponent:n]
|
|
error:nil];
|
|
if (!d) {
|
|
continue;
|
|
}
|
|
|
|
GMDirectoryEntry *entry = [GMDirectoryEntry directoryEntryWithName:n attributes:d];
|
|
[entries addObject:entry];
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
#pragma mark Getting and Setting Attributes
|
|
|
|
#define DateFromTimespec(t) \
|
|
[NSDate dateWithTimeIntervalSince1970:((t).tv_sec + (t).tv_nsec / 1000000000.0)]
|
|
|
|
#define TimespecFromDate(d) \
|
|
({ \
|
|
NSTimeInterval _i = [(d) timeIntervalSince1970]; \
|
|
struct timespec _t; \
|
|
_t.tv_sec = (__darwin_time_t)_i; \
|
|
_t.tv_nsec = (long)((_i - _t.tv_sec) * 1000000000); \
|
|
_t; \
|
|
})
|
|
|
|
- (NSDictionary *)attributesOfItemAtPath:(NSString *)path
|
|
userData:(id)userData
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
NSDictionary *d =
|
|
[[NSFileManager defaultManager] attributesOfItemAtPath:p error:error];
|
|
if (d) {
|
|
NSMutableDictionary *attribs =
|
|
[NSMutableDictionary dictionaryWithDictionary:d];
|
|
int ret = 0;
|
|
|
|
struct stat stbuf;
|
|
ret = lstat([p UTF8String], &stbuf);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_BKUPTIME;
|
|
attributes.dirattr = 0;
|
|
attributes.fileattr = 0;
|
|
attributes.forkattr = 0;
|
|
attributes.volattr = 0;
|
|
|
|
struct timeattrbuf {
|
|
uint32_t size;
|
|
struct timespec bkuptime;
|
|
} __attribute__ ((packed));
|
|
|
|
struct timeattrbuf timebuf;
|
|
|
|
ret = getattrlist([p UTF8String], &attributes, &timebuf, sizeof(timebuf),
|
|
FSOPT_NOFOLLOW);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
[attribs setObject:[NSNumber numberWithInt:stbuf.st_flags]
|
|
forKey:kGMUserFileSystemFileFlagsKey];
|
|
[attribs setObject:DateFromTimespec(stbuf.st_atimespec)
|
|
forKey:kGMUserFileSystemFileAccessDateKey];
|
|
[attribs setObject:DateFromTimespec(stbuf.st_ctimespec)
|
|
forKey:kGMUserFileSystemFileChangeDateKey];
|
|
[attribs setObject:DateFromTimespec(timebuf.bkuptime)
|
|
forKey:kGMUserFileSystemFileBackupDateKey];
|
|
[attribs setObject:[NSNumber numberWithLongLong:stbuf.st_blocks]
|
|
forKey:kGMUserFileSystemFileSizeInBlocksKey];
|
|
[attribs setObject:[NSNumber numberWithInt:stbuf.st_blksize]
|
|
forKey:kGMUserFileSystemFileOptimalIOSizeKey];
|
|
return attribs;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
NSDictionary *d =
|
|
[[NSFileManager defaultManager] attributesOfFileSystemForPath:p
|
|
error:error];
|
|
if (d) {
|
|
NSMutableDictionary *attribs =
|
|
[NSMutableDictionary dictionaryWithDictionary:d];
|
|
|
|
struct statfs stbuf;
|
|
int ret = statfs([p UTF8String], &stbuf);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSURL *URL = [NSURL fileURLWithPath:p isDirectory:YES];
|
|
NSNumber *supportsCaseSensitiveNames = nil;
|
|
if (![URL getResourceValue:&supportsCaseSensitiveNames
|
|
forKey:NSURLVolumeSupportsCaseSensitiveNamesKey
|
|
error:error]) {
|
|
return nil;
|
|
}
|
|
if (!supportsCaseSensitiveNames) {
|
|
supportsCaseSensitiveNames = [NSNumber numberWithBool:YES];
|
|
}
|
|
|
|
[attribs setObject:[NSNumber numberWithBool:YES]
|
|
forKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey];
|
|
[attribs setObject:[NSNumber numberWithInt:255]
|
|
forKey:kGMUserFileSystemVolumeMaxFilenameLengthKey];
|
|
[attribs setObject:[NSNumber numberWithUnsignedLong:stbuf.f_bsize]
|
|
forKey:kGMUserFileSystemVolumeFileSystemBlockSizeKey];
|
|
[attribs setObject:supportsCaseSensitiveNames
|
|
forKey:kGMUserFileSystemVolumeSupportsCaseSensitiveNamesKey];
|
|
[attribs setObject:[NSNumber numberWithBool:YES]
|
|
forKey:kGMUserFileSystemVolumeSupportsSwapRenamingKey];
|
|
[attribs setObject:[NSNumber numberWithBool:YES]
|
|
forKey:kGMUserFileSystemVolumeSupportsExclusiveRenamingKey];
|
|
[attribs setObject:[NSNumber numberWithBool:YES]
|
|
forKey:kGMUserFileSystemVolumeSupportsSetVolumeNameKey];
|
|
[attribs setObject:[NSNumber numberWithBool:YES]
|
|
forKey:kGMUserFileSystemVolumeSupportsReadWriteNodeLockingKey];
|
|
return attribs;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)setAttributes:(NSDictionary *)attributes
|
|
ofItemAtPath:(NSString *)path
|
|
userData:(id)userData
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
|
|
NSNumber *offset = [attributes objectForKey:NSFileSize];
|
|
if (offset) {
|
|
int ret = truncate([p UTF8String], [offset longLongValue]);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
NSDate *accessDate = attributes[kGMUserFileSystemFileAccessDateKey];
|
|
if (accessDate) {
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_ACCTIME;
|
|
attributes.dirattr = 0;
|
|
attributes.fileattr = 0;
|
|
attributes.forkattr = 0;
|
|
attributes.volattr = 0;
|
|
|
|
struct timespec acctime = TimespecFromDate(accessDate);
|
|
int ret = setattrlist([p UTF8String], &attributes, &acctime,
|
|
sizeof(struct timespec), FSOPT_NOFOLLOW);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
NSDate *changeDate = attributes[kGMUserFileSystemFileChangeDateKey];
|
|
if (changeDate) {
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_CHGTIME;
|
|
attributes.dirattr = 0;
|
|
attributes.fileattr = 0;
|
|
attributes.forkattr = 0;
|
|
attributes.volattr = 0;
|
|
|
|
struct timespec chgtime = TimespecFromDate(changeDate);
|
|
int ret = setattrlist([p UTF8String], &attributes, &chgtime,
|
|
sizeof(struct timespec), FSOPT_NOFOLLOW);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
NSDate *backupDate = attributes[kGMUserFileSystemFileBackupDateKey];
|
|
if (backupDate) {
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_BKUPTIME;
|
|
attributes.dirattr = 0;
|
|
attributes.fileattr = 0;
|
|
attributes.forkattr = 0;
|
|
attributes.volattr = 0;
|
|
|
|
struct timespec bkuptime = TimespecFromDate(backupDate);
|
|
int ret = setattrlist([p UTF8String], &attributes, &bkuptime,
|
|
sizeof(struct timespec), FSOPT_NOFOLLOW);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
NSNumber *flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey];
|
|
if (flags != nil) {
|
|
int ret = lchflags([p UTF8String], [flags intValue]);
|
|
if (ret < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
return [[NSFileManager defaultManager] setAttributes:attributes
|
|
ofItemAtPath:p
|
|
error:error];
|
|
}
|
|
|
|
- (BOOL)setAttributes:(NSDictionary *)attributes
|
|
ofFileSystemAtPath:(NSString *)path
|
|
error:(NSError **)error {
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark Extended Attributes
|
|
|
|
#define G_PREFIX "org"
|
|
#define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security"
|
|
#define A_PREFIX "com"
|
|
#define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security"
|
|
#define XATTR_APPLE_PREFIX "com.apple."
|
|
|
|
- (NSArray *)extendedAttributesOfItemAtPath:(NSString *)path error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
|
|
ssize_t size = listxattr([p UTF8String], NULL, 0, XATTR_NOFOLLOW);
|
|
if (size < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSMutableData *data = [NSMutableData dataWithLength:size];
|
|
size = listxattr([p UTF8String], [data mutableBytes], [data length], XATTR_NOFOLLOW);
|
|
if (size < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray *contents = [NSMutableArray array];
|
|
char *ptr = (char *)[data bytes];
|
|
while (ptr < (((char *)[data bytes]) + size)) {
|
|
NSString *s = [NSString stringWithUTF8String:ptr];
|
|
if (strcmp(ptr, G_KAUTH_FILESEC_XATTR) != 0) {
|
|
[contents addObject:s];
|
|
}
|
|
ptr += ([s length] + 1);
|
|
}
|
|
return contents;
|
|
}
|
|
|
|
- (NSData *)valueOfExtendedAttribute:(NSString *)name
|
|
ofItemAtPath:(NSString *)path
|
|
position:(off_t)position
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
|
|
const char *n = [name UTF8String];
|
|
if (strcmp(n, A_KAUTH_FILESEC_XATTR) == 0) {
|
|
char new_name[MAXPATHLEN];
|
|
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
|
|
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
|
|
n = new_name;
|
|
}
|
|
|
|
ssize_t size = getxattr([p UTF8String], n, NULL, 0, (uint32_t)position,
|
|
XATTR_NOFOLLOW);
|
|
if (size < 0) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSMutableData *data = [NSMutableData dataWithLength:size];
|
|
ssize_t ret = getxattr([p UTF8String], n, [data mutableBytes], [data length],
|
|
(uint32_t)position, XATTR_NOFOLLOW);
|
|
if (ret == -1) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
- (BOOL)setExtendedAttribute:(NSString *)name
|
|
ofItemAtPath:(NSString *)path
|
|
value:(NSData *)value
|
|
position:(off_t)position
|
|
options:(int)options
|
|
error:(NSError **)error {
|
|
// Setting com.apple.FinderInfo happens in the kernel, so security related
|
|
// bits are set in the options. We need to explicitly remove them or the call
|
|
// to setxattr will fail.
|
|
// TODO: Why is this necessary?
|
|
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
|
|
const char *n = [name UTF8String];
|
|
if (strcmp(n, A_KAUTH_FILESEC_XATTR) == 0) {
|
|
char new_name[MAXPATHLEN];
|
|
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
|
|
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
|
|
n = new_name;
|
|
}
|
|
|
|
options |= XATTR_NOFOLLOW;
|
|
if (!strncmp(n, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) {
|
|
options &= ~(XATTR_NOSECURITY);
|
|
}
|
|
|
|
int ret = setxattr([p UTF8String], n, [value bytes], [value length],
|
|
(uint32_t)position, options);
|
|
if (ret == -1) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)removeExtendedAttribute:(NSString *)name
|
|
ofItemAtPath:(NSString *)path
|
|
error:(NSError **)error {
|
|
NSString *p = [rootPath_ stringByAppendingString:path];
|
|
|
|
const char *n = [name UTF8String];
|
|
if (strcmp(n, A_KAUTH_FILESEC_XATTR) == 0) {
|
|
char new_name[MAXPATHLEN];
|
|
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
|
|
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
|
|
n = new_name;
|
|
}
|
|
|
|
int res = removexattr([p UTF8String], n, XATTR_NOFOLLOW);
|
|
if (res == -1) {
|
|
if (error) {
|
|
*error = [NSError errorWithPOSIXCode:errno];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|