summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2015-05-27 18:29:57 +0200
committerDavid Sterba <dsterba@suse.cz>2015-05-27 18:49:59 +0200
commit123a2a085027eee7212a0c13921f574d65847c9f (patch)
treed403766e62b7a7b2b8e0a03f78fd6b9c05407d4f
parentab747f84cad2047dcc7eb2850f4c9251437eb50b (diff)
btrfs-progs: receive: restore capabilities after chown
Capabilities are cleared after chown, and the btrfs-stream encodes the CHOWN command after any SET_XATTR command. So the capabilites are not always preserved. This could be fixed in kernel to emit the instructions in the right order, but fix in userspace will make it work for older kernels. If we see the capabilities among xattrs, cache the value and apply it again in case it's followed by chown on that file. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=68891 Reported-by: Juan Orti Alcaine <j.orti.alcaine@gmail.com> Signed-off-by: David Sterba <dsterba@suse.cz>
-rw-r--r--cmds-receive.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/cmds-receive.c b/cmds-receive.c
index b0a312ca..05af6f7e 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -68,6 +68,14 @@ struct btrfs_receive
struct subvol_uuid_search sus;
int honor_end_cmd;
+
+ /*
+ * Buffer to store capabilities from security.capabilities xattr,
+ * usually 20 bytes, but make same room for potentially larger
+ * encodings. Must be set only once per file, denoted by length > 0.
+ */
+ char cached_capabilities[64];
+ int cached_capabilities_len;
};
static int finish_subvol(struct btrfs_receive *r)
@@ -652,6 +660,24 @@ static int process_set_xattr(const char *path, const char *name,
struct btrfs_receive *r = user;
char *full_path = path_cat(r->full_subvol_path, path);
+ if (strcmp("security.capability", name) == 0) {
+ if (g_verbose >= 3)
+ fprintf(stderr, "set_xattr: cache capabilities\n");
+ if (r->cached_capabilities_len)
+ fprintf(stderr,
+ "WARNING: capabilities set multiple times per file: %s\n",
+ full_path);
+ if (len > sizeof(r->cached_capabilities)) {
+ fprintf(stderr,
+ "ERROR: capabilities encoded to %d bytes, buffer too small\n",
+ len);
+ ret = -E2BIG;
+ goto out;
+ }
+ r->cached_capabilities_len = len;
+ memcpy(r->cached_capabilities, data, len);
+ }
+
if (g_verbose >= 2) {
fprintf(stderr, "set_xattr %s - name=%s data_len=%d "
"data=%.*s\n", path, name, len,
@@ -757,6 +783,23 @@ static int process_chown(const char *path, u64 uid, u64 gid, void *user)
goto out;
}
+ if (r->cached_capabilities_len) {
+ if (g_verbose >= 2)
+ fprintf(stderr, "chown: restore capabilities\n");
+ ret = lsetxattr(full_path, "security.capability",
+ r->cached_capabilities,
+ r->cached_capabilities_len, 0);
+ memset(r->cached_capabilities, 0,
+ sizeof(r->cached_capabilities));
+ r->cached_capabilities_len = 0;
+ if (ret < 0) {
+ ret = -errno;
+ fprintf(stderr, "ERROR: restoring capabilities %s: %s\n",
+ path, strerror(-ret));
+ goto out;
+ }
+ }
+
out:
free(full_path);
return ret;
@@ -894,6 +937,14 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd,
goto out;
while (!end) {
+ if (r->cached_capabilities_len) {
+ if (g_verbose >= 3)
+ fprintf(stderr, "clear cached capabilities\n");
+ memset(r->cached_capabilities, 0,
+ sizeof(r->cached_capabilities));
+ r->cached_capabilities_len = 0;
+ }
+
ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r,
r->honor_end_cmd,
max_errors);