dav/libxattr.c

changeset 361
b6f2462ee055
child 364
3769ba002fd1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dav/libxattr.c	Mon Jan 01 19:53:36 2018 +0100
@@ -0,0 +1,444 @@
+#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; \
+        array = realloc(array, len * sizeof(char*)); \
+    } \
+    array[pos] = obj; \
+    pos++;
+
+
+#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);
+    memcpy(attrname, "user.", 5);
+    memcpy(attrname+5, name, namelen + 1);
+    return attrname;
+}
+
+char * xattr_get(const char *path, const char *attr, ssize_t *len) {
+    char *attrname = name2nsname(attr);
+    
+    char *buf = malloc(ATTR_BUF_LEN);
+    ssize_t vlen = getxattr(path, attrname, buf, ATTR_BUF_LEN);
+    if(vlen < 0) {
+        switch(errno) {
+            case ERANGE: {
+                ssize_t attrlen = getxattr(path, attrname, NULL, 0);
+                if(attrlen > 0) {
+                    free(buf);
+                    buf = malloc(attrlen);
+                    vlen = getxattr(path, attrname, buf, attrlen);
+                    if(vlen > 0) {
+                        break;
+                    }
+                }
+            }
+            default: {
+                *len = -1;
+                free(buf);
+                free(attrname);
+                return NULL;
+            }
+        }
+    }
+    
+    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;
+}
+
+#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(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(attrlen);
+    ssize_t vlen = getxattr(path, attr, buf, attrlen, 0, 0);
+    if(vlen < 0) {
+        *len = -1;
+        free(buf);
+        return NULL;
+    }
+    
+    *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;
+}
+
+#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(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(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(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);
+    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;
+}
+
+#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(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(attrlen);
+    ssize_t vlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, buf, attrlen);
+    if(vlen < 0) {
+        *len = -1;
+        free(buf);
+        return NULL;
+    }
+    
+    *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;
+}
+
+#endif /* FreeBSD */
+
+
+#ifndef XATTR_SUPPORTED
+
+char ** xattr_list(const char *path, ssize_t *nelm) {
+    *nelm = -1;
+    return NULL;
+}
+
+char * xattr_get(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;
+}
+
+#endif /* unsupported platform */
+
+void xattr_free_list(char **attrnames, ssize_t nelm) {
+    if(attrnames) {
+        for(int i=0;i<nelm;i++) {
+            free(attrnames[i]);
+        }
+        free(attrnames);
+    }
+}

mercurial