From: Torsten Schmutzler <git-ts@theblacksun.eu>
Date: Thu, 18 Oct 2012 09:02:38 +0000 (+0200)
Subject: Add wrappers for *xattr calls
X-Git-Url: http://anonscm.debian.org/gitweb/?p=users%2Fclint%2Ffakeroot.git;a=commitdiff_plain;h=b5159a93f0ea7f1d7cf788754ef4d8c7d30dd22e;hp=4f9c384058aa846ac603572619770234ea916079

Add wrappers for *xattr calls

Conflicts:
	debian/control
---

diff --git a/communicate.c b/communicate.c
index 02b258a..293f404 100644
--- a/communicate.c
+++ b/communicate.c
@@ -612,6 +612,9 @@ static void send_fakem_nr(const struct fake_msg *buf)
   fm.st.mode = htonl(buf->st.mode);
   fm.st.nlink = htonl(buf->st.nlink);
   fm.remote = htonl(0);
+  fm.xattr.buffersize = htonl(buf->xattr.buffersize);
+  fm.xattr.flags_rc = htonl(buf->xattr.flags_rc);
+  memcpy(fm.xattr.buf, buf->xattr.buf, MAX_IPC_BUFFER_SIZE);
 
   while (1) {
     ssize_t len;
@@ -668,6 +671,8 @@ static void get_fakem_nr(struct fake_msg *buf)
   buf->st.mode = ntohl(buf->st.mode);
   buf->st.nlink = ntohl(buf->st.nlink);
   buf->remote = ntohl(buf->remote);
+  buf->xattr.buffersize = ntohl(buf->xattr.buffersize);
+  buf->xattr.flags_rc = ntohl(buf->xattr.flags_rc);
 }
 
 void send_get_fakem(struct fake_msg *buf)
@@ -756,6 +761,63 @@ void send_get_stat(struct stat *st
   }
 }
 
+void send_get_xattr(struct stat *st
+		, xattr_args *xattr
+#ifdef STUPID_ALPHA_HACK
+		, int ver
+#endif
+		){
+  struct fake_msg buf;
+  size_t in_size;
+  size_t name_size;
+  size_t total_size;
+
+#ifndef FAKEROOT_FAKENET
+  if(init_get_msg()!=-1)
+#endif /* ! FAKEROOT_FAKENET */
+  {
+#ifndef STUPID_ALPHA_HACK
+    cpyfakemstat(&buf,st);
+#else
+    cpyfakemstat(&buf,st,ver);
+#endif
+    in_size = xattr->size;
+    total_size = (xattr->func == setxattr_func) ? (in_size) : 0;
+    if (xattr->name)
+    {
+      name_size = strlen(xattr->name);
+      total_size += name_size + 1;
+    }
+    if (total_size > MAX_IPC_BUFFER_SIZE)
+    {
+      xattr->rc = ERANGE;
+      return;
+    }
+    if (xattr->name) {
+      strcpy(buf.xattr.buf, xattr->name);
+      if (xattr->func == setxattr_func)
+        memcpy(&buf.xattr.buf[name_size + 1], xattr->value, in_size);
+    }
+    buf.xattr.buffersize = total_size;
+    buf.xattr.flags_rc = xattr->flags;
+    buf.id=xattr->func;
+    send_get_fakem(&buf);
+    xattr->rc = buf.xattr.flags_rc;
+    xattr->size = buf.xattr.buffersize;
+    if (buf.xattr.buffersize) {
+      if (!in_size) {
+        /* Special case. Return size of required buffer */
+        return;
+      }
+      if (xattr->size > in_size) {
+        xattr->rc = ERANGE;
+        return;
+      }
+      memcpy(xattr->value, buf.xattr.buf, xattr->size);
+    }
+  }
+}
+
 #ifdef STAT64_SUPPORT
 void send_get_stat64(struct stat64 *st
 #ifdef STUPID_ALPHA_HACK
@@ -784,6 +846,63 @@ void send_get_stat64(struct stat64 *st
 #endif
   }
 }
+
+void send_get_xattr64(struct stat64 *st
+		, xattr_args *xattr
+#ifdef STUPID_ALPHA_HACK
+		, int ver
+#endif
+		){
+  struct fake_msg buf;
+  size_t in_size;
+  size_t name_size;
+  size_t total_size;
+
+#ifndef FAKEROOT_FAKENET
+  if(init_get_msg()!=-1)
+#endif /* ! FAKEROOT_FAKENET */
+  {
+#ifndef STUPID_ALPHA_HACK
+    cpyfakemstat64(&buf,st);
+#else
+    cpyfakemstat64(&buf,st,ver);
+#endif
+    in_size = xattr->size;
+    total_size = (xattr->func == setxattr_func) ? (in_size) : 0;
+    if (xattr->name)
+    {
+      name_size = strlen(xattr->name);
+      total_size += name_size + 1;
+    }
+    if (total_size > MAX_IPC_BUFFER_SIZE)
+    {
+      xattr->rc = ERANGE;
+      return;
+    }
+    if (xattr->name) {
+      strcpy(buf.xattr.buf, xattr->name);
+      if (xattr->func == setxattr_func)
+        memcpy(&buf.xattr.buf[name_size + 1], xattr->value, in_size);
+    }
+    buf.xattr.buffersize = total_size;
+    buf.xattr.flags_rc = xattr->flags;
+    buf.id=xattr->func;
+    send_get_fakem(&buf);
+    xattr->rc = buf.xattr.flags_rc;
+    xattr->size = buf.xattr.buffersize;
+    if (buf.xattr.buffersize) {
+      if (!in_size) {
+        /* Special case. Return size of required buffer */
+        return;
+      }
+      if (xattr->size > in_size) {
+        xattr->rc = ERANGE;
+        return;
+      }
+      memcpy(xattr->value, buf.xattr.buf, xattr->size);
+    }
+  }
+}
 #endif /* STAT64_SUPPORT */
 
 #ifndef FAKEROOT_FAKENET
diff --git a/communicate.h b/communicate.h
index 947e807..3f9ca39 100644
--- a/communicate.h
+++ b/communicate.h
@@ -139,15 +139,28 @@
    enabled any more, but used to be in libtricks.
 */
 
