--- /dev/null
+From 89efda52e6b6930f80f5adda9c3c9edfb1397191 Mon Sep 17 00:00:00 2001
+From: Marcos Paulo de Souza <mpdesouza@suse.com>
+Date: Sun, 10 May 2020 23:15:07 -0300
+Subject: btrfs: send: emit file capabilities after chown
+
+From: Marcos Paulo de Souza <mpdesouza@suse.com>
+
+commit 89efda52e6b6930f80f5adda9c3c9edfb1397191 upstream.
+
+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 on 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 subvolume snapshot -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 subvolume snapshot -r fs1 fs1/snap_complete
+ $ btrfs subvolume snapshot -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
+
+To fix that, 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 workaround not needed anymore.
+
+Link: https://github.com/kdave/btrfs-progs/issues/202
+CC: stable@vger.kernel.org # 4.4+
+Suggested-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/send.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+--- a/fs/btrfs/send.c
++++ b/fs/btrfs/send.c
+@@ -35,6 +35,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
+@@ -4554,6 +4555,10 @@ static int __process_new_xattr(int num,
+ 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;
+@@ -5096,6 +5101,64 @@ static int send_extent_data(struct send_
+ return 0;
+ }
+
++/*
++ * Search for a capability xattr related to sctx->cur_ino. If the capability is
++ * 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;
++ 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,
++ XATTR_NAME_CAPS, strlen(XATTR_NAME_CAPS), 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)(di + 1) + btrfs_dir_name_len(leaf, di);
++ read_extent_buffer(leaf, buf, data_ptr, buf_len);
++
++ ret = send_set_xattr(sctx, fspath, XATTR_NAME_CAPS,
++ strlen(XATTR_NAME_CAPS), 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,
+@@ -5907,6 +5970,10 @@ static int finish_inode_if_needed(struct
+ 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.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
- include/linux/string.h | 60 +++++++++++++++++++++++++++++++++---------
+ include/linux/string.h | 60 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 48 insertions(+), 12 deletions(-)
-diff --git a/include/linux/string.h b/include/linux/string.h
-index 3d43329c20be..315fef3aff4e 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
-@@ -238,6 +238,31 @@ void __read_overflow3(void) __compiletime_error("detected read beyond size of ob
+@@ -238,6 +238,31 @@ void __read_overflow3(void) __compiletim
void __write_overflow(void) __compiletime_error("detected write beyond size of object passed as 1st parameter");
#if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
__FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size)
{
size_t p_size = __builtin_object_size(p, 0);
-@@ -245,14 +270,14 @@ __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size)
+@@ -245,14 +270,14 @@ __FORTIFY_INLINE char *strncpy(char *p,
__write_overflow();
if (p_size < size)
fortify_panic(__func__);
if (strlcat(p, q, p_size) >= p_size)
fortify_panic(__func__);
return p;
-@@ -266,7 +291,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p)
+@@ -266,7 +291,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(
/* Work around gcc excess stack consumption issue */
if (p_size == (size_t)-1 ||
(__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0'))
ret = strnlen(p, p_size);
if (p_size <= ret)
fortify_panic(__func__);
-@@ -299,7 +324,7 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size)
+@@ -299,7 +324,7 @@ __FORTIFY_INLINE size_t strlcpy(char *p,
__write_overflow();
if (len >= p_size)
fortify_panic(__func__);
p[len] = '\0';
}
return ret;
-@@ -312,12 +337,12 @@ __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count)
+@@ -312,12 +337,12 @@ __FORTIFY_INLINE char *strncat(char *p,
size_t p_size = __builtin_object_size(p, 0);
size_t q_size = __builtin_object_size(q, 0);
if (p_size == (size_t)-1 && q_size == (size_t)-1)
p[p_len + copy_len] = '\0';
return p;
}
-@@ -329,7 +354,7 @@ __FORTIFY_INLINE void *memset(void *p, int c, __kernel_size_t size)
+@@ -329,7 +354,7 @@ __FORTIFY_INLINE void *memset(void *p, i
__write_overflow();
if (p_size < size)
fortify_panic(__func__);
}
__FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
-@@ -344,7 +369,7 @@ __FORTIFY_INLINE void *memcpy(void *p, const void *q, __kernel_size_t size)
+@@ -344,7 +369,7 @@ __FORTIFY_INLINE void *memcpy(void *p, c
}
if (p_size < size || q_size < size)
fortify_panic(__func__);
}
__FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size)
-@@ -359,7 +384,7 @@ __FORTIFY_INLINE void *memmove(void *p, const void *q, __kernel_size_t size)
+@@ -359,7 +384,7 @@ __FORTIFY_INLINE void *memmove(void *p,
}
if (p_size < size || q_size < size)
fortify_panic(__func__);
}
extern void *__real_memscan(void *, int, __kernel_size_t) __RENAME(memscan);
-@@ -385,7 +410,7 @@ __FORTIFY_INLINE int memcmp(const void *p, const void *q, __kernel_size_t size)
+@@ -385,7 +410,7 @@ __FORTIFY_INLINE int memcmp(const void *
}
if (p_size < size || q_size < size)
fortify_panic(__func__);
}
__FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size)
-@@ -395,7 +420,7 @@ __FORTIFY_INLINE void *memchr(const void *p, int c, __kernel_size_t size)
+@@ -395,7 +420,7 @@ __FORTIFY_INLINE void *memchr(const void
__read_overflow();
if (p_size < size)
fortify_panic(__func__);
}
void *__real_memchr_inv(const void *s, int c, size_t n) __RENAME(memchr_inv);
-@@ -426,11 +451,22 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q)
+@@ -426,11 +451,22 @@ __FORTIFY_INLINE char *strcpy(char *p, c
size_t p_size = __builtin_object_size(p, 0);
size_t q_size = __builtin_object_size(q, 0);
if (p_size == (size_t)-1 && q_size == (size_t)-1)
#endif
/**
---
-2.25.1
-