aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2014-04-16 13:57:50 -0500
committerPeter Seebach <peter.seebach@windriver.com>2014-04-21 18:12:22 -0500
commitd4c7177bb87aad975852715c6956426b5595bb78 (patch)
tree53c8520e9510f9118a4d855c274cf5a679f25e99
parentf1d474452e24e185c4f657bba700ac6509cbd0cd (diff)
downloadpseudo-d4c7177bb87aad975852715c6956426b5595bb78.tar.gz
pseudo-d4c7177bb87aad975852715c6956426b5595bb78.tar.bz2
pseudo-d4c7177bb87aad975852715c6956426b5595bb78.zip
Initial draft xattr support
Initial, incomplete, support for extended attributes. Extended attributes are implemented fairly naively, using a second table in the file database using the primary file table's id as a foreign key. The ON DELETE CASCADE behavior requires sqlite 3.6.19 or later with foreign key and trigger support compiled in. To reduce round-trips, the client does not check for existing attributes, but rather, sends three distinct set messages; OP_SET_XATTR, OP_CREATE_XATTR, OP_REPLACE_XATTR. A SET message always succeeds, a CREATE fails if the attribute already exists, and a REPLACE fails if the attribute does not already exist. The /* flags */ feature of makewrappers is used to correct path names appropriately, so all functions are already working with complete paths, and can always use functions that work on links; if they were supposed to dereference, the path fixup code got that. The xattr support is enabled, for now, conditional on whether getfattr --help succeeds. Not yet implemented: Translation for system.posix_acl_access, which is used by "cp -a" (or "cp --preserve-all") on some systems to try to copy modes. Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
-rw-r--r--enums/debug_type.in1
-rw-r--r--enums/op.in6
-rw-r--r--ports/linux/noxattr/guts/fgetxattr.c (renamed from ports/linux/guts/fgetxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/flistxattr.c (renamed from ports/linux/guts/flistxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/fremovexattr.c (renamed from ports/linux/guts/fremovexattr.c)0
-rw-r--r--ports/linux/noxattr/guts/fsetxattr.c (renamed from ports/linux/guts/fsetxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/getxattr.c (renamed from ports/linux/guts/getxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/lgetxattr.c (renamed from ports/linux/guts/lgetxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/listxattr.c (renamed from ports/linux/guts/listxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/llistxattr.c (renamed from ports/linux/guts/llistxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/lremovexattr.c (renamed from ports/linux/guts/lremovexattr.c)0
-rw-r--r--ports/linux/noxattr/guts/lsetxattr.c (renamed from ports/linux/guts/lsetxattr.c)0
-rw-r--r--ports/linux/noxattr/guts/removexattr.c (renamed from ports/linux/guts/removexattr.c)0
-rw-r--r--ports/linux/noxattr/guts/setxattr.c (renamed from ports/linux/guts/setxattr.c)0
-rw-r--r--ports/linux/noxattr/wrapfuncs.in14
-rwxr-xr-xports/linux/subports5
-rw-r--r--ports/linux/wrapfuncs.in14
-rw-r--r--ports/linux/xattr/guts/fgetxattr.c12
-rw-r--r--ports/linux/xattr/guts/flistxattr.c12
-rw-r--r--ports/linux/xattr/guts/fremovexattr.c12
-rw-r--r--ports/linux/xattr/guts/fsetxattr.c12
-rw-r--r--ports/linux/xattr/guts/getxattr.c12
-rw-r--r--ports/linux/xattr/guts/lgetxattr.c12
-rw-r--r--ports/linux/xattr/guts/listxattr.c12
-rw-r--r--ports/linux/xattr/guts/llistxattr.c12
-rw-r--r--ports/linux/xattr/guts/lremovexattr.c12
-rw-r--r--ports/linux/xattr/guts/lsetxattr.c12
-rw-r--r--ports/linux/xattr/guts/removexattr.c12
-rw-r--r--ports/linux/xattr/guts/setxattr.c12
-rw-r--r--ports/linux/xattr/portdefs.h1
-rw-r--r--ports/linux/xattr/pseudo_wrappers.c117
-rw-r--r--ports/linux/xattr/wrapfuncs.in12
-rw-r--r--pseudo.c90
-rw-r--r--pseudo.h8
-rw-r--r--pseudo_client.c42
-rw-r--r--pseudo_db.c313
-rw-r--r--pseudo_db.h14
-rw-r--r--pseudo_server.c16
-rw-r--r--pseudo_server.h2
-rw-r--r--pseudo_util.c8
-rw-r--r--pseudo_wrappers.c10
-rw-r--r--pseudodb.c2
42 files changed, 755 insertions, 64 deletions
diff --git a/enums/debug_type.in b/enums/debug_type.in
index 4e3126e..f4de3ab 100644
--- a/enums/debug_type.in
+++ b/enums/debug_type.in
@@ -25,3 +25,4 @@ ipc, 'i', "client/server interactions"
invoke, 'k', "invocation and launching"
benchmark, 'b', "performance statistics"
verbose, 'V', "extra detail"
+xattr, 'x', "extended attributes"
diff --git a/enums/op.in b/enums/op.in
index 3b8e23e..61ee666 100644
--- a/enums/op.in
+++ b/enums/op.in
@@ -21,3 +21,9 @@ exec, 0
may-unlink, 1
did-unlink, 0
cancel-unlink, 0
+get-xattr, 1
+list-xattr, 1
+remove-xattr, 1
+set-xattr, 0
+create-xattr, 1
+replace-xattr, 1
diff --git a/ports/linux/guts/fgetxattr.c b/ports/linux/noxattr/guts/fgetxattr.c
index 9d33643..9d33643 100644
--- a/ports/linux/guts/fgetxattr.c
+++ b/ports/linux/noxattr/guts/fgetxattr.c
diff --git a/ports/linux/guts/flistxattr.c b/ports/linux/noxattr/guts/flistxattr.c
index 77db021..77db021 100644
--- a/ports/linux/guts/flistxattr.c
+++ b/ports/linux/noxattr/guts/flistxattr.c
diff --git a/ports/linux/guts/fremovexattr.c b/ports/linux/noxattr/guts/fremovexattr.c
index 529a9de..529a9de 100644
--- a/ports/linux/guts/fremovexattr.c
+++ b/ports/linux/noxattr/guts/fremovexattr.c
diff --git a/ports/linux/guts/fsetxattr.c b/ports/linux/noxattr/guts/fsetxattr.c
index 3c56ddd..3c56ddd 100644
--- a/ports/linux/guts/fsetxattr.c
+++ b/ports/linux/noxattr/guts/fsetxattr.c
diff --git a/ports/linux/guts/getxattr.c b/ports/linux/noxattr/guts/getxattr.c
index fe8912d..fe8912d 100644
--- a/ports/linux/guts/getxattr.c
+++ b/ports/linux/noxattr/guts/getxattr.c
diff --git a/ports/linux/guts/lgetxattr.c b/ports/linux/noxattr/guts/lgetxattr.c
index 404211f..404211f 100644
--- a/ports/linux/guts/lgetxattr.c
+++ b/ports/linux/noxattr/guts/lgetxattr.c
diff --git a/ports/linux/guts/listxattr.c b/ports/linux/noxattr/guts/listxattr.c
index 1b0b5e7..1b0b5e7 100644
--- a/ports/linux/guts/listxattr.c
+++ b/ports/linux/noxattr/guts/listxattr.c
diff --git a/ports/linux/guts/llistxattr.c b/ports/linux/noxattr/guts/llistxattr.c
index a33f970..a33f970 100644
--- a/ports/linux/guts/llistxattr.c
+++ b/ports/linux/noxattr/guts/llistxattr.c
diff --git a/ports/linux/guts/lremovexattr.c b/ports/linux/noxattr/guts/lremovexattr.c
index 38429da..38429da 100644
--- a/ports/linux/guts/lremovexattr.c
+++ b/ports/linux/noxattr/guts/lremovexattr.c
diff --git a/ports/linux/guts/lsetxattr.c b/ports/linux/noxattr/guts/lsetxattr.c
index 140ae8d..140ae8d 100644
--- a/ports/linux/guts/lsetxattr.c
+++ b/ports/linux/noxattr/guts/lsetxattr.c
diff --git a/ports/linux/guts/removexattr.c b/ports/linux/noxattr/guts/removexattr.c
index cd7f486..cd7f486 100644
--- a/ports/linux/guts/removexattr.c
+++ b/ports/linux/noxattr/guts/removexattr.c
diff --git a/ports/linux/guts/setxattr.c b/ports/linux/noxattr/guts/setxattr.c
index de2de98..de2de98 100644
--- a/ports/linux/guts/setxattr.c
+++ b/ports/linux/noxattr/guts/setxattr.c
diff --git a/ports/linux/noxattr/wrapfuncs.in b/ports/linux/noxattr/wrapfuncs.in
new file mode 100644
index 0000000..de22ae1
--- /dev/null
+++ b/ports/linux/noxattr/wrapfuncs.in
@@ -0,0 +1,14 @@
+# we use "pathname" to avoid canonicalizing paths, because these functions are
+# unimplemented
+ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size);
+ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size);
+ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
+ssize_t listxattr(const char *pathname, char *list, size_t size);
+ssize_t llistxattr(const char *pathname, char *list, size_t size);
+ssize_t flistxattr(int filedes, char *list, size_t size);
+int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags);
+int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags);
+int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
+int removexattr(const char *pathname, const char *name);
+int lremovexattr(const char *pathname, const char *name);
+int fremovexattr(int filedes, const char *name);
diff --git a/ports/linux/subports b/ports/linux/subports
index 02a4688..b68c25b 100755
--- a/ports/linux/subports
+++ b/ports/linux/subports
@@ -25,3 +25,8 @@ rm -f dummy.c dummy.o
if ! $found; then
echo >&2 "Can't tell, omitting clone(2) support."
fi
+if getfattr --help >/dev/null 2>&1; then
+ echo "linux/xattr"
+else
+ echo "linux/noxattr"
+fi
diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in
index 1ffdb3a..e2b661b 100644
--- a/ports/linux/wrapfuncs.in
+++ b/ports/linux/wrapfuncs.in
@@ -13,20 +13,6 @@ int fcntl(int fd, int cmd, ...{struct flock *lock});
# just so we know the inums of symlinks
char *canonicalize_file_name(const char *filename);
int eaccess(const char *path, int mode);
-# we use "pathname" to avoid canonicalizing paths, because these functions are
-# unimplemented
-ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size);
-ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size);
-ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
-ssize_t listxattr(const char *pathname, char *list, size_t size);
-ssize_t llistxattr(const char *pathname, char *list, size_t size);
-ssize_t flistxattr(int filedes, char *list, size_t size);
-int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags);
-int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags);
-int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
-int removexattr(const char *pathname, const char *name);
-int lremovexattr(const char *pathname, const char *name);
-int fremovexattr(int filedes, const char *name);
int open64(const char *path, int flags, ...{mode_t mode}); /* flags=0 */
int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=0 */
int __openat64_2(int dirfd, const char *path, int flags); /* flags=0 */
diff --git a/ports/linux/xattr/guts/fgetxattr.c b/ports/linux/xattr/guts/fgetxattr.c
new file mode 100644
index 0000000..ae8c3a3
--- /dev/null
+++ b/ports/linux/xattr/guts/fgetxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_getxattr(NULL, filedes, name, value, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/flistxattr.c b/ports/linux/xattr/guts/flistxattr.c
new file mode 100644
index 0000000..cdd9454
--- /dev/null
+++ b/ports/linux/xattr/guts/flistxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t flistxattr(int filedes, char *list, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_listxattr(NULL, filedes, list, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/fremovexattr.c b/ports/linux/xattr/guts/fremovexattr.c
new file mode 100644
index 0000000..a029d2c
--- /dev/null
+++ b/ports/linux/xattr/guts/fremovexattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int fremovexattr(int filedes, const char *name)
+ * int rc = -1;
+ */
+ rc = shared_removexattr(NULL, filedes, name);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/fsetxattr.c b/ports/linux/xattr/guts/fsetxattr.c
new file mode 100644
index 0000000..cbed2ea
--- /dev/null
+++ b/ports/linux/xattr/guts/fsetxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags)
+ * int rc = -1;
+ */
+ rc = shared_setxattr(NULL, filedes, name, value, size, flags);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/getxattr.c b/ports/linux/xattr/guts/getxattr.c
new file mode 100644
index 0000000..7bd2bf5
--- /dev/null
+++ b/ports/linux/xattr/guts/getxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_getxattr(path, -1, name, value, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/lgetxattr.c b/ports/linux/xattr/guts/lgetxattr.c
new file mode 100644
index 0000000..675d3da
--- /dev/null
+++ b/ports/linux/xattr/guts/lgetxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_getxattr(path, -1, name, value, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/listxattr.c b/ports/linux/xattr/guts/listxattr.c
new file mode 100644
index 0000000..0decf71
--- /dev/null
+++ b/ports/linux/xattr/guts/listxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t listxattr(const char *path, char *list, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_listxattr(path, -1, list, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/llistxattr.c b/ports/linux/xattr/guts/llistxattr.c
new file mode 100644
index 0000000..9934256
--- /dev/null
+++ b/ports/linux/xattr/guts/llistxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * ssize_t llistxattr(const char *path, char *list, size_t size)
+ * ssize_t rc = -1;
+ */
+ rc = shared_listxattr(path, -1, list, size);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/lremovexattr.c b/ports/linux/xattr/guts/lremovexattr.c
new file mode 100644
index 0000000..1f39788
--- /dev/null
+++ b/ports/linux/xattr/guts/lremovexattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int lremovexattr(const char *path, const char *name)
+ * int rc = -1;
+ */
+ rc = shared_removexattr(path, -1, name);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/lsetxattr.c b/ports/linux/xattr/guts/lsetxattr.c
new file mode 100644
index 0000000..b167b15
--- /dev/null
+++ b/ports/linux/xattr/guts/lsetxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags)
+ * int rc = -1;
+ */
+ rc = shared_setxattr(path, -1, name, value, size, flags);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/removexattr.c b/ports/linux/xattr/guts/removexattr.c
new file mode 100644
index 0000000..0d4d8e3
--- /dev/null
+++ b/ports/linux/xattr/guts/removexattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int removexattr(const char *path, const char *name)
+ * int rc = -1;
+ */
+ rc = shared_removexattr(path, -1, name);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/guts/setxattr.c b/ports/linux/xattr/guts/setxattr.c
new file mode 100644
index 0000000..dd85252
--- /dev/null
+++ b/ports/linux/xattr/guts/setxattr.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * int setxattr(const char *path, const char *name, const void *value, size_t size, int flags)
+ * int rc = -1;
+ */
+ rc = shared_setxattr(path, -1, name, value, size, flags);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/xattr/portdefs.h b/ports/linux/xattr/portdefs.h
new file mode 100644
index 0000000..ec030b7
--- /dev/null
+++ b/ports/linux/xattr/portdefs.h
@@ -0,0 +1 @@
+#include <attr/xattr.h>
diff --git a/ports/linux/xattr/pseudo_wrappers.c b/ports/linux/xattr/pseudo_wrappers.c
new file mode 100644
index 0000000..7fe44a6
--- /dev/null
+++ b/ports/linux/xattr/pseudo_wrappers.c
@@ -0,0 +1,117 @@
+/* shared functionality for the xattr code */
+/* Each of these functions is expecting to get an optional name, and
+ * a populated statbuf to use for sending messages to the server.
+ */
+
+#define RC_AND_BUF \
+ int rc; \
+ PSEUDO_STATBUF buf; \
+ if (path) { \
+ rc = base_lstat(path, &buf); \
+ } else { \
+ rc = base_fstat(fd, &buf); \
+ } \
+ if (rc == -1) { \
+ return rc; \
+ }
+
+static ssize_t shared_getxattr(const char *path, int fd, const char *name, void *value, size_t size) {
+ RC_AND_BUF
+
+ pseudo_debug(PDBGF_XATTR, "getxattr(%s/%d, %s)\n",
+ path ? path : "<no path>", fd, name);
+ pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name);
+ if (result->result != RESULT_SUCCEED) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ if (value) {
+ pseudo_debug(PDBGF_XATTR, "returned attributes: '%s' (%d bytes)\n",
+ result->path, result->pathlen);
+ if (size >= result->pathlen) {
+ memcpy(value, result->path, result->pathlen);
+ } else {
+ memcpy(value, result->path, size);
+ errno = ERANGE;
+ }
+ }
+ return result->pathlen;
+}
+
+static int shared_setxattr(const char *path, int fd, const char *name, const void *value, size_t size, int flags) {
+ RC_AND_BUF
+
+ char *combined;
+ size_t nlen = strlen(name);
+ size_t combined_len = nlen + size + 1;
+ combined = malloc(combined_len + 1);
+ memcpy(combined, name, nlen);
+ combined[nlen] = '\0';
+ memcpy(combined + nlen + 1, value, size);
+ combined[combined_len] = '\0';
+
+ pseudo_debug(PDBGF_XATTR, "setxattr(%s/%d, %s, %s => %s [%d])\n",
+ path ? path : "<no path>", fd, name, (char *) value, combined + nlen + 1, (int) size);
+
+ pseudo_op_t op;
+ switch (flags) {
+ case XATTR_CREATE:
+ op = OP_CREATE_XATTR;
+ break;
+ case XATTR_REPLACE:
+ op = OP_REPLACE_XATTR;
+ break;
+ default:
+ op = OP_SET_XATTR;
+ break;
+ }
+
+ pseudo_msg_t *result = pseudo_client_op(op, 0, fd, -1, path, &buf, combined, combined_len);
+
+ /* we automatically assume success */
+ if (op == OP_SET_XATTR) {
+ return 0;
+ }
+
+ /* CREATE/REPLACE operations can report failure */
+ if (!result || result->result == RESULT_FAIL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size) {
+ RC_AND_BUF
+ pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf);
+ if (result->result != RESULT_SUCCEED) {
+ pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n");
+ errno = ENOATTR;
+ return -1;
+ }
+ if (list) {
+ pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of names, starting '%.*s'\n",
+ (int) result->pathlen, (int) result->pathlen, result->path);
+ if (size >= result->pathlen) {
+ memcpy(list, result->path, result->pathlen);
+ } else {
+ memcpy(list, result->path, size);
+ errno = ERANGE;
+ }
+ }
+ return result->pathlen;
+}
+
+static int shared_removexattr(const char *path, int fd, const char *name) {
+ RC_AND_BUF
+ pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name);
+
+ if (result->result != RESULT_SUCCEED) {
+ /* docs say ENOATTR, but I don't have one */
+ errno = ENOENT;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/ports/linux/xattr/wrapfuncs.in b/ports/linux/xattr/wrapfuncs.in
new file mode 100644
index 0000000..edfc788
--- /dev/null
+++ b/ports/linux/xattr/wrapfuncs.in
@@ -0,0 +1,12 @@
+ssize_t getxattr(const char *path, const char *name, void *value, size_t size) /* flags=0 */;
+ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) /* flags=AT_SYMLINK_NOFOLLOW */;
+ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
+int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) /* flags=0 */;
+int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) /* flags=AT_SYMLINK_NOFOLLOW */;
+int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags);
+ssize_t listxattr(const char *path, char *list, size_t size) /* flags=0 */;
+ssize_t llistxattr(const char *path, char *list, size_t size) /* flags=AT_SYMLINK_NOFOLLOW */;
+ssize_t flistxattr(int filedes, char *list, size_t size);
+int removexattr(const char *path, const char *name) /* flags=0 */;
+int lremovexattr(const char *path, const char *name) /* flags=AT_SYMLINK_NOFOLLOW */;
+int fremovexattr(int filedes, const char *name);
diff --git a/pseudo.c b/pseudo.c
index cc19b9c..bb8c60b 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -51,7 +51,7 @@ long opt_p = 0;
char *opt_r = NULL;
int opt_S = 0;
-static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag);
+static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len);
static int pseudo_db_check(int fix);
void
@@ -318,7 +318,7 @@ main(int argc, char *argv[]) {
pseudo_diag("Couldn't allocate data structure for path.\n");
exit(EXIT_FAILURE);
}
- if (pdb_find_file_path(msg)) {
+ if (pdb_find_file_path(msg, NULL)) {
pseudo_diag("Couldn't find a database entry for '%s'.\n", opt_i);
exit(EXIT_FAILURE);
}
@@ -485,14 +485,17 @@ main(int argc, char *argv[]) {
* sanity checks, then implements the fairly small DB changes required.
*/
int
-pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
+pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) {
pseudo_msg_t msg_header;
pseudo_msg_t by_path = { .op = 0 }, by_ino = { .op = 0 };
+ long long row = -1;
pseudo_msg_t db_header;
char *path_by_ino = 0;
char *oldpath = 0;
+ size_t oldpathlen = 0;
int found_path = 0, found_ino = 0;
int prefer_ino = 0;
+ int xattr_flags = 0;
if (!msg)
return 1;
@@ -520,11 +523,23 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
* stuff into a rename, break them apart (null seperated)
*/
- if (msg->pathlen && msg->op == OP_RENAME) {
- /* In a rename there are two paths, null seperate in msg->path */
- oldpath = msg->path + strlen(msg->path) + 1;
- pseudo_debug(PDBGF_OP | PDBGF_FILE, "rename: path %s, oldpath %s\n",
- msg->path, oldpath);
+ if (msg->pathlen) {
+ switch (msg->op) {
+ case OP_RENAME:
+ case OP_CREATE_XATTR:
+ case OP_GET_XATTR:
+ case OP_LIST_XATTR:
+ case OP_REPLACE_XATTR:
+ case OP_SET_XATTR:
+ /* In a rename there are two paths, null separated in msg->path */
+ oldpath = msg->path + strlen(msg->path) + 1;
+ oldpathlen = msg->pathlen - (oldpath - msg->path);
+ pseudo_debug(PDBGF_OP | PDBGF_FILE | PDBGF_XATTR, "%s: path '%s', oldpath '%s' [%d]\n",
+ pseudo_op_name(msg->op), msg->path, oldpath, (int) oldpathlen);
+ break;
+ default:
+ break;
+ }
}
/* stash original header, in case we need it later */
@@ -537,7 +552,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
/* Lookup the full path, with inode and dev if available */
if (msg->pathlen && msg->dev && msg->ino) {
- if (!pdb_find_file_exact(msg)) {
+ if (!pdb_find_file_exact(msg, &row)) {
/* restore header contents */
by_path = *msg;
by_ino = *msg;
@@ -553,7 +568,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
if (msg->pathlen) {
/* for now, don't canonicalize paths anymore */
/* used to do it here, but now doing it in client */
- if (!pdb_find_file_path(msg)) {
+ if (!pdb_find_file_path(msg, &row)) {
by_path = *msg;
found_path = 1;
} else {
@@ -564,7 +579,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
}
/* search on original inode -- in case of mismatch */
if (msg->dev && msg->ino) {
- if (!pdb_find_file_dev(&by_ino)) {
+ if (!pdb_find_file_dev(&by_ino, &row)) {
found_ino = 1;
path_by_ino = pdb_get_file_path(&by_ino);
}
@@ -760,7 +775,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
pdb_unlink_file_dev(&by_ino);
}
if (!found_path) {
- pdb_link_file(msg);
+ pdb_link_file(msg, NULL);
} else {
/* again, an error, but leaving it alone for now. */
pseudo_diag("creat ignored for existing file '%s'.\n",
@@ -790,7 +805,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
/* if the path is not known, link it */
if (!found_path) {
pseudo_debug(PDBGF_FILE, "(new) ");
- pdb_link_file(msg);
+ pdb_link_file(msg, NULL);
}
break;
case OP_CHOWN: /* FALLTHROUGH */
@@ -816,7 +831,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
}
/* if the path is not known, link it */
if (!found_path) {
- pdb_link_file(msg);
+ pdb_link_file(msg, NULL);
}
break;
case OP_STAT: /* FALLTHROUGH */
@@ -871,7 +886,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
} else {
*msg = msg_header;
}
- pdb_link_file(msg);
+ pdb_link_file(msg, NULL);
break;
case OP_RENAME:
/* a rename implies renaming an existing entry... and every
@@ -933,7 +948,46 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
pdb_unlink_file_dev(&by_ino);
}
*msg = msg_header;
- pdb_link_file(msg);
+ pdb_link_file(msg, NULL);
+ break;
+ case OP_GET_XATTR:
+ if (pdb_get_xattr(row, &oldpath, &oldpathlen)) {
+ msg->result = RESULT_FAIL;
+ } else {
+ *response_path = oldpath;
+ *response_len = oldpathlen;
+ pseudo_debug(PDBGF_XATTR, "get results: '%s' (%d bytes)\n",
+ *response_path, (int) *response_len);
+ }
+ break;
+ case OP_LIST_XATTR:
+ if (pdb_list_xattr(row, &oldpath, &oldpathlen)) {
+ msg->result = RESULT_FAIL;
+ } else {
+ pseudo_debug(PDBGF_XATTR, "got %d bytes of xattrs to list: %.*s\n", (int) oldpathlen, (int) oldpathlen, oldpath);
+ *response_path = oldpath;
+ *response_len = oldpathlen;
+ }
+ break;
+ case OP_CREATE_XATTR:
+ case OP_REPLACE_XATTR: /* fallthrough */
+ if (msg->op == OP_CREATE_XATTR) {
+ xattr_flags = XATTR_CREATE;
+ }
+ if (msg->op == OP_REPLACE_XATTR) {
+ xattr_flags = XATTR_REPLACE;
+ }
+ case OP_SET_XATTR:
+ /* we need a row entry to store xattr info */
+ if (row == -1) {
+ pdb_link_file(msg, &row);
+ }
+ if (pdb_set_xattr(row, oldpath, oldpathlen, xattr_flags)) {
+ msg->result = RESULT_FAIL;
+ }
+ break;
+ case OP_REMOVE_XATTR:
+ pdb_remove_xattr(row, oldpath, oldpathlen);
break;
default:
pseudo_diag("unknown op from client %d, op %d [%s]\n",
@@ -956,7 +1010,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) {
/* SHUTDOWN does not get this far, it's handled in pseudo_server.c */
int
-pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag) {
+pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) {
switch (msg->type) {
case PSEUDO_MSG_PING:
msg->result = RESULT_SUCCEED;
@@ -966,7 +1020,7 @@ pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag)
break;
case PSEUDO_MSG_OP:
case PSEUDO_MSG_FASTOP:
- return pseudo_op(msg, program, tag);
+ return pseudo_op(msg, program, tag, response_path, response_len);
break;
case PSEUDO_MSG_ACK: /* FALLTHROUGH */
case PSEUDO_MSG_NAK: /* FALLTHROUGH */
diff --git a/pseudo.h b/pseudo.h
index 1a0b257..ad78686 100644
--- a/pseudo.h
+++ b/pseudo.h
@@ -134,4 +134,12 @@ extern char *pseudo_version;
*/
#define PSEUDO_LINK_SYMLINK_BEHAVIOR 0
+/* given n, pick a multiple of block enough bigger than n
+ * to give us some breathing room.
+ */
+static inline size_t
+round_up(size_t n, size_t block) {
+ return block * (((n + block / 4) / block) + 1);
+}
+
#include "pseudo_ports.h"
diff --git a/pseudo_client.c b/pseudo_client.c
index 2134c8e..40985e6 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -1056,6 +1056,7 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
size_t pathlen = -1;
int do_request = 0;
char *oldpath = 0;
+ size_t oldpathlen = 0;
char *alloced_path = 0;
/* disable wrappers */
@@ -1073,6 +1074,11 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
pseudo_magic();
return 0;
}
+ /* we have to calculate this here, because SET_XATTR
+ * and friends will be using oldpath to hold a hunk of
+ * data of arbitrary length
+ */
+ oldpathlen = strlen(oldpath);
if (!path) {
pseudo_diag("rename (%s) without new path.\n",
path ? path : "<nil>");
@@ -1081,6 +1087,28 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
}
}
+ /* we treat the "create" and "replace" flags as logically
+ * distinct operations, because they can fail when set can't.
+ */
+ if (op == OP_SET_XATTR || op == OP_CREATE_XATTR || op == OP_REPLACE_XATTR) {
+ va_list ap;
+ va_start(ap, buf);
+ oldpath = va_arg(ap, char *);
+ oldpathlen = va_arg(ap, size_t);
+ pseudo_debug(PDBGF_XATTR, "setxattr, oldpath (%d bytes): '%s'\n",
+ (int) oldpathlen, oldpath);
+ va_end(ap);
+ }
+ if (op == OP_GET_XATTR){
+ va_list ap;
+ va_start(ap, buf);
+ oldpath = va_arg(ap, char *);
+ oldpathlen = strlen(oldpath);
+ pseudo_debug(PDBGF_XATTR, "getxattr, oldpath (%d bytes): '%s'\n",
+ (int) oldpathlen, oldpath);
+ va_end(ap);
+ }
+
if (path) {
/* path fixup has to happen in the specific functions,
* because they may have to make calls which have to be
@@ -1093,16 +1121,18 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
pathlen = strlen(path) + 1;
int strip_slash = (pathlen > 2 && (path[pathlen - 2]) == '/');
if (oldpath) {
- size_t full_len = strlen(oldpath) + 1 + pathlen;
+ size_t full_len = oldpathlen + 1 + pathlen;
+ size_t partial_len = pathlen - 1 - strip_slash;
char *both_paths = malloc(full_len);
if (!both_paths) {
pseudo_diag("Can't allocate space for paths for a rename operation. Sorry.\n");
pseudo_magic();
return 0;
}
- snprintf(both_paths, full_len, "%.*s%c%s",
- (int) (pathlen - 1 - strip_slash),
- path, 0, oldpath);
+ memcpy(both_paths, path, partial_len);
+ both_paths[partial_len] = '\0';
+ memcpy(both_paths + partial_len + 1, oldpath, oldpathlen);
+ both_paths[full_len - 1] = '\0';
pseudo_debug(PDBGF_PATH | PDBGF_FILE, "rename: %s -> %s [%d]\n",
both_paths + pathlen, both_paths, (int) full_len);
alloced_path = both_paths;
@@ -1239,6 +1269,10 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
case OP_DID_UNLINK:
case OP_CANCEL_UNLINK:
case OP_MAY_UNLINK:
+ case OP_GET_XATTR:
+ case OP_LIST_XATTR:
+ case OP_SET_XATTR:
+ case OP_REMOVE_XATTR:
do_request = 1;
break;
default:
diff --git a/pseudo_db.c b/pseudo_db.c
index 0d6f5ca..2faeb10 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -85,6 +85,13 @@ static struct sql_table {
"rdev INTEGER",
NULL,
NULL },
+ { "xattrs",
+ "id INTEGER PRIMARY KEY, "
+ "file_id INTEGER REFERENCES files(id) ON DELETE CASCADE, "
+ "name VARCHAR, "
+ "value VARCHAR",
+ NULL,
+ NULL },
{ NULL, NULL, NULL, NULL },
}, log_tables[] = {
{ "logs",
@@ -114,6 +121,7 @@ static struct sql_index {
/* { "files__path", "files", "path" }, */
{ "files__path_dev_ino", "files", "path, dev, ino" },
{ "files__dev_ino", "files", "dev, ino" },
+ { "xattrs__file", "xattrs", "file_id" },
{ NULL, NULL, NULL },
}, log_indexes[] = {
{ NULL, NULL, NULL },
@@ -136,6 +144,7 @@ static char *file_pragmas[] = {
* need.
*/
"PRAGMA synchronous = OFF;",
+ "PRAGMA foreign_keys = ON;",
NULL
};
@@ -365,6 +374,7 @@ make_tables(sqlite3 *db,
for (i = 0; sql_tables[i].name; ++i) {
found = 0;
+ printf("considering table %s\n", sql_tables[i].name);
for (j = 1; j <= rows; ++j) {
if (!strcmp(existing[j], sql_tables[i].name)) {
found = 1;
@@ -587,6 +597,8 @@ get_db(struct database_info *dbinfo) {
if (dbinfo->pragmas) {
for (i = 0; dbinfo->pragmas[i]; ++i) {
rc = sqlite3_exec(db, dbinfo->pragmas[i], NULL, NULL, &errmsg);
+ pseudo_debug(PDBGF_SQL | PDBGF_VERBOSE, "executed pragma: '%s', rc %d.\n",
+ dbinfo->pragmas[i], rc);
if (rc) {
dberr(db, dbinfo->pragmas[i]);
}
@@ -1356,7 +1368,7 @@ log_entry_free(log_entry *e) {
* or 'NAMELESS FILE'.
*/
int
-pdb_link_file(pseudo_msg_t *msg) {
+pdb_link_file(pseudo_msg_t *msg, long long *row) {
static sqlite3_stmt *insert;
int rc;
char *sql = "INSERT INTO files "
@@ -1397,6 +1409,10 @@ pdb_link_file(pseudo_msg_t *msg) {
if (rc != SQLITE_DONE) {
dberr(file_db, "insert may have failed (rc %d)", rc);
}
+ /* some users care what the row ID is */
+ if (row) {
+ *row = sqlite3_last_insert_rowid(file_db);
+ }
sqlite3_reset(insert);
sqlite3_clear_bindings(insert);
return rc != SQLITE_DONE;
@@ -1933,7 +1949,7 @@ pdb_update_file(pseudo_msg_t *msg) {
/* find file using both path AND dev/inode as key */
int
-pdb_find_file_exact(pseudo_msg_t *msg) {
+pdb_find_file_exact(pseudo_msg_t *msg, long long *row) {
static sqlite3_stmt *select;
int rc;
char *sql = "SELECT * FROM files WHERE path = ? AND dev = ? AND ino = ?;";
@@ -1961,6 +1977,9 @@ pdb_find_file_exact(pseudo_msg_t *msg) {
rc = sqlite3_step(select);
switch (rc) {
case SQLITE_ROW:
+ if (row) {
+ *row = sqlite3_column_int64(select, 0);
+ }
msg->uid = (unsigned long) sqlite3_column_int64(select, 4);
msg->gid = (unsigned long) sqlite3_column_int64(select, 5);
msg->mode = (unsigned long) sqlite3_column_int64(select, 6);
@@ -1984,7 +2003,7 @@ pdb_find_file_exact(pseudo_msg_t *msg) {
/* find file using path as a key */
int
-pdb_find_file_path(pseudo_msg_t *msg) {
+pdb_find_file_path(pseudo_msg_t *msg, long long *row) {
static sqlite3_stmt *select;
int rc;
char *sql = "SELECT * FROM files WHERE path = ?;";
@@ -2015,6 +2034,9 @@ pdb_find_file_path(pseudo_msg_t *msg) {
rc = sqlite3_step(select);
switch (rc) {
case SQLITE_ROW:
+ if (row) {
+ *row = sqlite3_column_int64(select, 0);
+ }
msg->dev = sqlite3_column_int64(select, 2);
msg->ino = sqlite3_column_int64(select, 3);
msg->uid = sqlite3_column_int64(select, 4);
@@ -2090,7 +2112,7 @@ pdb_get_file_path(pseudo_msg_t *msg) {
/* find file using dev/inode as key */
int
-pdb_find_file_dev(pseudo_msg_t *msg) {
+pdb_find_file_dev(pseudo_msg_t *msg, long long *row) {
static sqlite3_stmt *select;
int rc;
char *sql = "SELECT * FROM files WHERE dev = ? AND ino = ?;";
@@ -2114,6 +2136,9 @@ pdb_find_file_dev(pseudo_msg_t *msg) {
rc = sqlite3_step(select);
switch (rc) {
case SQLITE_ROW:
+ if (row) {
+ *row = sqlite3_column_int64(select, 0);
+ }
msg->uid = (unsigned long) sqlite3_column_int64(select, 4);
msg->gid = (unsigned long) sqlite3_column_int64(select, 5);
msg->mode = (unsigned long) sqlite3_column_int64(select, 6);
@@ -2135,11 +2160,286 @@ pdb_find_file_dev(pseudo_msg_t *msg) {
return rc;
}
+int
+pdb_get_xattr(long long file_id, char **value, size_t *len) {
+ static sqlite3_stmt *select;
+ int rc;
+ char *response;
+ size_t length;
+ char *sql = "SELECT value FROM xattrs WHERE file_id = ? AND name = ?;";
+
+ if (!file_db && get_dbs()) {
+ pseudo_diag("%s: database error.\n", __func__);
+ return 0;
+ }
+ if (!select) {
+ rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare SELECT statement");
+ return 1;
+ }
+ }
+ pseudo_debug(PDBGF_XATTR, "requested xattr named '%s' for file %lld\n", *value, file_id);
+ sqlite3_bind_int(select, 1, file_id);
+ rc = sqlite3_bind_text(select, 2, *value, -1, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr name to SELECT.");
+ return 1;
+ }
+ rc = sqlite3_step(select);
+ switch (rc) {
+ case SQLITE_ROW:
+ response = (char *) sqlite3_column_text(select, 0);
+ length = sqlite3_column_bytes(select, 0);
+ pseudo_debug(PDBGF_XATTR, "got %d-byte results: '%s'\n",
+ (int) length, response);
+ if (response && length >= 1) {
+ /* not a strdup because the values can contain
+ * arbitrary bytes.
+ */
+ *value = malloc(length);
+ memcpy(*value, response, length);
+ *len = length;
+ rc = 0;
+ } else {
+ *value = NULL;
+ *len = 0;
+ rc = 1;
+ }
+ break;
+ case SQLITE_DONE:
+ pseudo_debug(PDBGF_DB, "find_exact: sqlite_done on first row\n");
+ rc = 1;
+ break;
+ default:
+ dberr(file_db, "find_exact: select returned neither a row nor done");
+ rc = 1;
+ break;
+ }
+ sqlite3_reset(select);
+ sqlite3_clear_bindings(select);
+ return rc;
+}
+
+int
+pdb_list_xattr(long long file_id, char **value, size_t *len) {
+ static sqlite3_stmt *select;
+ size_t allocated = 0;
+ size_t used = 0;
+ char *buffer = 0;
+ int rc;
+ char *sql = "SELECT name FROM xattrs WHERE file_id = ?;";
+
+ /* if we don't have a record of the file, it must not have
+ * any extended attributes...
+ */
+ if (file_id == -1) {
+ *value = NULL;
+ *len = 0;
+ return 0;
+ }
+
+ if (!file_db && get_dbs()) {
+ pseudo_diag("%s: database error.\n", __func__);
+ return 0;
+ }
+ if (!select) {
+ rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare SELECT statement");
+ return 1;
+ }
+ }
+ sqlite3_bind_int(select, 1, file_id);
+ do {
+ rc = sqlite3_step(select);
+ if (rc == SQLITE_ROW) {
+ char *value = (char *) sqlite3_column_text(select, 0);
+ size_t len = sqlite3_column_bytes(select, 0);
+ if (!buffer) {
+ allocated = round_up(len, 256);
+ buffer = malloc(allocated);
+ }
+ if (used + len + 2 > allocated) {
+ size_t new_allocated = round_up(used + len + 2, 256);
+ char *new_buffer = malloc(new_allocated);
+ memcpy(new_buffer, buffer, used);
+ free(buffer);
+ allocated = new_allocated;
+ buffer = new_buffer;
+ }
+ memcpy(buffer + used, value, len);
+ buffer[used + len] = '\0';
+ used = used + len + 1;
+ } else if (rc == SQLITE_DONE) {
+ *value = buffer;
+ *len = used;
+ } else {
+ dberr(file_db, "non-row response from select?");
+ free(buffer);
+ *value = NULL;
+ *len = 0;
+ }
+ } while (rc == SQLITE_ROW);
+ sqlite3_reset(select);
+ sqlite3_clear_bindings(select);
+ return rc != SQLITE_DONE;
+}
+
+int
+pdb_remove_xattr(long long file_id, char *value, size_t len) {
+ static sqlite3_stmt *delete;
+ int rc;
+ char *sql = "DELETE FROM xattrs WHERE file_id = ? AND name = ?;";
+
+ if (!file_db && get_dbs()) {
+ pseudo_diag("%s: database error.\n", __func__);
+ return 0;
+ }
+ if (!delete) {
+ rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &delete, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare DELETE statement");
+ return 1;
+ }
+ }
+ sqlite3_bind_int(delete, 1, file_id);
+ rc = sqlite3_bind_text(delete, 2, value, len, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr name to DELETE.");
+ return 1;
+ }
+ file_db_dirty = 1;
+ rc = sqlite3_step(delete);
+ if (rc != SQLITE_DONE) {
+ dberr(file_db, "delete xattr may have failed");
+ }
+ sqlite3_reset(delete);
+ sqlite3_clear_bindings(delete);
+ return rc != SQLITE_DONE;
+}
+
+int
+pdb_set_xattr(long long file_id, char *value, size_t len, int flags) {
+ static sqlite3_stmt *select, *update, *insert;
+ int rc;
+ long long existing_row = -1;
+ char *select_sql = "SELECT id FROM xattrs WHERE file_id = ? AND name = ?;";
+ char *insert_sql = "INSERT INTO xattrs "
+ " ( file_id, name, value ) "
+ " VALUES (?, ?, ?);";
+ char *update_sql = "UPDATE xattrs SET value = ? WHERE id = ?;";
+ char *vname = value;
+ size_t vlen;
+
+ if (!file_db && get_dbs()) {
+ pseudo_diag("%s: database error.\n", __func__);
+ return 0;
+ }
+ if (!select) {
+ rc = sqlite3_prepare_v2(file_db, select_sql, strlen(select_sql), &select, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare SELECT statement");
+ return 1;
+ }
+ }
+ sqlite3_bind_int(select, 1, file_id);
+ rc = sqlite3_bind_text(select, 2, value, -1, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr name to SELECT.");
+ return 1;
+ }
+ rc = sqlite3_step(select);
+ switch (rc) {
+ case SQLITE_ROW:
+ existing_row = sqlite3_column_int64(select, 0);
+ break;
+ case SQLITE_DONE:
+ pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_exact: sqlite_done on first row\n");
+ existing_row = -1;
+ break;
+ default:
+ dberr(file_db, "set_xattr: select returned neither a row nor done");
+ rc = 1;
+ break;
+ }
+ sqlite3_reset(select);
+ sqlite3_clear_bindings(select);
+ if (flags == XATTR_CREATE && existing_row != -1) {
+ pseudo_debug(PDBGF_DB, "XATTR_CREATE with an existing row, failing.");
+ return 1;
+ }
+ if (flags == XATTR_REPLACE && existing_row == -1) {
+ pseudo_debug(PDBGF_DB, "XATTR_REPLACE with no existing row, failing.");
+ return 1;
+ }
+ /* the material after the name is the value */
+ vlen = strlen(value);
+ len = len - (vlen + 1);
+ value = value + len;
+ pseudo_debug(PDBGF_XATTR, "trying to set a value for %lld: name is '%s', value is '%s'. Existing row %lld.\n",
+ file_id, vname, value, existing_row);
+ if (existing_row != -1) {
+ /* update */
+ if (!update) {
+ rc = sqlite3_prepare_v2(file_db, update_sql, strlen(update_sql), &update, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare UPDATE statement");
+ return 1;
+ }
+ }
+ rc = sqlite3_bind_text(update, 1, value, -1, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr value to UPDATE.");
+ return 1;
+ }
+ sqlite3_bind_int(update, 2, existing_row);
+ file_db_dirty = 1;
+ rc = sqlite3_step(update);
+ if (rc != SQLITE_DONE) {
+ dberr(file_db, "update xattr may have failed");
+ }
+ sqlite3_reset(update);
+ sqlite3_clear_bindings(update);
+ return rc != SQLITE_DONE;
+ } else {
+ /* insert */
+ if (!insert) {
+ rc = sqlite3_prepare_v2(file_db, insert_sql, strlen(insert_sql), &insert, NULL);
+ if (rc) {
+ dberr(file_db, "couldn't prepare INSERT statement");
+ return 1;
+ }
+ }
+ pseudo_debug(PDBGF_XATTR, "insert should be getting ID %lld\n", file_id);
+ sqlite3_bind_int64(insert, 1, file_id);
+ rc = sqlite3_bind_text(insert, 2, vname, -1, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr name to INSERT statement");
+ return 1;
+ }
+ rc = sqlite3_bind_text(insert, 3, value, vlen, SQLITE_STATIC);
+ if (rc) {
+ dberr(file_db, "couldn't bind xattr value to INSERT statement");
+ return 1;
+ }
+ file_db_dirty = 1;
+ rc = sqlite3_step(insert);
+ if (rc != SQLITE_DONE) {
+ dberr(file_db, "insert xattr may have failed");
+ }
+ sqlite3_reset(insert);
+ sqlite3_clear_bindings(insert);
+ return rc != SQLITE_DONE;
+ }
+ return rc;
+}
+
/* find file using only inode as key. Unused for now, planned to come
* in for NFS usage.
*/
int
-pdb_find_file_ino(pseudo_msg_t *msg) {
+pdb_find_file_ino(pseudo_msg_t *msg, long long *row) {
static sqlite3_stmt *select;
int rc;
char *sql = "SELECT * FROM files WHERE ino = ?;";
@@ -2162,6 +2462,9 @@ pdb_find_file_ino(pseudo_msg_t *msg) {
rc = sqlite3_step(select);
switch (rc) {
case SQLITE_ROW:
+ if (row) {
+ *row = sqlite3_column_int64(select, 0);
+ }
msg->dev = (unsigned long) sqlite3_column_int64(select, 2);
msg->uid = (unsigned long) sqlite3_column_int64(select, 4);
msg->gid = (unsigned long) sqlite3_column_int64(select, 5);
diff --git a/pseudo_db.h b/pseudo_db.h
index 9e0382a..a8d9676 100644
--- a/pseudo_db.h
+++ b/pseudo_db.h
@@ -41,7 +41,7 @@ extern int pdb_maybe_backup(void);
extern int pdb_cancel_unlink_file(pseudo_msg_t *msg);
extern int pdb_did_unlink_file(char *path, int deleting);
extern int pdb_did_unlink_files(int deleting);
-extern int pdb_link_file(pseudo_msg_t *msg);
+extern int pdb_link_file(pseudo_msg_t *msg, long long *row);
extern int pdb_may_unlink_file(pseudo_msg_t *msg, int deleting);
extern int pdb_unlink_file(pseudo_msg_t *msg);
extern int pdb_unlink_file_dev(pseudo_msg_t *msg);
@@ -51,11 +51,15 @@ extern int pdb_update_inode(pseudo_msg_t *msg);
extern int pdb_unlink_contents(pseudo_msg_t *msg);
extern int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg);
extern int pdb_renumber_all(dev_t from, dev_t to);
-extern int pdb_find_file_exact(pseudo_msg_t *msg);
-extern int pdb_find_file_path(pseudo_msg_t *msg);
-extern int pdb_find_file_dev(pseudo_msg_t *msg);
-extern int pdb_find_file_ino(pseudo_msg_t *msg);
+extern int pdb_find_file_exact(pseudo_msg_t *msg, long long *row);
+extern int pdb_find_file_path(pseudo_msg_t *msg, long long *row);
+extern int pdb_find_file_dev(pseudo_msg_t *msg, long long *row);
+extern int pdb_find_file_ino(pseudo_msg_t *msg, long long *row);
extern char *pdb_get_file_path(pseudo_msg_t *msg);
+extern int pdb_get_xattr(long long file_id, char **value, size_t *len);
+extern int pdb_list_xattr(long long file_id, char **value, size_t *len);
+extern int pdb_remove_xattr(long long file_id, char *value, size_t len);
+extern int pdb_set_xattr(long long file_id, char *value, size_t len, int flags);
struct log_history;
typedef struct log_history *log_history;
diff --git a/pseudo_server.c b/pseudo_server.c
index 822df2b..a7600af 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -1,5 +1,6 @@
/*
* pseudo_server.c, pseudo's server-side logic and message handling
+
*
* Copyright (c) 2008-2010, 2013 Wind River Systems, Inc.
*
@@ -268,6 +269,7 @@ serve_client(int i) {
in = pseudo_msg_receive(clients[i].fd);
if (in) {
char *response_path = 0;
+ size_t response_pathlen;
int send_response = 1;
pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "got a message (%d): %s\n", in->type, (in->pathlen ? in->path : "<no path>"));
/* handle incoming ping */
@@ -306,16 +308,22 @@ serve_client(int i) {
if (in->type != PSEUDO_MSG_SHUTDOWN) {
if (in->type == PSEUDO_MSG_FASTOP)
send_response = 0;
- if (pseudo_server_response(in, clients[i].program, clients[i].tag)) {
+ /* most messages don't need these, but xattr may */
+ response_path = 0;
+ response_pathlen = -1;
+ if (pseudo_server_response(in, clients[i].program, clients[i].tag, &response_path, &response_pathlen)) {
in->type = PSEUDO_MSG_NAK;
} else {
in->type = PSEUDO_MSG_ACK;
pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "response: %d (%s)\n",
in->result, pseudo_res_name(in->result));
}
- /* no path in response */
- in->pathlen = 0;
in->client = i;
+ if (response_path) {
+ in->pathlen = response_pathlen;
+ } else {
+ in->pathlen = 0;
+ }
} else {
/* the server's listen fd is "a client", and
* so is the program connecting to request a shutdown.
@@ -347,7 +355,7 @@ serve_client(int i) {
}
}
if (send_response) {
- if ((rc = pseudo_msg_send(clients[i].fd, in, -1, response_path)) != 0) {
+ if ((rc = pseudo_msg_send(clients[i].fd, in, in->pathlen, response_path)) != 0) {
pseudo_debug(PDBGF_SERVER, "failed to send response to client %d [%d]: %d (%s)\n",
i, (int) clients[i].pid, rc, strerror(errno));
}
diff --git a/pseudo_server.h b/pseudo_server.h
index 5c595ea..06598e7 100644
--- a/pseudo_server.h
+++ b/pseudo_server.h
@@ -18,6 +18,6 @@
*
*/
extern int pseudo_server_start(int);
-extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag);
+extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len);
extern int pseudo_server_timeout;
extern int opt_l;
diff --git a/pseudo_util.c b/pseudo_util.c
index 277b058..a4a14b4 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -421,14 +421,6 @@ pseudo_diag(char *fmt, ...) {
return wrote;
}
-/* given n, pick a multiple of block enough bigger than n
- * to give us some breathing room.
- */
-static inline size_t
-round_up(size_t n, size_t block) {
- return block * (((n + block / 4) / block) + 1);
-}
-
/* store pid in text form for prepending to messages */
void
pseudo_new_pid() {
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index a594d2e..4a40fed 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -48,6 +48,9 @@
#include "pseudo_ipc.h"
#include "pseudo_client.h"
+/* Types and declarations we need in advance. */
+#include "pseudo_wrapper_table.c"
+
static void pseudo_enosys(const char *);
static int pseudo_check_wrappers(void);
static volatile int antimagic = 0;
@@ -62,10 +65,6 @@ static void pseudo_sigblock(sigset_t *);
extern char *program_invocation_short_name;
static sigset_t pseudo_saved_sigmask;
-/* the generated code goes here */
-#include "pseudo_wrapper_table.c"
-#include "pseudo_wrapfuncs.c"
-
/* Constructor only exists in libpseudo */
static void _libpseudo_init(void) __attribute__ ((constructor));
@@ -252,4 +251,7 @@ pseudo_check_wrappers(void) {
return _libpseudo_initted;
}
+/* the generated code goes here */
#include "port_wrappers.c"
+#include "pseudo_wrapfuncs.c"
+
diff --git a/pseudodb.c b/pseudodb.c
index 9203648..1be8651 100644
--- a/pseudodb.c
+++ b/pseudodb.c
@@ -38,7 +38,7 @@ main(int argc, char **argv) {
exit(1);
}
msg = pseudo_msg_new(0, argv[1]);
- rc = pdb_find_file_path(msg);
+ rc = pdb_find_file_path(msg, NULL);
if (rc) {
printf("error.\n");
return 1;