-enum {chown_func,
-        /*2*/  chmod_func,
-        /*3*/  mknod_func,
-               stat_func,
-        /*5*/  unlink_func,
-               debugdata_func,
-               reqoptions_func,
+enum {chown_func = 0,
+               chmod_func = 1,
+               mknod_func = 2,
+               stat_func = 3,
+               unlink_func = 4,
+               debugdata_func = 5,
+               reqoptions_func = 6,
+               listxattr_func = 7,
+               getxattr_func = 8,
+               setxattr_func = 9,
+               removexattr_func = 10,
                last_func};
 
+typedef struct {
+  int func;
+  const char *name;
+  char *value;
+  size_t size;
+  int flags;
+  int rc;
+} xattr_args;
+
 #include "message.h"
 
 extern const char *env_var_set(const char *env);
@@ -162,6 +175,11 @@ extern void send_get_stat(struct stat *buf);
 #else
 extern void send_get_stat(struct stat *buf,int ver);
 #endif
+#ifndef STUPID_ALPHA_HACK
+extern void send_get_xattr(struct stat *st, xattr_args *xattr);
+#else
+extern void send_get_xattr(struct stat *st, xattr_args *xattr, int ver);
+#endif
 extern void cpyfakefake (struct fakestat *b1, const struct fakestat *b2);
 #ifndef STUPID_ALPHA_HACK
 extern void cpystatfakem(struct     stat *st, const struct fake_msg *buf);
@@ -189,9 +207,11 @@ extern void unlock_comm_sd(void);
 #ifndef STUPID_ALPHA_HACK
 extern void send_stat64(const struct stat64 *st, func_id_t f);
 extern void send_get_stat64(struct stat64 *buf);
+extern void send_get_xattr64(struct stat64 *st, xattr_args *xattr);
 #else
 extern void send_stat64(const struct stat64 *st, func_id_t f, int ver);
 extern void send_get_stat64(struct stat64 *buf, int ver);
+extern void send_get_xattr64(struct stat64 *st, xattr_args *xattr, int ver);
 #endif
 extern void stat64from32(struct stat64 *s64, const struct stat *s32);
 extern void stat32from64(struct stat *s32, const struct stat64 *s64);
diff --git a/configure.ac b/configure.ac
index 43a0984..6cf46ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,7 +60,7 @@ AC_SUBST(signal)
 dnl Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STDC
-AC_CHECK_HEADERS(fcntl.h unistd.h features.h sys/feature_tests.h pthread.h stdint.h inttypes.h grp.h endian.h sys/sysmacros.h sys/socket.h sys/acl.h fts.h)
+AC_CHECK_HEADERS(fcntl.h unistd.h features.h sys/feature_tests.h pthread.h stdint.h inttypes.h grp.h endian.h sys/sysmacros.h sys/socket.h sys/acl.h sys/capability.h sys/xattr.h fts.h)
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
diff --git a/faked.c b/faked.c
index 1b12b1c..66e0b32 100644
--- a/faked.c
+++ b/faked.c
@@ -104,6 +104,9 @@
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/types.h>
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
 #include <fcntl.h>
 #include <ctype.h>
 #include <stdio.h>
@@ -150,6 +153,10 @@ void process_chmod(struct fake_msg *buf);
 void process_mknod(struct fake_msg *buf);
 void process_stat(struct fake_msg *buf);
 void process_unlink(struct fake_msg *buf);
+void process_listxattr(struct fake_msg *buf);
+void process_setxattr(struct fake_msg *buf);
+void process_getxattr(struct fake_msg *buf);
+void process_removexattr(struct fake_msg *buf);
 
 #ifdef FAKEROOT_FAKENET
 static int get_fakem(struct fake_msg *buf);
@@ -162,6 +169,12 @@ process_func func_arr[]={process_chown,
 			 process_mknod,
 			 process_stat,
 			 process_unlink,
+			 NULL, /* debugdata */
+			 NULL, /* reqoptions */
+			 process_listxattr,
+			 process_getxattr,
+			 process_setxattr,
+			 process_removexattr,
 			 };
 
 unsigned int highest_funcid = sizeof(func_arr)/sizeof(func_arr[0]);
@@ -190,13 +203,91 @@ static void fail(const char *msg)
 }
 #endif
 
+struct xattr_node_s;
+typedef struct xattr_node_s {
+  struct xattr_node_s *next;
+  char                *key;
+  char                *value;
+  size_t              value_size;
+} xattr_node_t;
+
 struct data_node_s;
 typedef struct data_node_s {
   struct data_node_s *next;
   struct fakestat     buf;
   uint32_t            remote;
+  xattr_node_t       *xattr;
 } data_node_t;
 
