build: Add ./configure --disable-fuse option.
[libguestfs.git] / daemon / xattr.c
index 70c9070..a5169cf 100644 (file)
 #include <stdio.h>
 #include <unistd.h>
 
-#include "../src/guestfs_protocol.h"
+#include "guestfs_protocol.h"
 #include "daemon.h"
 #include "actions.h"
+#include "optgroups.h"
 
-#ifdef HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
+#if defined(HAVE_ATTR_XATTR_H) || defined(HAVE_SYS_XATTR_H)
 
-static guestfs_int_xattr_list *getxattrs (char *path, ssize_t (*listxattr) (const char *path, char *list, size_t size), ssize_t (*getxattr) (const char *path, const char *name, void *value, size_t size));
-static int _setxattr (char *xattr, char *val, int vallen, char *path, int (*setxattr) (const char *path, const char *name, const void *value, size_t size, int flags));
-static int _removexattr (char *xattr, char *path, int (*removexattr) (const char *path, const char *name));
+# ifdef HAVE_ATTR_XATTR_H
+#  include <attr/xattr.h>
+# else
+#  ifdef HAVE_SYS_XATTR_H
+#   include <sys/xattr.h>
+#  endif
+# endif
+
+int
+optgroup_linuxxattrs_available (void)
+{
+  return 1;
+}
+
+static guestfs_int_xattr_list *getxattrs (const char *path, ssize_t (*listxattr) (const char *path, char *list, size_t size), ssize_t (*getxattr) (const char *path, const char *name, void *value, size_t size));
+static int _setxattr (const char *xattr, const char *val, int vallen, const char *path, int (*setxattr) (const char *path, const char *name, const void *value, size_t size, int flags));
+static int _removexattr (const char *xattr, const char *path, int (*removexattr) (const char *path, const char *name));
 
 guestfs_int_xattr_list *
-do_getxattrs (char *path)
+do_getxattrs (const char *path)
 {
 #if defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR)
   return getxattrs (path, listxattr, getxattr);
 #else
-  reply_with_error ("getxattrs: no support for listxattr and getxattr");
+  reply_with_error ("no support for listxattr and getxattr");
   return NULL;
 #endif
 }
 
 guestfs_int_xattr_list *
-do_lgetxattrs (char *path)
+do_lgetxattrs (const char *path)
 {
 #if defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR)
   return getxattrs (path, llistxattr, lgetxattr);
 #else
-  reply_with_error ("lgetxattrs: no support for llistxattr and lgetxattr");
+  reply_with_error ("no support for llistxattr and lgetxattr");
   return NULL;
 #endif
 }
 
 int
-do_setxattr (char *xattr, char *val, int vallen, char *path)
+do_setxattr (const char *xattr, const char *val, int vallen, const char *path)
 {
 #if defined(HAVE_SETXATTR)
   return _setxattr (xattr, val, vallen, path, setxattr);
 #else
-  reply_with_error ("setxattr: no support for setxattr");
+  reply_with_error ("no support for setxattr");
   return -1;
 #endif
 }
 
 int
-do_lsetxattr (char *xattr, char *val, int vallen, char *path)
+do_lsetxattr (const char *xattr, const char *val, int vallen, const char *path)
 {
 #if defined(HAVE_LSETXATTR)
   return _setxattr (xattr, val, vallen, path, lsetxattr);
 #else
-  reply_with_error ("lsetxattr: no support for lsetxattr");
+  reply_with_error ("no support for lsetxattr");
   return -1;
 #endif
 }
 
 int
-do_removexattr (char *xattr, char *path)
+do_removexattr (const char *xattr, const char *path)
 {
 #if defined(HAVE_REMOVEXATTR)
   return _removexattr (xattr, path, removexattr);
 #else
-  reply_with_error ("removexattr: no support for removexattr");
+  reply_with_error ("no support for removexattr");
   return -1;
 #endif
 }
 
 int
-do_lremovexattr (char *xattr, char *path)
+do_lremovexattr (const char *xattr, const char *path)
 {
 #if defined(HAVE_LREMOVEXATTR)
   return _removexattr (xattr, path, lremovexattr);
 #else
-  reply_with_error ("lremovexattr: no support for lremovexattr");
+  reply_with_error ("no support for lremovexattr");
   return -1;
 #endif
 }
 
 static guestfs_int_xattr_list *
