src/server/util/libxattr.c

changeset 480
9f69e4b8b695
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/util/libxattr.c	Sat Mar 18 15:52:35 2023 +0100
@@ -0,0 +1,576 @@
+/*
+ * 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);
+    }
+}

mercurial