+static xattr_node_t *xattr_find(xattr_node_t *node, char *key)
+{
+  while (node) {
+    if (node->key && (!strcmp(node->key, key)))
+      break;
+    node = node->next;
+  }
+  return node;
+}
+
+static int xattr_erase(xattr_node_t **head, char *key)
+{
+  xattr_node_t *cur_node, *prev_node = NULL;
+
+  for (cur_node = *head; cur_node; prev_node = cur_node, cur_node = cur_node->next) {
+    if (cur_node->key && (!strcmp(cur_node->key, key))) {
+      if (prev_node == NULL) {
+        *head = cur_node->next;
+      } else {
+        prev_node->next = cur_node->next;
+      }
+      free(cur_node->key);
+      if (cur_node->value)
+        free(cur_node->value);
+      free(cur_node);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int xattr_clear(xattr_node_t **head)
+{
+  xattr_node_t *cur_node, *next_node;
+  cur_node = *head;
+  *head = NULL;
+  while (cur_node) {
+    next_node = cur_node->next;
+    if (cur_node->key)
+      free(cur_node->key);
+    if (cur_node->value)
+      free(cur_node->value);
+    free(cur_node);
+    cur_node = next_node;
+  }
+  return 0;
+}
+
+static xattr_node_t *xattr_insert(xattr_node_t **head)
+{
+  xattr_node_t *new_node;
+  new_node = calloc(1, sizeof(xattr_node_t));
+  new_node->next = *head;
+  *head = new_node;
+  return new_node;
+}
+
+static void xattr_fill(xattr_node_t *node, char *key, char *value, size_t value_size)
+{
+  if (node->key)
+    free(node->key);
+  node->key = strdup(key);
+  if (node->value)
+    free(node->value);
+  node->value = malloc(value_size);
+  memcpy(node->value, value, value_size);
+  node->value_size = value_size;
+}
+
 #define data_node_get(n)   ((struct fakestat *) &(n)->buf)
 
 #define HASH_TABLE_SIZE 10009
@@ -247,6 +338,7 @@ static void data_insert(const struct fakestat *buf,
   }
 
   memcpy(&n->buf, buf, sizeof (struct fakestat));
+  n->xattr = NULL;
   n->remote = (uint32_t) remote;
 }
 
@@ -266,6 +358,7 @@ static data_node_t *data_erase(data_node_t *pos)
   else
     prev->next = next;
 
+  xattr_clear(&n->xattr);
   free(n);
 
   return next;
@@ -361,6 +454,9 @@ static void faked_send_fakem(const struct fake_msg *buf)
   fm.st.mode = htonl(buf->st.mode);
   fm.st.nlink = htonl(buf->st.nlink);
   fm.remote = htonl(buf->remote);
+  fm.xattr.buffersize = htonl(buf->xattr.buffersize);
+  fm.xattr.flags_rc = htonl(buf->xattr.flags_rc);
+  memcpy(fm.xattr.buf, buf->xattr.buf, MAX_IPC_BUFFER_SIZE);
 
   while (1) {
     ssize_t len;
@@ -779,6 +875,169 @@ void process_unlink(struct fake_msg *buf){
   }
 }
 
+void process_listxattr(struct fake_msg *buf)
+{
+  data_node_t *i;
+  xattr_node_t *x = NULL;
+
+  buf->xattr.flags_rc = 0;
+  i = data_find(&buf->st, buf->remote);
+  if(debug){
+    fprintf(stderr,"FAKEROOT: process listxattr\n");
+  }
+  if (i != data_end()) {
+    x = i->xattr;
+  }
+  if (!x) {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT:    (previously unknown)\n");
+    }
+    buf->xattr.buffersize = 0;
+  } else {
+    int bsize = 0;
+    while (x) {
+      int keysize = strlen(x->key);
+      if ((bsize + keysize + 1) > MAX_IPC_BUFFER_SIZE)
+      {
+        buf->xattr.flags_rc = ERANGE;
+        break;
+      }
+      strcpy(&buf->xattr.buf[bsize], x->key);
+      bsize += keysize + 1;
+      x = x->next;
+    }
+    buf->xattr.buffersize = bsize;
+    if(debug) {
+      fprintf(stderr,"FAKEROOT: (previously known): xattr=%s\n", buf->xattr.buf);
+    }
+  }
+  faked_send_fakem(buf);
+}
+
+void process_setxattr(struct fake_msg *buf)
+{
+  data_node_t *i;
+  xattr_node_t *x = NULL;
+  xattr_node_t **x_ref = NULL;
+  xattr_node_t *new_node = NULL;
+  struct fakestat st;
+  char *value = NULL;
+  int key_size, value_size;
+  int flags = buf->xattr.flags_rc;
+
+  buf->xattr.flags_rc = 0;
+  /* Need some more bounds checking */
+  key_size = strlen(buf->xattr.buf);
+  value = &buf->xattr.buf[key_size + 1];
+  value_size = buf->xattr.buffersize - key_size - 1;
+
+  i = data_find(&buf->st, buf->remote);
+  if(debug){
+    fprintf(stderr,"FAKEROOT: process setxattr key = %s\n", buf->xattr.buf);
+  }
+  if (i == data_end()) {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT:    (previously unknown)\n");
+    }
+    st=buf->st;
+    /* We pretend that unknown files are owned
+       by root.root, so we have to maintain that pretense when the
+       caller asks to leave an id unchanged. */
+    if ((uint32_t)st.uid == (uint32_t)-1)
+       st.uid = 0;
+    if ((uint32_t)st.gid == (uint32_t)-1)
+       st.gid = 0;
+    insert_or_overwrite(&st, buf->remote);
+    i = data_find(&buf->st, buf->remote);
+  }
+  x = xattr_find(i->xattr, buf->xattr.buf);
+  if (x) {
+    if (flags == XATTR_CREATE) {
+      buf->xattr.flags_rc = EEXIST;
+      if (debug) {
+        fprintf(stderr,"FAKEROOT:    Already exists\n");
+      }
+    } else {
+      xattr_fill(x, buf->xattr.buf, value, value_size);
+      if (debug) {
+        fprintf(stderr,"FAKEROOT:    Replaced\n");
+      }
+    }
+  } else {
+    if (flags == XATTR_REPLACE) {
+      buf->xattr.flags_rc = ENODATA;
+      if (debug) {
+        fprintf(stderr,"FAKEROOT:    Replace requested but no previous entry found\n");
+      }
+    } else {
+      x = xattr_insert(&i->xattr);
+      xattr_fill(x, buf->xattr.buf, value, value_size);
+      if (debug) {
+        fprintf(stderr,"FAKEROOT:    Inserted\n");
+      }
+    }
+  }
+  buf->xattr.buffersize = 0;
+  faked_send_fakem(buf);
+}
+
+void process_getxattr(struct fake_msg *buf)
+{
+  data_node_t *i;
+  xattr_node_t *x = NULL;
+
+  buf->xattr.flags_rc = ENODATA;
+  i = data_find(&buf->st, buf->remote);
+  if(debug){
+    fprintf(stderr,"FAKEROOT: process getxattr key = %s\n", buf->xattr.buf);
+  }
+  if (i != data_end()) {
+    x = xattr_find(i->xattr, buf->xattr.buf);
+  }
+  if (!x) {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT:    (previously unknown)\n");
+    }
+    buf->xattr.buffersize = 0;
+  } else {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT: (previously known): %s\n", x->value);
+    }
+    buf->xattr.buffersize = x->value_size;
+    memcpy(buf->xattr.buf, x->value, x->value_size);
+    buf->xattr.flags_rc = 0;
+  }
+  faked_send_fakem(buf);
+}
+
+void process_removexattr(struct fake_msg *buf)
+{
+  data_node_t *i;
+  xattr_node_t *x = NULL;
+
+  buf->xattr.flags_rc = ENODATA;
+  i = data_find(&buf->st, buf->remote);
+  if(debug){
+    fprintf(stderr,"FAKEROOT: process removexattr key = %s\n", buf->xattr.buf);
+  }
+  if (i != data_end()) {
+    x = xattr_find(i->xattr, buf->xattr.buf);
+  }
+  if (!x) {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT:    (previously unknown)\n");
+    }
+  } else {
+    if (debug) {
+      fprintf(stderr,"FAKEROOT: (previously known): %s\n", x->value);
+    }
+    xattr_erase(&i->xattr, buf->xattr.buf);
+    buf->xattr.flags_rc = 0;
+  }
+  buf->xattr.buffersize = 0;
+  faked_send_fakem(buf);
+}
+
 void debugdata(int dummy UNUSED){
   int stored_errno = errno;
   data_node_t *i;
@@ -1022,6 +1281,8 @@ static int get_fakem(struct fake_msg *buf)
   buf->st.mode = ntohl(buf->st.mode);
   buf->st.nlink = ntohl(buf->st.nlink);
   buf->remote = ntohl(buf->remote);
+  buf->xattr.buffersize = ntohl(buf->xattr.buffersize);
+  buf->xattr.flags_rc = ntohl(buf->xattr.flags_rc);
 
   return 0;
 }