-getxattrs (char *path,
-          ssize_t (*listxattr) (const char *path, char *list, size_t size),
-          ssize_t (*getxattr) (const char *path, const char *name,
-                               void *value, size_t size))
+getxattrs (const char *path,
+           ssize_t (*listxattr) (const char *path, char *list, size_t size),
+           ssize_t (*getxattr) (const char *path, const char *name,
+                                void *value, size_t size))
 {
   ssize_t len, vlen;
   char *buf = NULL;
   int i, j;
   guestfs_int_xattr_list *r = NULL;
 
-  NEED_ROOT (NULL);
-  ABS_PATH (path, NULL);
-
   CHROOT_IN;
   len = listxattr (path, NULL, 0);
   CHROOT_OUT;
   if (len == -1) {
-    reply_with_perror ("listxattr");
+    reply_with_perror ("listxattr: %s", path);
     goto error;
   }
 
@@ -130,7 +141,7 @@ getxattrs (char *path,
   len = listxattr (path, buf, len);
   CHROOT_OUT;
   if (len == -1) {
-    reply_with_perror ("listxattr");
+    reply_with_perror ("listxattr: %s", path);
     goto error;
   }
 
@@ -168,15 +179,15 @@ getxattrs (char *path,
     r->guestfs_int_xattr_list_val[j].attrval.attrval_len = vlen;
 
     if (r->guestfs_int_xattr_list_val[j].attrname == NULL ||
-       r->guestfs_int_xattr_list_val[j].attrval.attrval_val == NULL) {
+        r->guestfs_int_xattr_list_val[j].attrval.attrval_val == NULL) {
       reply_with_perror ("malloc");
       goto error;
     }
 
     CHROOT_IN;
     vlen = getxattr (path, &buf[i],
-                    r->guestfs_int_xattr_list_val[j].attrval.attrval_val,
-                    vlen);
+                     r->guestfs_int_xattr_list_val[j].attrval.attrval_val,
+                     vlen);
     CHROOT_OUT;
     if (vlen == -1) {
       reply_with_perror ("getxattr");
@@ -191,11 +202,13 @@ getxattrs (char *path,
  error:
   free (buf);
   if (r) {
-    if (r->guestfs_int_xattr_list_val)
-      for (i = 0; i < r->guestfs_int_xattr_list_len; ++i) {
-       free (r->guestfs_int_xattr_list_val[i].attrname);
-       free (r->guestfs_int_xattr_list_val[i].attrval.attrval_val);
+    if (r->guestfs_int_xattr_list_val) {
+      unsigned int k;
+      for (k = 0; k < r->guestfs_int_xattr_list_len; ++k) {
+        free (r->guestfs_int_xattr_list_val[k].attrname);
+        free (r->guestfs_int_xattr_list_val[k].attrval.attrval_val);
       }
+    }
     free (r->guestfs_int_xattr_list_val);
   }
   free (r);
@@ -203,9 +216,9 @@ getxattrs (char *path,
 }
 
 static int
-_setxattr (char *xattr, char *val, int vallen, char *path,
-          int (*setxattr) (const char *path, const char *name,
-                           const void *value, size_t size, int flags))
+_setxattr (const char *xattr, const char *val, int vallen, const char *path,
+           int (*setxattr) (const char *path, const char *name,
+                            const void *value, size_t size, int flags))
 {
   int r;
 
@@ -221,8 +234,8 @@ _setxattr (char *xattr, char *val, int vallen, char *path,
 }
 
 static int
-_removexattr (char *xattr, char *path,
-             int (*removexattr) (const char *path, const char *name))
+_removexattr (const char *xattr, const char *path,
+              int (*removexattr) (const char *path, const char *name))
 {
   int r;
 
@@ -237,48 +250,251 @@ _removexattr (char *xattr, char *path,
   return 0;
 }
 
-#else /* !HAVE_ATTR_XATTR_H */
-
 guestfs_int_xattr_list *
-do_getxattrs (char *path)
+do_lxattrlist (const char *path, char *const *names)
 {
-  reply_with_error ("getxattrs: no support for xattrs");
+#if defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR)
+  /* XXX This would be easier if the kernel had lgetxattrat.  In the
+   * meantime we use this buffer to store the whole path name.
+   */
+  char pathname[PATH_MAX];
+  size_t path_len = strlen (path);
+  guestfs_int_xattr_list *ret = NULL;
+  int i, j;
+  size_t k, m, nr_attrs;
+  ssize_t len, vlen;
+  char *buf = NULL;
+
+  if (path_len >= PATH_MAX) {
+    reply_with_perror ("path longer than PATH_MAX");
+    goto error;
+  }
+
+  strcpy (pathname, path);
+
+  ret = malloc (sizeof (*ret));
+  if (ret == NULL) {
+    reply_with_perror ("malloc");
+    goto error;
+  }
+
+  ret->guestfs_int_xattr_list_len = 0;
+  ret->guestfs_int_xattr_list_val = NULL;
+
+  for (k = 0; names[k] != NULL; ++k) {
+    /* Be careful in here about which errors cause the whole call
+     * to abort, and which errors allow us to continue processing
+     * the call, recording a special "error attribute" in the
+     * outgoing struct list.
+     */
+    if (path_len + strlen (names[k]) + 2 > PATH_MAX) {
+      reply_with_perror ("path and name longer than PATH_MAX");
+      goto error;
+    }
+    pathname[path_len] = '/';
+    strcpy (&pathname[path_len+1], names[k]);
+
+    /* Reserve space for the special attribute. */
+    void *newptr =
+      realloc (ret->guestfs_int_xattr_list_val,
+               (ret->guestfs_int_xattr_list_len+1)*sizeof (guestfs_int_xattr));
+    if (newptr == NULL) {
+      reply_with_perror ("realloc");
+      goto error;
+    }
+    ret->guestfs_int_xattr_list_val = newptr;
+    ret->guestfs_int_xattr_list_len++;
+
+    guestfs_int_xattr *entry =
+      &ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-1];
+    entry->attrname = NULL;
+    entry->attrval.attrval_len = 0;
+    entry->attrval.attrval_val = NULL;
+
+    entry->attrname = strdup ("");
+    if (entry->attrname == NULL) {
+      reply_with_perror ("strdup");
+      goto error;
+    }
+
+    CHROOT_IN;
+    len = llistxattr (pathname, NULL, 0);
+    CHROOT_OUT;
+    if (len == -1)
+      continue; /* not fatal */
+
+    buf = malloc (len);
+    if (buf == NULL) {
+      reply_with_perror ("malloc");
+      goto error;
+    }
+
+    CHROOT_IN;
+    len = llistxattr (pathname, buf, len);
+    CHROOT_OUT;
+    if (len == -1)
+      continue; /* not fatal */
+
+    /* What we get from the kernel is a string "foo\0bar\0baz" of length
+     * len.  First count the strings.
+     */
+    nr_attrs = 0;
+    for (i = 0; i < len; i += strlen (&buf[i]) + 1)
+      nr_attrs++;
+
+    newptr =
+      realloc (ret->guestfs_int_xattr_list_val,
+               (ret->guestfs_int_xattr_list_len+nr_attrs) *
+               sizeof (guestfs_int_xattr));
+    if (newptr == NULL) {
+      reply_with_perror ("realloc");
+      goto error;
+    }
+    ret->guestfs_int_xattr_list_val = newptr;
+    ret->guestfs_int_xattr_list_len += nr_attrs;
+
+    /* entry[0] is the special attribute,
+     * entry[1..nr_attrs] are the attributes.
+     */
+    entry = &ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-nr_attrs-1];
+    for (m = 1; m <= nr_attrs; ++m) {
+      entry[m].attrname = NULL;
+      entry[m].attrval.attrval_len = 0;
+      entry[m].attrval.attrval_val = NULL;
+    }
+
+    for (i = 0, j = 0; i < len; i += strlen (&buf[i]) + 1, ++j) {
+      CHROOT_IN;
+      vlen = lgetxattr (pathname, &buf[i], NULL, 0);
+      CHROOT_OUT;
+      if (vlen == -1) {
+        reply_with_perror ("getxattr");
+        goto error;
+      }
+
+      entry[j+1].attrname = strdup (&buf[i]);
+      entry[j+1].attrval.attrval_val = malloc (vlen);
+      entry[j+1].attrval.attrval_len = vlen;
+
+      if (entry[j+1].attrname == NULL ||
+          entry[j+1].attrval.attrval_val == NULL) {
+        reply_with_perror ("malloc");
+        goto error;
+      }
+
+      CHROOT_IN;
+      vlen = lgetxattr (pathname, &buf[i],
+                        entry[j+1].attrval.attrval_val, vlen);
+      CHROOT_OUT;
+      if (vlen == -1) {
+        reply_with_perror ("getxattr");
+        goto error;
+      }
+    }
+
+    free (buf); buf = NULL;
+
+    char num[16];
+    snprintf (num, sizeof num, "%zu", nr_attrs);
+    entry[0].attrval.attrval_len = strlen (num) + 1;
+    entry[0].attrval.attrval_val = strdup (num);
+
+    if (entry[0].attrval.attrval_val == NULL) {
+      reply_with_perror ("strdup");
+      goto error;
+    }
+  }
+
+  /* If verbose, debug what we're about to send back. */
+  if (verbose) {
+    fprintf (stderr, "lxattrlist: returning: [\n");
+    for (k = 0; k < ret->guestfs_int_xattr_list_len; ++k) {
+      const guestfs_int_xattr *entry = &ret->guestfs_int_xattr_list_val[k];
+      if (STRNEQ (entry[0].attrname, "")) {
+        fprintf (stderr, "ERROR: expecting empty attrname at k = %zu\n", k);
+        break;
+      }
+      fprintf (stderr, "  %zu: special attrval = %s\n",
+               k, entry[0].attrval.attrval_val);
+      for (i = 1; k+i < ret->guestfs_int_xattr_list_len; ++i) {
+        if (STREQ (entry[i].attrname, ""))
+          break;
+        fprintf (stderr, "    name %s, value length %d\n",
+                 entry[i].attrname, entry[i].attrval.attrval_len);
+      }
+      k += i-1;
+    }
+    fprintf (stderr, "]\n");
+  }
+
+  return ret;
+
+ error:
+  free (buf);
+  if (ret) {
+    if (ret->guestfs_int_xattr_list_val) {
+      for (k = 0; k < ret->guestfs_int_xattr_list_len; ++k) {
+        free (ret->guestfs_int_xattr_list_val[k].attrname);
+        free (ret->guestfs_int_xattr_list_val[k].attrval.attrval_val);
+      }
+      free (ret->guestfs_int_xattr_list_val);
+    }
+    free (ret);
+  }
+  return NULL;
+#else
+  reply_with_error ("no support for llistxattr and lgetxattr");
   return NULL;
+#endif
+}
+
+#else /* no xattr.h */
+int
+optgroup_linuxxattrs_available (void)
+{
+  return 0;
 }
 
 guestfs_int_xattr_list *
-do_lgetxattrs (char *path)
+do_getxattrs (const char *path)
 {
-  reply_with_error ("lgetxattrs: no support for xattrs");
-  return NULL;
+  NOT_AVAILABLE (NULL);
+}
+
+guestfs_int_xattr_list *
+do_lgetxattrs (const char *path)
+{
+  NOT_AVAILABLE (NULL);
 }
 
 int
-do_setxattr (char *xattr, char *val, int vallen, char *path)
+do_setxattr (const char *xattr, const char *val, int vallen, const char *path)
 {
-  reply_with_error ("setxattr: no support for xattrs");
-  return -1;
+  NOT_AVAILABLE (-1);
 }
 
 int
-do_lsetxattr (char *xattr, char *val, int vallen, char *path)
+do_lsetxattr (const char *xattr, const char *val, int vallen, const char *path)
 {
-  reply_with_error ("lsetxattr: no support for xattrs");
-  return -1;
+  NOT_AVAILABLE (-1);
 }
 
 int
-do_removexattr (char *xattr, char *path)
+do_removexattr (const char *xattr, const char *path)
 {
-  reply_with_error ("removexattr: no support for xattrs");
-  return -1;
+  NOT_AVAILABLE (-1);
 }
 
 int
-do_lremovexattr (char *xattr, char *path)
+do_lremovexattr (const char *xattr, const char *path)
 {
-  reply_with_error ("lremovexattr: no support for xattrs");
-  return -1;
+  NOT_AVAILABLE (-1);
+}
+
+guestfs_int_xattr_list *
+do_lxattrlist (const char *path, char *const *names)
+{
+  NOT_AVAILABLE (NULL);
 }
 
-#endif /* !HAVE_ATTR_XATTR_H */
+#endif /* no xattr.h */