Wed, 27 Nov 2024 23:00:07 +0100
add TODO to use a future ucx feature
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2018 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include "libxattr.h" #include <errno.h> #include <sys/types.h> #include <string.h> #define LIST_BUF_LEN 1024 #define LIST_ARRAY_LEN 8 #define ATTR_BUF_LEN 1024 #define ARRAY_ADD(array, pos, len, obj) if(pos >= len) { \ len *= 2; /* TODO: missing error handling for realloc() */ \ array = realloc(array, len * sizeof(char*)); \ } \ array[pos] = obj; \ pos++; static void* libxattr_malloc(void *unused, size_t size) { return malloc(size); } static void libxattr_free(void *unused, void *ptr) { free(ptr); } #ifdef __linux__ #define XATTR_SUPPORTED #include <sys/xattr.h> static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { size_t arraylen = LIST_ARRAY_LEN; size_t arraypos = 0; char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); char *begin = buf; char *name = NULL; for(int i=0;i<length;i++) { if(!name && buf[i] == '.') { int nslen = (buf+i-begin); //printf("%.*s\n", nslen, begin); name = buf + i + 1; } if(buf[i] == '\0') { char *attrname = strdup(name); ARRAY_ADD(array, arraypos, arraylen, attrname); begin = buf + i + 1; name = 0; } } if(arraypos == 0) { free(array); array = NULL; } *nelm = arraypos; return array; } char ** xattr_list(const char *path, ssize_t *nelm) { char *list = malloc(LIST_BUF_LEN); ssize_t len = listxattr(path, list, LIST_BUF_LEN); if(len == -1) { switch(errno) { case ERANGE: { // buffer too, get size of attribute list ssize_t newlen = listxattr(path, NULL, 0); if(newlen > 0) { // second try list = realloc(list, newlen); len = listxattr(path, list, newlen); if(len != -1) { // this time it worked break; } } } default: { free(list); *nelm = -1; return NULL; } } } char **ret = parse_xattrlist(list, len, nelm); free(list); return ret; } static char* name2nsname(const char *name) { // add the 'user' namespace to the name size_t namelen = strlen(name); char *attrname = malloc(8 + namelen); if(!attrname) { return NULL; } memcpy(attrname, "user.", 5); memcpy(attrname+5, name, namelen + 1); return attrname; } char * xattr_get_alloc( void *pool, libxattr_malloc_func malloc_func, libxattr_free_func free_func, const char *path, const char *attr, ssize_t *len) { char *buf = malloc_func(pool, ATTR_BUF_LEN); if(!buf) { *len = -1; return NULL; } char *attrname = name2nsname(attr); if(!attrname) { free_func(pool, buf); *len = -1; return NULL; } ssize_t vlen = getxattr(path, attrname, buf, ATTR_BUF_LEN - 1); if(vlen < 0) { switch(errno) { case ERANGE: { ssize_t attrlen = getxattr(path, attrname, NULL, 0); if(attrlen > 0) { free_func(pool, buf); buf = malloc_func(pool, attrlen + 1); if(!buf) { free(attrname); *len = -1; return NULL; } vlen = getxattr(path, attrname, buf, attrlen); if(vlen > 0) { break; } } } default: { *len = -1; free_func(pool, buf); free(attrname); return NULL; } } } buf[vlen] = 0; free(attrname); *len = vlen; return buf; } int xattr_set(const char *path, const char *name, const void *value, size_t len) { char *attrname = name2nsname(name); int ret = setxattr(path, attrname, value, len, 0); free(attrname); return ret; } int xattr_remove(const char *path, const char *name) { char *attrname = name2nsname(name); int ret = removexattr(path, attrname); free(attrname); return ret; } #endif /* Linux */ #ifdef __APPLE__ #define XATTR_SUPPORTED #include <sys/xattr.h> static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { size_t arraylen = LIST_ARRAY_LEN; size_t arraypos = 0; char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); char *name = buf; for(int i=0;i<length;i++) { if(buf[i] == '\0') { char *attrname = strdup(name); ARRAY_ADD(array, arraypos, arraylen, attrname); name = buf + i + 1; } } if(arraypos == 0) { free(array); array = NULL; } *nelm = arraypos; return array; } char ** xattr_list(const char *path, ssize_t *nelm) { char *list = malloc(LIST_BUF_LEN); ssize_t len = listxattr(path, list, LIST_BUF_LEN, 0); if(len == -1) { switch(errno) { case ERANGE: { // buffer too, get size of attribute list ssize_t newlen = listxattr(path, NULL, 0, 0); if(newlen > 0) { // second try list = realloc(list, newlen); len = listxattr(path, list, newlen, 0); if(len != -1) { // this time it worked break; } } } default: { free(list); *nelm = -1; return NULL; } } } char **ret = parse_xattrlist(list, len, nelm); free(list); return ret; } char * xattr_get_alloc( void *pool, libxattr_malloc_func malloc_func, libxattr_free_func free_func, const char *path, const char *attr, ssize_t *len) { // get attribute length ssize_t attrlen = getxattr(path, attr, NULL, 0, 0, 0); if(attrlen < 0) { *len = -1; return NULL; } char *buf = malloc_func(pool, attrlen + 1); if(!buf) { *len = -1; return NULL; } ssize_t vlen = getxattr(path, attr, buf, attrlen, 0, 0); if(vlen < 0) { *len = -1; free_func(pool, buf); return NULL; } buf[attrlen] = 0; *len = vlen; return buf; } int xattr_set(const char *path, const char *name, const void *value, size_t len) { int ret = setxattr(path, name, value, len, 0, 0); return ret; } int xattr_remove(const char *path, const char *name) { return removexattr(path, name, 0); } #endif /* Apple */ #ifdef __sun #define XATTR_SUPPORTED #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <fcntl.h> static int open_attrfile(const char *path, const char *attr, int oflag) { int file = open(path, O_RDONLY); if(file == -1) { return -1; } int attrfile = openat(file, attr, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); close(file); return attrfile; } char ** xattr_list(const char *path, ssize_t *nelm) { *nelm = -1; int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); if(attrdir == -1) { return NULL; } DIR *dir = fdopendir(attrdir); if(!dir) { close(attrdir); return NULL; } size_t arraylen = LIST_ARRAY_LEN; size_t arraypos = 0; char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); struct dirent *ent; while((ent = readdir(dir)) != NULL) { if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "SUNWattr_ro") || !strcmp(ent->d_name, "SUNWattr_rw")) { continue; } char *name = strdup(ent->d_name); ARRAY_ADD(array, arraypos, arraylen, name); } closedir(dir); *nelm = arraypos; return array; } char * xattr_get_alloc( void *pool, libxattr_malloc_func malloc_func, libxattr_free_func free_func, const char *path, const char *attr, ssize_t *len) { *len = -1; int attrfile = open_attrfile(path, attr, O_RDONLY|O_XATTR); if(attrfile == -1) { return NULL; } struct stat s; if(fstat(attrfile, &s)) { close(attrfile); return NULL; } size_t bufsize = (size_t)s.st_size; char *buf = malloc_func(pool, bufsize); char *b = buf; size_t cur = 0; while(cur < bufsize) { ssize_t r = read(attrfile, buf + cur, bufsize - cur); if(r <= 0) { break; } cur += r; } close(attrfile); if(cur != bufsize) { free_func(pool, buf); return NULL; } *len = (ssize_t)bufsize; return buf; } int xattr_set(const char *path, const char *name, const void *value, size_t len) { int attrfile = open_attrfile(path, name, O_CREAT|O_WRONLY|O_XATTR|O_TRUNC); if(attrfile == -1) { return -1; } const char *p = value; size_t remaining = len; while(remaining > 0) { ssize_t w = write(attrfile, p, remaining); if(w <= 0) { break; } p += w; remaining -= w; } close(attrfile); return remaining > 0 ? -1 : 0; } int xattr_remove(const char *path, const char *name) { int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); if(attrdir == -1) { return -1; } int ret = unlinkat(attrdir, name, 0); close(attrdir); return ret; } #endif /* Sun */ #ifdef __FreeBSD__ #define XATTR_SUPPORTED #include <sys/types.h> #include <sys/extattr.h> static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { size_t arraylen = LIST_ARRAY_LEN; size_t arraypos = 0; char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); char *name = buf; for(int i=0;i<length;i++) { char namelen = buf[i]; char *name = buf + i + 1; char *attrname = malloc(namelen + 1); memcpy(attrname, name, namelen); attrname[namelen] = 0; ARRAY_ADD(array, arraypos, arraylen, attrname); i += namelen; } if(arraypos == 0) { free(array); array = NULL; } *nelm = arraypos; return array; } char ** xattr_list(const char *path, ssize_t *nelm) { *nelm = -1; ssize_t lslen = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); if(lslen <= 0) { if(lslen == 0) { *nelm = 0; } return NULL; } char *list = malloc(lslen); ssize_t len = extattr_list_file(path, EXTATTR_NAMESPACE_USER, list, lslen); if(len == -1) { free(list); return NULL; } char **ret = parse_xattrlist(list, len, nelm); free(list); return ret; } char * xattr_get_alloc( void *pool, libxattr_malloc_func malloc_func, libxattr_free_func free_func, const char *path, const char *attr, ssize_t *len) { // get attribute length ssize_t attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, NULL, 0); if(attrlen < 0) { *len = -1; return NULL; } char *buf = malloc_func(pool, attrlen + 1); ssize_t vlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, buf, attrlen); if(vlen < 0) { *len = -1; free_func(pool, buf); return NULL; } buf[attrlen] = 0; *len = vlen; return buf; } int xattr_set(const char *path, const char *name, const void *value, size_t len) { int ret = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, value, len); return ret; } int xattr_remove(const char *path, const char *name) { return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); } #endif /* FreeBSD */ #ifndef XATTR_SUPPORTED char ** xattr_list(const char *path, ssize_t *nelm) { *nelm = -1; return NULL; } char * xattr_get_alloc( void *pool, libxattr_malloc_func malloc_func, libxattr_free_func free_func, const char *path, const char *attr, ssize_t *len) { *len = -1; return NULL; } int xattr_set(const char *path, const char *name, const void *value, size_t len) { return -1; } int xattr_remove(const char *path, const char *name) { return -1; } #endif /* unsupported platform */ char * xattr_get(const char *path, const char *attr, ssize_t *len) { return xattr_get_alloc(NULL, libxattr_malloc, libxattr_free, path, attr, len); } void xattr_free_list(char **attrnames, ssize_t nelm) { if(attrnames) { for(int i=0;i<nelm;i++) { free(attrnames[i]); } free(attrnames); } }