diff --git a/libfakeroot.c b/libfakeroot.c
index 68a95fb..79a0fca 100644
--- a/libfakeroot.c
+++ b/libfakeroot.c
@@ -64,11 +64,13 @@
 #define SEND_STAT64(a,b,c) send_stat64(a,b,c)
 #define SEND_GET_STAT(a,b) send_get_stat(a,b)
 #define SEND_GET_STAT64(a,b) send_get_stat64(a,b)
+#define SEND_GET_XATTR64(a,b,c) send_get_xattr64(a,b,c)
 #else
 #define SEND_STAT(a,b,c) send_stat(a,b)
 #define SEND_STAT64(a,b,c) send_stat64(a,b)
 #define SEND_GET_STAT(a,b) send_get_stat(a)
 #define SEND_GET_STAT64(a,b) send_get_stat64(a)
+#define SEND_GET_XATTR64(a,b,c) send_get_xattr64(a,b)
 #endif
 
 /*
@@ -82,6 +84,7 @@
 #define INT_NEXT_FSTAT(a,b) NEXT_FSTAT64(_STAT_VER,a,b)
 #define INT_NEXT_FSTATAT(a,b,c,d) NEXT_FSTATAT64(_STAT_VER,a,b,c,d)
 #define INT_SEND_STAT(a,b) SEND_STAT64(a,b,_STAT_VER)
+#define INT_SEND_GET_XATTR(a,b) SEND_GET_XATTR64(a,b,_STAT_VER)
 #else
 #define INT_STRUCT_STAT struct stat
 #define INT_NEXT_STAT(a,b) NEXT_STAT(_STAT_VER,a,b)
@@ -89,6 +92,7 @@
 #define INT_NEXT_FSTAT(a,b) NEXT_FSTAT(_STAT_VER,a,b)
 #define INT_NEXT_FSTATAT(a,b,c,d) NEXT_FSTATAT(_STAT_VER,a,b,c,d)
 #define INT_SEND_STAT(a,b) SEND_STAT(a,b,_STAT_VER)
+#define INT_SEND_GET_XATTR(a,b) SEND_GET_XATTR(a,b,_STAT_VER)
 #endif
 
 #include <stdlib.h>
@@ -106,6 +110,9 @@
 #ifdef HAVE_SYS_ACL_H
 #include <sys/acl.h>
 #endif /* HAVE_SYS_ACL_H */
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
 #if HAVE_FTS_H
 #include <fts.h>
 #endif /* HAVE_FTS_H */
@@ -1537,6 +1544,305 @@ int setgroups(SETGROUPS_SIZE_TYPE size, const gid_t *list){
     return 0;
 }
 
+int capset(cap_user_header_t hdrp, const cap_user_data_t datap)
+{
+  int rc = next_capset(hdrp, datap);
+  return (fakeroot_disabled) ? (rc) : 0;
+}
+
+static size_t common_setxattr(INT_STRUCT_STAT *st, const char *name, void * value, size_t size, int flags)
+{
+  xattr_args xattr;
+  xattr.name = name;
+  xattr.value = value;
+  xattr.size = size;
+  xattr.flags = flags;
+  xattr.func = setxattr_func;
+  INT_SEND_GET_XATTR(st, &xattr);
+  if (xattr.rc)
+  {
+    errno = xattr.rc;
+    return -1;
+  }
+  return 0;
+}
+
+static size_t common_getxattr(INT_STRUCT_STAT *st, const char *name, void * value, size_t size)
+{
+  xattr_args xattr;
+  xattr.name = name;
+  xattr.value = value;
+  xattr.size = size;
+  xattr.func = getxattr_func;
+  INT_SEND_GET_XATTR(st, &xattr);
+  if (xattr.rc)
+  {
+    errno = xattr.rc;
+    return -1;
+  }
+  return xattr.size;
+}
+
+static size_t common_listxattr(INT_STRUCT_STAT *st, char *list, size_t size)
+{
+  xattr_args xattr;
+  xattr.name = NULL;
+  xattr.value = list;
+  xattr.size = size;
+  xattr.func = listxattr_func;
+  INT_SEND_GET_XATTR(st, &xattr);
+  if (xattr.rc)
+  {
+    errno = xattr.rc;
+    return -1;
+  }
+  return xattr.size;
+}
+
+static size_t common_removexattr(INT_STRUCT_STAT *st, char *name)
+{
+  xattr_args xattr;
+  xattr.name = name;
+  xattr.value = NULL;
+  xattr.size = 0;
+  xattr.func = removexattr_func;
+  INT_SEND_GET_XATTR(st, &xattr);
+  if (xattr.rc)
+  {
+    errno = xattr.rc;
+    return -1;
+  }
+  return 0;
+}
+
+ssize_t setxattr(const char *path, const char *name, void *value, size_t size, int flags)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_setxattr(path, name, value, size, flags);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "setxattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_STAT(path, &st);
+  if(r)
+    return r;
+
+  return common_setxattr(&st, name, value, size, flags);
+}
+
+ssize_t lsetxattr(const char *path, const char *name, void *value, size_t size, int flags)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_lsetxattr(path, name, value, size, flags);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "lsetxattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_LSTAT(path, &st);
+  if(r)
+    return r;
+
+  return common_setxattr(&st, name, value, size, flags);
+}
+
+ssize_t fsetxattr(int fd, const char *name, void *value, size_t size, int flags)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_fsetxattr(fd, name, value, size, flags);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "fsetxattr fd %d\n", fd);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_FSTAT(fd, &st);
+  if(r)
+    return r;
+
+  return common_setxattr(&st, name, value, size, flags);
+}
+
+ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_getxattr(path, name, value, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "getxattr path %s name %s size %d\n", path, name, size);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_STAT(path, &st);
+  if(r)
+    return r;
+
+  return common_getxattr(&st, name, value, size);
+}
+
+ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_lgetxattr(path, name, value, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "lgetxattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_LSTAT(path, &st);
+  if(r)
+    return r;
+
+  return common_getxattr(&st, name, value, size);
+}
+
+ssize_t fgetxattr(int fd, const char *name, void *value, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_fgetxattr(fd, name, value, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "fgetxattr fd %d\n", fd);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_FSTAT(fd, &st);
+  if(r)
+    return r;
+
+  return common_getxattr(&st, name, value, size);
+}
+
+ssize_t listxattr(const char *path, char *list, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_listxattr(path, list, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "listxattr path %s size %d\n", path, size);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_STAT(path, &st);
+  if(r)
+    return r;
+
+  return common_listxattr(&st, list, size);
+}
+
+ssize_t llistxattr(const char *path, char *list, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_llistxattr(path, list, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "llistxattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_LSTAT(path, &st);
+  if(r)
+    return r;
+
+  return common_listxattr(&st, list, size);
+}
+
+ssize_t flistxattr(int fd, char *list, size_t size)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_flistxattr(fd, list, size);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "flistxattr fd %d\n", fd);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_FSTAT(fd, &st);
+  if(r)
+    return r;
+
+  return common_listxattr(&st, list, size);
+}
+
+ssize_t removexattr(const char *path, char *name)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_removexattr(path, name);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "removexattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_STAT(path, &st);
+  if(r)
+    return r;
+
+  return common_removexattr(&st, name);
+}
+
+ssize_t lremovexattr(const char *path, char *name)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_lremovexattr(path, name);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "lremovexattr path %s\n", path);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_LSTAT(path, &st);
+  if(r)
+    return r;
+
+  return common_removexattr(&st, name);
+}
+
+ssize_t fremovexattr(int fd, char *name)
+{
+  INT_STRUCT_STAT st;
+  int r;
+  if (fakeroot_disabled)
+    return next_fremovexattr(fd, name);
+
+#ifdef LIBFAKEROOT_DEBUGGING
+  if (fakeroot_debug) {
+    fprintf(stderr, "fremovexattr fd %d\n", fd);
+  }
+#endif /* LIBFAKEROOT_DEBUGGING */
+  r=INT_NEXT_FSTAT(fd, &st);
+  if(r)
+    return r;
+
+  return common_removexattr(&st, name);
+}
+
 #undef fakeroot_disabled
 
 int fakeroot_disable(int new)
diff --git a/message.h b/message.h
index a96cf90..0b64fb9 100644
--- a/message.h
+++ b/message.h
@@ -64,6 +64,20 @@ struct fakestat {
 #pragma pack()
 #endif
 
+#define MAX_IPC_BUFFER_SIZE 256
+
+#if __SUNPRO_C
+#pragma pack(4)
+#endif
+struct fakexattr {
+	uint32_t   buffersize;
+	char       buf[MAX_IPC_BUFFER_SIZE];
+	int32_t    flags_rc; /* flags from setxattr. Return code on round trip */
+} FAKEROOT_ATTR(packed);
+#if __SUNPRO_C
+#pragma pack()
+#endif
+
 #if __SUNPRO_C
 #pragma pack(4)
 #endif
@@ -77,6 +91,7 @@ struct fake_msg {
 	int serial;
 #endif
 	struct fakestat st;
+	struct fakexattr xattr;
 	uint32_t        remote;
 } FAKEROOT_ATTR(packed);
 #if __SUNPRO_C
diff --git a/test/Makefile.am b/test/Makefile.am
index c7448b0..b37d4cd 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -10,7 +10,8 @@ TESTS =						\
   t.option					\
   t.tar						\
   t.touchinstall				\
-  t.truereturn
+  t.truereturn \
+  t.xattr
 
 suffix =
 TESTS_ENVIRONMENT =				\
diff --git a/test/t.xattr b/test/t.xattr
new file mode 100755
index 0000000..a134e0a
--- /dev/null
+++ b/test/t.xattr
@@ -0,0 +1,11 @@
+#!/bin/sh
+. ./defs || exit 1
+
+mkdir $tmp
+touch $tmp/foo
+# fakeroot mangles spaces unless the getopt utility is the GNU version
+echo "setcap cap_net_raw+ep $tmp/foo; getcap $tmp/foo" >$tmp/sh
+run_fakeroot -- \
+  ${posixshell} $tmp/sh >$tmp/out
+cat $tmp/out
+grep "^$tmp/foo = cap_net_raw+ep" $tmp/out
diff --git a/wrapfunc.inp b/wrapfunc.inp
index 5629b0f..cda578a 100644
--- a/wrapfunc.inp
+++ b/wrapfunc.inp
@@ -145,6 +145,19 @@ setfsgid;gid_t;(gid_t fsgid);(fsgid)
 #endif /* HAVE_SETFSGID */
 initgroups;int;(const char *user, INITGROUPS_SECOND_ARG group);(user, group)
 setgroups;int;(SETGROUPS_SIZE_TYPE size, const gid_t *list);(size, list)
+capset;int;(cap_user_header_t hdrp, const cap_user_data_t datap);(hdrp, datap)
+listxattr; ssize_t;(const char *path, char *list, size_t size);(path, list, size)
+llistxattr; ssize_t;(const char *path, char *list, size_t size);(path, list, size)
+flistxattr; ssize_t;(int fd, char *list, size_t size);(fd, list, size)
+getxattr;ssize_t;(const char *path, const char *name, void *value, size_t size);(path, name, value, size)
+lgetxattr;ssize_t;(const char *path, const char *name, void *value, size_t size);(path, name, value, size)
+fgetxattr;ssize_t;(int fd, const char *name, void *value, size_t size);(fd, name, value, size)
+setxattr;ssize_t;(const char *path, const char *name, void *value, size_t size, int flags);(path, name, value, size, flags)
+lsetxattr;ssize_t;(const char *path, const char *name, void *value, size_t size, int flags);(path, name, value, size, flags)
+fsetxattr;ssize_t;(int fd, const char *name, void *value, size_t size, int flags);(fd, name, value, size, flags)
+removexattr;ssize_t;(const char *path, const char *name);(path, name)
+lremovexattr;ssize_t;(const char *path, const char *name);(path, name)
+fremovexattr;ssize_t;(int fd, const char *name);(fd, name)
 
 #ifdef HAVE_FSTATAT
 #ifdef HAVE_FCHMODAT
