Skip to content

Commit

Permalink
btrfs: send: Emit file capabilities after chown
Browse files Browse the repository at this point in the history
[PROBLEM]
Whenever a chown is executed, all capabilities of the file being touched are
lost.  When doing incremental send with a file with capabilities, there is a
situation where the capability can be lost in the receiving side. The
sequence of actions bellow shows the problem:

$ mount /dev/sda fs1
$ mount /dev/sdb fs2

$ touch fs1/foo.bar
$ setcap cap_sys_nice+ep fs1/foo.bar
$ btrfs subvol snap -r fs1 fs1/snap_init
$ btrfs send fs1/snap_init | btrfs receive fs2

$ chgrp adm fs1/foo.bar
$ setcap cap_sys_nice+ep fs1/foo.bar

$ btrfs subvol snap -r fs1 fs1/snap_complete
$ btrfs subvol snap -r fs1 fs1/snap_incremental

$ btrfs send fs1/snap_complete | btrfs receive fs2
$ btrfs send -p fs1/snap_init fs1/snap_incremental | btrfs receive fs2

At this point, only a chown was emitted by "btrfs send" since only the group
was changed. This makes the cap_sys_nice capability to be dropped from
fs2/snap_incremental/foo.bar

[FIX]
Only emit capabilities after chown is emitted. The current code
first checks for xattrs that are new/changed, emits them, and later emit
the chown. Now, __process_new_xattr skips capabilities, letting only
finish_inode_if_needed to emit them, if they exist, for the inode being
processed.

This behavior was being worked around in "btrfs receive"
side by caching the capability and only applying it after chown. Now,
xattrs are only emmited _after_ chown, making that hack not needed
anymore.

Link: kdave/btrfs-progs#202
Suggested-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
  • Loading branch information
marcosps authored and intel-lab-lkp committed May 9, 2020
1 parent 5211fe9 commit 590eb58
Showing 1 changed file with 69 additions and 0 deletions.
69 changes: 69 additions & 0 deletions fs/btrfs/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "btrfs_inode.h"
#include "transaction.h"
#include "compression.h"
#include "xattr.h"

/*
* Maximum number of references an extent can have in order for us to attempt to
Expand Down Expand Up @@ -4545,6 +4546,10 @@ static int __process_new_xattr(int num, struct btrfs_key *di_key,
struct fs_path *p;
struct posix_acl_xattr_header dummy_acl;

/* capabilities are emitted by finish_inode_if_needed */
if (!strncmp(name, XATTR_NAME_CAPS, name_len))
return 0;

p = fs_path_alloc();
if (!p)
return -ENOMEM;
Expand Down Expand Up @@ -5107,6 +5112,66 @@ static int send_extent_data(struct send_ctx *sctx,
return 0;
}

/*
* Search for a capability xattr related to sctx->cur_ino. If the capability if
* found, call send_set_xattr function to emit it.
*
* Return %0 if there isn't a capability, or when the capability was emitted
* successfully, or < %0 if an error occurred.
*/
static int send_capabilities(struct send_ctx *sctx)
{
struct fs_path *fspath = NULL;
struct btrfs_path *path;
struct btrfs_dir_item *di;
struct extent_buffer *leaf;
unsigned long data_ptr;
const char *name = XATTR_NAME_CAPS;
char *buf = NULL;
int buf_len;
int ret = 0;

path = alloc_path_for_send();
if (!path)
return -ENOMEM;

di = btrfs_lookup_xattr(NULL, sctx->send_root, path, sctx->cur_ino,
name, strlen(name), 0);
if (!di) {
/* there is no xattr for this inode */
goto out;
} else if (IS_ERR(di)) {
ret = PTR_ERR(di);
goto out;
}

leaf = path->nodes[0];
buf_len = btrfs_dir_data_len(leaf, di);

fspath = fs_path_alloc();
buf = kmalloc(buf_len, GFP_KERNEL);
if (!fspath || !buf) {
ret = -ENOMEM;
goto out;
}

ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, fspath);
if (ret < 0)
goto out;

data_ptr = (unsigned long)((char *)(di + 1) +
btrfs_dir_name_len(leaf, di));
read_extent_buffer(leaf, buf, data_ptr,
btrfs_dir_data_len(leaf, di));

ret = send_set_xattr(sctx, fspath, name, strlen(name), buf, buf_len);
out:
kfree(buf);
fs_path_free(fspath);
btrfs_free_path(path);
return ret;
}

static int clone_range(struct send_ctx *sctx,
struct clone_root *clone_root,
const u64 disk_byte,
Expand Down Expand Up @@ -5972,6 +6037,10 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
goto out;
}

ret = send_capabilities(sctx);
if (ret < 0)
goto out;

/*
* If other directory inodes depended on our current directory
* inode's move/rename, now do their move/rename operations.
Expand Down

0 comments on commit 590eb58

Please sign in to comment.