0

I am trying to write some data (the length of the data is 367 bytes) in the header of a file using the following code:

const char *attrName = [kAttributeForKey UTF8String];
const char *path = [filePath fileSystemRepresentation];

const uint8_t *myDataBytes = (const uint8_t*)[myData bytes];
int result = setxattr(path, attrName, myDataBytes, sizeof(myDataBytes), 0, 0);

When I try to read it, the result is different:

const char *attrName = [kAttributeForKey UTF8String];
const char *path = [filePath fileSystemRepresentation];

int bufferLength = getxattr(path, attrName, NULL, 0, 0, 0);
char *buffer = malloc(bufferLength);
getxattr(path, attrName, buffer, bufferLength, 0, 0);

NSData *myData = [[NSData alloc] initWithBytes:buffer length:bufferLength];  
free(buffer);

Could someone tell me how can I make this work? Thanks in advance.

Levi
  • 7,313
  • 2
  • 32
  • 44
  • Well, for starters you're artificially limiting your getter to 255 bytes. If your data is longer, it'll be truncated-- you're already mallocing the correct size buffer to hold it all, why not pass in bufferLength to `getxattr` ? – Ben Zotto May 06 '13 at 13:59
  • @BenZotto Thanks, I've changed it. Anything else? – Levi May 06 '13 at 14:05
  • 1
    Just in case anybody needs it: Here is a Swift wrapper getting, setting, listing and removing extended attributes: http://stackoverflow.com/questions/38343186/write-extend-file-attributes-swift-example. – Martin R Oct 12 '16 at 08:36

3 Answers3

5

Here's a convenient NSFileManager category that gets and sets an NSString as a file's extended attribute.

+ (NSString *)xattrStringValueForKey:(NSString *)key atURL:(NSURL *)URL
{
    NSString *value = nil;
    const char *keyName = key.UTF8String;
    const char *filePath = URL.fileSystemRepresentation;

    ssize_t bufferSize = getxattr(filePath, keyName, NULL, 0, 0, 0);

    if (bufferSize != -1) {
        char *buffer = malloc(bufferSize+1);

        if (buffer) {
            getxattr(filePath, keyName, buffer, bufferSize, 0, 0);
            buffer[bufferSize] = '\0';
            value = [NSString stringWithUTF8String:buffer];
            free(buffer);
        }
    }
    return value;
}

+ (BOOL)setXAttrStringValue:(NSString *)value forKey:(NSString *)key atURL:(NSURL *)URL
{
    int failed = setxattr(URL.fileSystemRepresentation, key.UTF8String, value.UTF8String, value.length, 0, 0);
    return (failed == 0);
}
Peter
  • 2,005
  • 1
  • 20
  • 14
3

The problem is with your call to setxattr. The sizeof call can't be used. You want:

int result = setxattr(path, attrName, myDataBytes, [myData length], 0, 0);

The call to sizeof(myDataBytes) will return the size of the pointer, not the length of the data.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
0

Read the section "Getting and Setting attributes" here.

For your example here is some basic approach, maybe it'll help:

NSFileManager *fm = [NSFileManager defaultManager];

NSURL *path;
/*
 * You can set the following attributes: NSFileBusy, NSFileCreationDate, 
   NSFileExtensionHidden, NSFileGroupOwnerAccountID, NSFileGroupOwnerAccountName, 
   NSFileHFSCreatorCode, NSFileHFSTypeCode, NSFileImmutable, NSFileModificationDate,
   NSFileOwnerAccountID, NSFileOwnerAccountName, NSFilePosixPermissions
 */
[fm setAttributes:@{ NSFileOwnerAccountName : @"name" } ofItemAtPath:path error:nil];
abbood
  • 23,101
  • 16
  • 132
  • 246
art-divin
  • 1,635
  • 1
  • 20
  • 28