Skip to content

Instantly share code, notes, and snippets.

@fumiyas
Last active September 2, 2018 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fumiyas/fa3ff0f3e537af423fb99f1d8ebef87e to your computer and use it in GitHub Desktop.
Save fumiyas/fa3ff0f3e537af423fb99f1d8ebef87e to your computer and use it in GitHub Desktop.
macOS getxattr(2) is broken on SMB shares
/*
macOS getxattr(2) is broken on SMB shares:
* Cannot read xattr larger than 1048576 bytes.
* Fails by ERANGE (Result too large) if 'size' argument is smaller than
entire xattr size.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/xattr.h>
int main(int argc, char **argv) {
setbuf(stdout, NULL);
if (argc < 3) {
printf("Usage: %s PATH NAME [READ_CHUNK_SIZE]\n", argv[0]);
exit(1);
}
const char *path = argv[1];
const char *ea_name = argv[2];
size_t ea_read_chunk_size_max = (argc >= 4) ? strtol(argv[3], NULL, 10) : 0;
printf("path: %s\n", path);
printf("ea name: %s\n", ea_name);
ssize_t ea_size = getxattr(path, ea_name, NULL, 0, 0, 0);
if (ea_size == -1) {
fprintf(stderr, "%s: ERROR: getxattr failed: %s: %s: %s\n",
argv[0], path, ea_name, strerror(errno));
exit(1);
}
printf("ea size: %ld\n", (long)ea_size);
unsigned char *ea_value = malloc(ea_size);
if (ea_value == NULL) {
fprintf(stderr, "%s: ERROR: malloc failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
u_int32_t ea_read_offset = 0;
unsigned char *ea_value_ptr = ea_value;
size_t ea_read_remained = ea_size;
ssize_t ea_read_chunk_size;
for (long i=1; ea_read_offset < ea_size; i++) {
if (ea_read_chunk_size_max == 0 || ea_read_chunk_size_max > ea_read_remained) {
ea_read_chunk_size = ea_read_remained;
} else {
ea_read_chunk_size = ea_read_chunk_size_max;
}
ea_read_chunk_size = getxattr(path, ea_name, ea_value_ptr, ea_read_chunk_size, ea_read_offset, 0);
if (ea_read_chunk_size == -1) {
fprintf(stderr, "%s: ERROR: getxattr failed: %s: %s: %s\n",
argv[0], path, ea_name, strerror(errno));
exit(1);
}
printf(" loop %8ld: read chunk size=%ld, offset=%ld\n", i, (long)ea_read_chunk_size, (long)ea_read_offset);
ea_value_ptr += ea_read_chunk_size;
ea_read_offset += ea_read_chunk_size;
ea_read_remained -= ea_read_chunk_size;
}
printf("ea read size: %ld\n", (long)ea_read_offset);
}
$ ./getxattr '/Volumes/smb/foo.txt' com.apple.ResourceFork
path: /Volumes/smb/foo.txt
ea name: com.apple.ResourceFork
ea size: 6682679
loop 1: read chunk size=1048576, offset=0
./getxattr: ERROR: getxattr failed: /Volumes/smb/foo.txt: com.apple.ResourceFork: Result too large
$ ./getxattr '/Volumes/smb/foo.txt' com.apple.ResourceFork 100000
path: /Volumes/smb/foo.txt
ea name: com.apple.ResourceFork
ea size: 6682679
./getxattr: ERROR: getxattr failed: /Volumes/smb/foo.txt: com.apple.ResourceFork: Result too large
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment