* [PATCH] btrfs: add chattr support for send/receive
@ 2018-04-17 23:39 Howard McLauchlan
2018-04-17 23:39 ` [PATCH] btrfs-progs: " Howard McLauchlan
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Howard McLauchlan @ 2018-04-17 23:39 UTC (permalink / raw)
To: linux-btrfs
Cc: Chris Mason, Josef Bacik, David Sterba, Omar Sandoval,
kernel-team, Howard McLauchlan
Presently btrfs send/receive does not propagate inode attribute flags;
all chattr operations are effectively discarded upon transmission.
This patch adds kernel support for inode attribute flags. Userspace
support can be found under the commit:
btrfs-progs: add chattr support for send/receive
An associated xfstest can be found at:
btrfs: add verify chattr support for send/receive test
A caveat is that a user with an updated kernel (aware of chattrs) and an
older version of btrfs-progs (unaware of chattrs) will fail to receive
if a chattr is included in the send stream.
Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
---
Based on 4.17-rc1
fs/btrfs/ctree.h | 2 +
fs/btrfs/ioctl.c | 2 +-
fs/btrfs/send.c | 176 +++++++++++++++++++++++++++++++++++++++--------
fs/btrfs/send.h | 2 +
4 files changed, 154 insertions(+), 28 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 5474ef14d6e6..a0dc6a8a37eb 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1436,6 +1436,8 @@ struct btrfs_map_token {
unsigned long offset;
};
+unsigned int btrfs_flags_to_ioctl(unsigned int flags);
+
#define BTRFS_BYTES_TO_BLKS(fs_info, bytes) \
((bytes) >> (fs_info)->sb->s_blocksize_bits)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 632e26d6f7ce..36ce1e589f9e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -106,7 +106,7 @@ static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
/*
* Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
*/
-static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
+unsigned int btrfs_flags_to_ioctl(unsigned int flags)
{
unsigned int iflags = 0;
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 221e5cdb060b..da521a5a1843 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -101,6 +101,13 @@ struct send_ctx {
u64 cur_inode_last_extent;
u64 cur_inode_next_write_offset;
+ /*
+ * state for chattr purposes
+ */
+ u64 cur_inode_flip_flags;
+ u64 cur_inode_receive_flags;
+ int receive_flags_valid;
+
u64 send_progress;
struct list_head new_refs;
@@ -798,7 +805,7 @@ static int send_rmdir(struct send_ctx *sctx, struct fs_path *path)
*/
static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
- u64 *gid, u64 *rdev)
+ u64 *gid, u64 *rdev, u64 *flags)
{
int ret;
struct btrfs_inode_item *ii;
@@ -828,6 +835,8 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
*gid = btrfs_inode_gid(path->nodes[0], ii);
if (rdev)
*rdev = btrfs_inode_rdev(path->nodes[0], ii);
+ if (flags)
+ *flags = btrfs_inode_flags(path->nodes[0], ii);
return ret;
}
@@ -835,7 +844,7 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
static int get_inode_info(struct btrfs_root *root,
u64 ino, u64 *size, u64 *gen,
u64 *mode, u64 *uid, u64 *gid,
- u64 *rdev)
+ u64 *rdev, u64 *flags)
{
struct btrfs_path *path;
int ret;
@@ -844,7 +853,7 @@ static int get_inode_info(struct btrfs_root *root,
if (!path)
return -ENOMEM;
ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
- rdev);
+ rdev, flags);
btrfs_free_path(path);
return ret;
}
@@ -1233,7 +1242,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
* accept clones from these extents.
*/
ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
btrfs_release_path(bctx->path);
if (ret < 0)
return ret;
@@ -1593,7 +1602,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
u64 right_gen;
ret = get_inode_info(sctx->send_root, ino, NULL, &left_gen, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret < 0 && ret != -ENOENT)
goto out;
left_ret = ret;
@@ -1602,7 +1611,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
right_ret = -ENOENT;
} else {
ret = get_inode_info(sctx->parent_root, ino, NULL, &right_gen,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
if (ret < 0 && ret != -ENOENT)
goto out;
right_ret = ret;
@@ -1771,7 +1780,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
if (dir_gen) {
ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret < 0)
goto out;
}
@@ -1844,7 +1853,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
*/
if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret < 0 && ret != -ENOENT)
goto out;
if (ret) {
@@ -1872,7 +1881,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
if (other_inode > sctx->send_progress ||
is_waiting_for_move(sctx, other_inode)) {
ret = get_inode_info(sctx->parent_root, other_inode, NULL,
- who_gen, who_mode, NULL, NULL, NULL);
+ who_gen, who_mode, NULL, NULL, NULL, NULL);
if (ret < 0)
goto out;
@@ -1912,7 +1921,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
if (dir != BTRFS_FIRST_FREE_OBJECTID) {
ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret < 0 && ret != -ENOENT)
goto out;
if (ret) {
@@ -1935,7 +1944,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
}
ret = get_inode_info(sctx->send_root, ow_inode, NULL, &gen, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret < 0)
goto out;
@@ -2502,6 +2511,39 @@ static int send_chown(struct send_ctx *sctx, u64 ino, u64 gen, u64 uid, u64 gid)
return ret;
}
+static int send_chattr(struct send_ctx *sctx, u64 ino, u64 gen, u64 flags)
+{
+ struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
+ int ret = 0;
+ int __flags;
+ struct fs_path *p;
+
+ __flags = btrfs_flags_to_ioctl(flags);
+
+ btrfs_debug(fs_info, "send_chattr %llu flags=%llu", ino, flags);
+
+ p = fs_path_alloc();
+ if (!p)
+ return -ENOMEM;
+
+ ret = begin_cmd(sctx, BTRFS_SEND_C_CHATTR);
+ if (ret < 0)
+ goto out;
+
+ ret = get_cur_path(sctx, ino, gen, p);
+ if (ret < 0)
+ goto out;
+ TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
+ TLV_PUT_U64(sctx, BTRFS_SEND_A_CHATTR, __flags);
+
+ ret = send_cmd(sctx);
+
+tlv_put_failure:
+out:
+ fs_path_free(p);
+ return ret;
+}
+
static int send_utimes(struct send_ctx *sctx, u64 ino, u64 gen)
{
struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
@@ -2583,7 +2625,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)
if (ino != sctx->cur_ino) {
ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode,
- NULL, NULL, &rdev);
+ NULL, NULL, &rdev, NULL);
if (ret < 0)
goto out;
} else {
@@ -3299,7 +3341,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
* The parent inode might have been deleted in the send snapshot
*/
ret = get_inode_info(sctx->send_root, cur->dir, NULL,
- NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL, NULL);
if (ret == -ENOENT) {
ret = 0;
continue;
@@ -3469,11 +3511,11 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
}
ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
- &left_gen, NULL, NULL, NULL, NULL);
+ &left_gen, NULL, NULL, NULL, NULL, NULL);
if (ret < 0)
goto out;
ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
- &right_gen, NULL, NULL, NULL, NULL);
+ &right_gen, NULL, NULL, NULL, NULL, NULL);
if (ret < 0) {
if (ret == -ENOENT)
ret = 0;
@@ -3617,7 +3659,7 @@ static int is_ancestor(struct btrfs_root *root,
}
ret = get_inode_info(root, parent, NULL, &parent_gen,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
if (ret < 0)
goto out;
ret = check_ino_in_path(root, ino1, ino1_gen,
@@ -3707,7 +3749,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
ret = get_inode_info(sctx->parent_root, ino, NULL,
&parent_ino_gen, NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
if (ret < 0)
goto out;
if (ino_gen == parent_ino_gen) {
@@ -4184,7 +4226,7 @@ static int record_ref(struct btrfs_root *root, u64 dir, struct fs_path *name,
return -ENOMEM;
ret = get_inode_info(root, dir, NULL, &gen, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret < 0)
goto out;
@@ -4272,7 +4314,7 @@ static int __find_iref(int num, u64 dir, int index,
* else matches.
*/
ret = get_inode_info(ctx->root, dir, NULL, &dir_gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret)
return ret;
if (dir_gen != ctx->dir_gen)
@@ -4316,7 +4358,7 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
struct send_ctx *sctx = ctx;
ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret)
return ret;
@@ -4339,7 +4381,7 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
struct send_ctx *sctx = ctx;
ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret)
return ret;
@@ -4915,7 +4957,7 @@ static int send_clone(struct send_ctx *sctx,
if (clone_root->root == sctx->send_root) {
ret = get_inode_info(sctx->send_root, clone_root->ino, NULL,
- &gen, NULL, NULL, NULL, NULL);
+ &gen, NULL, NULL, NULL, NULL, NULL);
if (ret < 0)
goto out;
ret = get_cur_path(sctx, clone_root->ino, gen, p);
@@ -5777,9 +5819,11 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
u64 right_mode;
u64 right_uid;
u64 right_gid;
+ u64 left_flags;
int need_chmod = 0;
int need_chown = 0;
int need_truncate = 1;
+ int need_chattr = 0;
int pending_move = 0;
int refs_processed = 0;
@@ -5787,7 +5831,6 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
&refs_processed);
if (ret < 0)
goto out;
-
/*
* We have processed the refs and thus need to advance send_progress.
* Now, calls to get_cur_xxx will take the updated refs of the current
@@ -5805,11 +5848,67 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
if (sctx->cur_ino == 0 || sctx->cur_inode_deleted)
goto out;
+
+ /*
+ * If possible, we want to know what flags are set for this inode on the
+ * receiving end.
+ */
+ if (sctx->parent_root && !sctx->receive_flags_valid) {
+ ret = get_inode_info(sctx->parent_root, sctx->cur_ino, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ &sctx->cur_inode_receive_flags);
+ if (ret < 0)
+ goto out;
+ sctx->receive_flags_valid = 1;
+ }
+
+ /*
+ * The change is going to modify data and the inode already exists
+ * !at_end prevents unnecessary chattr.
+ */
+ if (!at_end && sctx->parent_root && !sctx->cur_inode_new &&
+ (sctx->cmp_key->type == BTRFS_EXTENT_DATA_KEY ||
+ sctx->cmp_key->type == BTRFS_XATTR_ITEM_KEY)) {
+
+ ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
+ NULL, NULL, NULL, NULL, &left_flags);
+ if (ret < 0)
+ goto out;
+ /*
+ * We check against the receive flags first; then check against
+ * the left flags to see if we can save a chattr later on
+ */
+ if (sctx->cur_inode_receive_flags & BTRFS_INODE_IMMUTABLE) {
+ sctx->cur_inode_flip_flags |= (left_flags &
+ BTRFS_INODE_IMMUTABLE);
+ left_flags &= ~BTRFS_INODE_IMMUTABLE;
+ need_chattr = 1;
+ }
+ if (sctx->cur_inode_receive_flags & BTRFS_INODE_APPEND) {
+ sctx->cur_inode_flip_flags |= (left_flags &
+ BTRFS_INODE_APPEND);
+ left_flags &= ~BTRFS_INODE_APPEND;
+ need_chattr = 1;
+ }
+ if (need_chattr) {
+ need_chattr = 0;
+ ret = send_chattr(sctx, sctx->cur_ino,
+ sctx->cur_inode_gen, left_flags);
+ if (ret < 0)
+ goto out;
+ /*
+ * left_flags is now an accurate rep of what the
+ * receiving inode's flags are
+ */
+ sctx->cur_inode_receive_flags = left_flags;
+ }
+ }
+
if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)
goto out;
ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
- &left_mode, &left_uid, &left_gid, NULL);
+ &left_mode, &left_uid, &left_gid, NULL, &left_flags);
if (ret < 0)
goto out;
@@ -5819,12 +5918,14 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
need_chmod = 1;
if (sctx->cur_inode_next_write_offset == sctx->cur_inode_size)
need_truncate = 0;
+ if (left_flags)
+ need_chattr = 1;
} else {
u64 old_size;
ret = get_inode_info(sctx->parent_root, sctx->cur_ino,
&old_size, NULL, &right_mode, &right_uid,
- &right_gid, NULL);
+ &right_gid, NULL, NULL);
if (ret < 0)
goto out;
@@ -5897,6 +5998,27 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
goto out;
}
+ /*
+ * At this point, if we toggled stuff earlier, untoggle it
+ * force a chattr and fix the flags
+ */
+ if (sctx->cur_inode_flip_flags) {
+ left_flags |= sctx->cur_inode_flip_flags;
+ }
+
+ /*
+ * We either need a chattr because this inode is new, or we need to make
+ * a change due to a discrepancy between left_flags and receive_flags
+ */
+ if (need_chattr || (sctx->cur_inode_receive_flags != left_flags)) {
+ ret = send_chattr(sctx, sctx->cur_ino, sctx->cur_inode_gen,
+ left_flags);
+ if (ret < 0)
+ goto out;
+ }
+ sctx->cur_inode_flip_flags = 0;
+ sctx->cur_inode_receive_flags = 0;
+ sctx->receive_flags_valid = 0;
out:
return ret;
}
@@ -6198,12 +6320,12 @@ static int dir_changed(struct send_ctx *sctx, u64 dir)
int ret;
ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret)
return ret;
ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret)
return ret;
diff --git a/fs/btrfs/send.h b/fs/btrfs/send.h
index ead397f7034f..b6e6dcc3db70 100644
--- a/fs/btrfs/send.h
+++ b/fs/btrfs/send.h
@@ -77,6 +77,7 @@ enum btrfs_send_cmd {
BTRFS_SEND_C_END,
BTRFS_SEND_C_UPDATE_EXTENT,
+ BTRFS_SEND_C_CHATTR,
__BTRFS_SEND_C_MAX,
};
#define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
@@ -114,6 +115,7 @@ enum {
BTRFS_SEND_A_CLONE_PATH,
BTRFS_SEND_A_CLONE_OFFSET,
BTRFS_SEND_A_CLONE_LEN,
+ BTRFS_SEND_A_CHATTR,
__BTRFS_SEND_A_MAX,
};
--
2.17.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH] btrfs-progs: add chattr support for send/receive
2018-04-17 23:39 [PATCH] btrfs: add chattr support for send/receive Howard McLauchlan
@ 2018-04-17 23:39 ` Howard McLauchlan
2018-04-17 23:49 ` [PATCH] btrfs: " Howard McLauchlan
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Howard McLauchlan @ 2018-04-17 23:39 UTC (permalink / raw)
To: linux-btrfs
Cc: Chris Mason, Josef Bacik, David Sterba, Omar Sandoval,
kernel-team, Howard McLauchlan
Presently, btrfs send/receive does not propagate inode attribute flags;
all chattr operations are effectively discarded upon transmission.
This patch adds userspace support for inode attribute flags. Kernel
support can be found under the commit:
btrfs: add chattr support for send/receive
An associated xfstest can also be found at:
btrfs: verify chattr support for send/receive test
A caveat is that a user with an updated kernel (aware of chattrs) and an
older version of btrfs-progs (unaware of chattrs) will fail to receive
if a chattr is included in the send stream.
Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
---
cmds-receive.c | 31 +++++++++++++++++++++++++++++++
send-dump.c | 8 +++++++-
send-stream.c | 5 +++++
send-stream.h | 1 +
send.h | 2 ++
5 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/cmds-receive.c b/cmds-receive.c
index 68123a31..883aee4e 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -38,6 +38,7 @@
#include <sys/types.h>
#include <sys/xattr.h>
#include <uuid/uuid.h>
+#include <linux/fs.h>
#include "ctree.h"
#include "ioctl.h"
@@ -1059,6 +1060,35 @@ static int process_update_extent(const char *path, u64 offset, u64 len,
return 0;
}
+static int process_chattr(const char *path, u64 flags, void *user)
+{
+ int ret = 0;
+ int fd = 0;
+ int _flags = flags;
+ struct btrfs_receive *rctx = user;
+ char full_path[PATH_MAX];
+
+ ret = path_cat_out(full_path, rctx->full_subvol_path, path);
+ if (ret < 0) {
+ error("chattr: path invalid: %s", path);
+ goto out;
+ }
+
+ if (g_verbose >= 2)
+ fprintf(stderr, "chattr %s - flags=0%o\n", path, (int)flags);
+
+ fd = open(full_path, O_RDONLY);
+ ret = ioctl(fd, FS_IOC_SETFLAGS, &_flags);
+
+ if (ret < 0) {
+ ret = -errno;
+ error("chattr %s failed: %s", path, strerror(-ret));
+ goto out;
+ }
+
+out:
+ return ret;
+}
static struct btrfs_send_ops send_ops = {
.subvol = process_subvol,
.snapshot = process_snapshot,
@@ -1081,6 +1111,7 @@ static struct btrfs_send_ops send_ops = {
.chown = process_chown,
.utimes = process_utimes,
.update_extent = process_update_extent,
+ .chattr = process_chattr,
};
static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
diff --git a/send-dump.c b/send-dump.c
index 1591e0cc..757ed5ec 100644
--- a/send-dump.c
+++ b/send-dump.c
@@ -316,6 +316,11 @@ static int print_update_extent(const char *path, u64 offset, u64 len,
offset, len);
}
+static int print_chattr(const char *path, u64 flags, void *user)
+{
+ return PRINT_DUMP(user, path, "chattr", "flags=%llu", flags);
+}
+
struct btrfs_send_ops btrfs_print_send_ops = {
.subvol = print_subvol,
.snapshot = print_snapshot,
@@ -337,5 +342,6 @@ struct btrfs_send_ops btrfs_print_send_ops = {
.chmod = print_chmod,
.chown = print_chown,
.utimes = print_utimes,
- .update_extent = print_update_extent
+ .update_extent = print_update_extent,
+ .chattr = print_chattr,
};
diff --git a/send-stream.c b/send-stream.c
index 78f2571a..2c927af2 100644
--- a/send-stream.c
+++ b/send-stream.c
@@ -453,6 +453,11 @@ static int read_and_process_cmd(struct btrfs_send_stream *sctx)
TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
ret = sctx->ops->update_extent(path, offset, tmp, sctx->user);
break;
+ case BTRFS_SEND_C_CHATTR:
+ TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
+ TLV_GET_U64(sctx, BTRFS_SEND_A_CHATTR, &tmp);
+ ret = sctx->ops->chattr(path, tmp, sctx->user);
+ break;
case BTRFS_SEND_C_END:
ret = 1;
break;
diff --git a/send-stream.h b/send-stream.h
index 39901f86..2446f22d 100644
--- a/send-stream.h
+++ b/send-stream.h
@@ -66,6 +66,7 @@ struct btrfs_send_ops {
struct timespec *mt, struct timespec *ct,
void *user);
int (*update_extent)(const char *path, u64 offset, u64 len, void *user);
+ int (*chattr)(const char *path, u64 flags, void *user);
};
int btrfs_read_and_process_send_stream(int fd,
diff --git a/send.h b/send.h
index fe613cbb..fd07495f 100644
--- a/send.h
+++ b/send.h
@@ -94,6 +94,7 @@ enum btrfs_send_cmd {
BTRFS_SEND_C_END,
BTRFS_SEND_C_UPDATE_EXTENT,
+ BTRFS_SEND_C_CHATTR,
__BTRFS_SEND_C_MAX,
};
#define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
@@ -131,6 +132,7 @@ enum {
BTRFS_SEND_A_CLONE_PATH,
BTRFS_SEND_A_CLONE_OFFSET,
BTRFS_SEND_A_CLONE_LEN,
+ BTRFS_SEND_A_CHATTR,
__BTRFS_SEND_A_MAX,
};
--
2.17.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] btrfs: add chattr support for send/receive
2018-04-17 23:39 [PATCH] btrfs: add chattr support for send/receive Howard McLauchlan
2018-04-17 23:39 ` [PATCH] btrfs-progs: " Howard McLauchlan
@ 2018-04-17 23:49 ` Howard McLauchlan
2018-04-18 8:15 ` Filipe Manana
2018-04-18 9:47 ` David Sterba
3 siblings, 0 replies; 6+ messages in thread
From: Howard McLauchlan @ 2018-04-17 23:49 UTC (permalink / raw)
To: linux-btrfs
Cc: Chris Mason, Josef Bacik, David Sterba, Omar Sandoval,
kernel-team
On 04/17/2018 04:39 PM, Howard McLauchlan wrote:
> Presently btrfs send/receive does not propagate inode attribute flags;
> all chattr operations are effectively discarded upon transmission.
>
> This patch adds kernel support for inode attribute flags. Userspace
> support can be found under the commit:
>
> btrfs-progs: add chattr support for send/receive
>
> An associated xfstest can be found at:
>
> btrfs: add verify chattr support for send/receive test
>
> A caveat is that a user with an updated kernel (aware of chattrs) and an
> older version of btrfs-progs (unaware of chattrs) will fail to receive
> if a chattr is included in the send stream.
>
> Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
> ---
> Based on 4.17-rc1
>
> fs/btrfs/ctree.h | 2 +
> fs/btrfs/ioctl.c | 2 +-
> fs/btrfs/send.c | 176 +++++++++++++++++++++++++++++++++++++++--------
> fs/btrfs/send.h | 2 +
> 4 files changed, 154 insertions(+), 28 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 5474ef14d6e6..a0dc6a8a37eb 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -1436,6 +1436,8 @@ struct btrfs_map_token {
> unsigned long offset;
> };
>
> +unsigned int btrfs_flags_to_ioctl(unsigned int flags);
> +
> #define BTRFS_BYTES_TO_BLKS(fs_info, bytes) \
> ((bytes) >> (fs_info)->sb->s_blocksize_bits)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 632e26d6f7ce..36ce1e589f9e 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -106,7 +106,7 @@ static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
> /*
> * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
> */
> -static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
> +unsigned int btrfs_flags_to_ioctl(unsigned int flags)
> {
> unsigned int iflags = 0;
>
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index 221e5cdb060b..da521a5a1843 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -101,6 +101,13 @@ struct send_ctx {
> u64 cur_inode_last_extent;
> u64 cur_inode_next_write_offset;
>
> + /*
> + * state for chattr purposes
> + */
> + u64 cur_inode_flip_flags;
> + u64 cur_inode_receive_flags;
> + int receive_flags_valid;
> +
> u64 send_progress;
>
> struct list_head new_refs;
> @@ -798,7 +805,7 @@ static int send_rmdir(struct send_ctx *sctx, struct fs_path *path)
> */
> static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
> - u64 *gid, u64 *rdev)
> + u64 *gid, u64 *rdev, u64 *flags)
> {
> int ret;
> struct btrfs_inode_item *ii;
> @@ -828,6 +835,8 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> *gid = btrfs_inode_gid(path->nodes[0], ii);
> if (rdev)
> *rdev = btrfs_inode_rdev(path->nodes[0], ii);
> + if (flags)
> + *flags = btrfs_inode_flags(path->nodes[0], ii);
>
> return ret;
> }
> @@ -835,7 +844,7 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> static int get_inode_info(struct btrfs_root *root,
> u64 ino, u64 *size, u64 *gen,
> u64 *mode, u64 *uid, u64 *gid,
> - u64 *rdev)
> + u64 *rdev, u64 *flags)
> {
> struct btrfs_path *path;
> int ret;
> @@ -844,7 +853,7 @@ static int get_inode_info(struct btrfs_root *root,
> if (!path)
> return -ENOMEM;
> ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
> - rdev);
> + rdev, flags);
> btrfs_free_path(path);
> return ret;
> }
> @@ -1233,7 +1242,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
> * accept clones from these extents.
> */
> ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> btrfs_release_path(bctx->path);
> if (ret < 0)
> return ret;
> @@ -1593,7 +1602,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
> u64 right_gen;
>
> ret = get_inode_info(sctx->send_root, ino, NULL, &left_gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> left_ret = ret;
> @@ -1602,7 +1611,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
> right_ret = -ENOENT;
> } else {
> ret = get_inode_info(sctx->parent_root, ino, NULL, &right_gen,
> - NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> right_ret = ret;
> @@ -1771,7 +1780,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
>
> if (dir_gen) {
> ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> }
> @@ -1844,7 +1853,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
> */
> if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
> ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> if (ret) {
> @@ -1872,7 +1881,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
> if (other_inode > sctx->send_progress ||
> is_waiting_for_move(sctx, other_inode)) {
> ret = get_inode_info(sctx->parent_root, other_inode, NULL,
> - who_gen, who_mode, NULL, NULL, NULL);
> + who_gen, who_mode, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -1912,7 +1921,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
>
> if (dir != BTRFS_FIRST_FREE_OBJECTID) {
> ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> if (ret) {
> @@ -1935,7 +1944,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
> }
>
> ret = get_inode_info(sctx->send_root, ow_inode, NULL, &gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -2502,6 +2511,39 @@ static int send_chown(struct send_ctx *sctx, u64 ino, u64 gen, u64 uid, u64 gid)
> return ret;
> }
>
> +static int send_chattr(struct send_ctx *sctx, u64 ino, u64 gen, u64 flags)
> +{
> + struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
> + int ret = 0;
> + int __flags;
> + struct fs_path *p;
> +
> + __flags = btrfs_flags_to_ioctl(flags);
> +
> + btrfs_debug(fs_info, "send_chattr %llu flags=%llu", ino, flags);
> +
> + p = fs_path_alloc();
> + if (!p)
> + return -ENOMEM;
> +
> + ret = begin_cmd(sctx, BTRFS_SEND_C_CHATTR);
> + if (ret < 0)
> + goto out;
> +
> + ret = get_cur_path(sctx, ino, gen, p);
> + if (ret < 0)
> + goto out;
> + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
> + TLV_PUT_U64(sctx, BTRFS_SEND_A_CHATTR, __flags);
> +
> + ret = send_cmd(sctx);
> +
> +tlv_put_failure:
> +out:
> + fs_path_free(p);
> + return ret;
> +}
> +
> static int send_utimes(struct send_ctx *sctx, u64 ino, u64 gen)
> {
> struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
> @@ -2583,7 +2625,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)
>
> if (ino != sctx->cur_ino) {
> ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode,
> - NULL, NULL, &rdev);
> + NULL, NULL, &rdev, NULL);
> if (ret < 0)
> goto out;
> } else {
> @@ -3299,7 +3341,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
> * The parent inode might have been deleted in the send snapshot
> */
> ret = get_inode_info(sctx->send_root, cur->dir, NULL,
> - NULL, NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL, NULL);
> if (ret == -ENOENT) {
> ret = 0;
> continue;
> @@ -3469,11 +3511,11 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
> }
>
> ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
> - &left_gen, NULL, NULL, NULL, NULL);
> + &left_gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
> - &right_gen, NULL, NULL, NULL, NULL);
> + &right_gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0) {
> if (ret == -ENOENT)
> ret = 0;
> @@ -3617,7 +3659,7 @@ static int is_ancestor(struct btrfs_root *root,
> }
>
> ret = get_inode_info(root, parent, NULL, &parent_gen,
> - NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = check_ino_in_path(root, ino1, ino1_gen,
> @@ -3707,7 +3749,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
>
> ret = get_inode_info(sctx->parent_root, ino, NULL,
> &parent_ino_gen, NULL, NULL, NULL,
> - NULL);
> + NULL, NULL);
> if (ret < 0)
> goto out;
> if (ino_gen == parent_ino_gen) {
> @@ -4184,7 +4226,7 @@ static int record_ref(struct btrfs_root *root, u64 dir, struct fs_path *name,
> return -ENOMEM;
>
> ret = get_inode_info(root, dir, NULL, &gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -4272,7 +4314,7 @@ static int __find_iref(int num, u64 dir, int index,
> * else matches.
> */
> ret = get_inode_info(ctx->root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
> if (dir_gen != ctx->dir_gen)
> @@ -4316,7 +4358,7 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
> struct send_ctx *sctx = ctx;
>
> ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> @@ -4339,7 +4381,7 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
> struct send_ctx *sctx = ctx;
>
> ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> @@ -4915,7 +4957,7 @@ static int send_clone(struct send_ctx *sctx,
>
> if (clone_root->root == sctx->send_root) {
> ret = get_inode_info(sctx->send_root, clone_root->ino, NULL,
> - &gen, NULL, NULL, NULL, NULL);
> + &gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = get_cur_path(sctx, clone_root->ino, gen, p);
> @@ -5777,9 +5819,11 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> u64 right_mode;
> u64 right_uid;
> u64 right_gid;
> + u64 left_flags;
> int need_chmod = 0;
> int need_chown = 0;
> int need_truncate = 1;
> + int need_chattr = 0;
> int pending_move = 0;
> int refs_processed = 0;
>
> @@ -5787,7 +5831,6 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> &refs_processed);
> if (ret < 0)
> goto out;
> -
> /*
> * We have processed the refs and thus need to advance send_progress.
> * Now, calls to get_cur_xxx will take the updated refs of the current
> @@ -5805,11 +5848,67 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>
> if (sctx->cur_ino == 0 || sctx->cur_inode_deleted)
> goto out;
> +
> + /*
> + * If possible, we want to know what flags are set for this inode on the
> + * receiving end.
> + */
> + if (sctx->parent_root && !sctx->receive_flags_valid) {
> + ret = get_inode_info(sctx->parent_root, sctx->cur_ino, NULL,
> + NULL, NULL, NULL, NULL, NULL,
> + &sctx->cur_inode_receive_flags);
> + if (ret < 0)
> + goto out;
> + sctx->receive_flags_valid = 1;
> + }
> +
> + /*
> + * The change is going to modify data and the inode already exists
> + * !at_end prevents unnecessary chattr.
> + */
> + if (!at_end && sctx->parent_root && !sctx->cur_inode_new &&
> + (sctx->cmp_key->type == BTRFS_EXTENT_DATA_KEY ||
> + sctx->cmp_key->type == BTRFS_XATTR_ITEM_KEY)) {
> +
> + ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
> + NULL, NULL, NULL, NULL, &left_flags);
> + if (ret < 0)
> + goto out;
> + /*
> + * We check against the receive flags first; then check against
> + * the left flags to see if we can save a chattr later on
> + */
> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_IMMUTABLE) {
> + sctx->cur_inode_flip_flags |= (left_flags &
> + BTRFS_INODE_IMMUTABLE);
> + left_flags &= ~BTRFS_INODE_IMMUTABLE;
> + need_chattr = 1;
> + }
> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_APPEND) {
> + sctx->cur_inode_flip_flags |= (left_flags &
> + BTRFS_INODE_APPEND);
> + left_flags &= ~BTRFS_INODE_APPEND;
> + need_chattr = 1;
> + }
> + if (need_chattr) {
> + need_chattr = 0;
> + ret = send_chattr(sctx, sctx->cur_ino,
> + sctx->cur_inode_gen, left_flags);
> + if (ret < 0)
> + goto out;
> + /*
> + * left_flags is now an accurate rep of what the
> + * receiving inode's flags are
> + */
> + sctx->cur_inode_receive_flags = left_flags;
> + }
> + }
> +
> if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)
> goto out;
>
> ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
> - &left_mode, &left_uid, &left_gid, NULL);
> + &left_mode, &left_uid, &left_gid, NULL, &left_flags);
> if (ret < 0)
> goto out;
>
> @@ -5819,12 +5918,14 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> need_chmod = 1;
> if (sctx->cur_inode_next_write_offset == sctx->cur_inode_size)
> need_truncate = 0;
> + if (left_flags)
> + need_chattr = 1;
> } else {
> u64 old_size;
>
> ret = get_inode_info(sctx->parent_root, sctx->cur_ino,
> &old_size, NULL, &right_mode, &right_uid,
> - &right_gid, NULL);
> + &right_gid, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -5897,6 +5998,27 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> goto out;
> }
>
> + /*
> + * At this point, if we toggled stuff earlier, untoggle it
> + * force a chattr and fix the flags
> + */
> + if (sctx->cur_inode_flip_flags) {
> + left_flags |= sctx->cur_inode_flip_flags;
> + }
> +
Just noticed I have some extra braces here. Will fix in V2 along with other feedback.
> + /*
> + * We either need a chattr because this inode is new, or we need to make
> + * a change due to a discrepancy between left_flags and receive_flags
> + */
> + if (need_chattr || (sctx->cur_inode_receive_flags != left_flags)) {
> + ret = send_chattr(sctx, sctx->cur_ino, sctx->cur_inode_gen,
> + left_flags);
> + if (ret < 0)
> + goto out;
> + }
> + sctx->cur_inode_flip_flags = 0;
> + sctx->cur_inode_receive_flags = 0;
> + sctx->receive_flags_valid = 0;
> out:
> return ret;
> }
> @@ -6198,12 +6320,12 @@ static int dir_changed(struct send_ctx *sctx, u64 dir)
> int ret;
>
> ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret)
> return ret;
>
> ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> diff --git a/fs/btrfs/send.h b/fs/btrfs/send.h
> index ead397f7034f..b6e6dcc3db70 100644
> --- a/fs/btrfs/send.h
> +++ b/fs/btrfs/send.h
> @@ -77,6 +77,7 @@ enum btrfs_send_cmd {
>
> BTRFS_SEND_C_END,
> BTRFS_SEND_C_UPDATE_EXTENT,
> + BTRFS_SEND_C_CHATTR,
> __BTRFS_SEND_C_MAX,
> };
> #define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
> @@ -114,6 +115,7 @@ enum {
> BTRFS_SEND_A_CLONE_PATH,
> BTRFS_SEND_A_CLONE_OFFSET,
> BTRFS_SEND_A_CLONE_LEN,
> + BTRFS_SEND_A_CHATTR,
>
> __BTRFS_SEND_A_MAX,
> };
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] btrfs: add chattr support for send/receive
2018-04-17 23:39 [PATCH] btrfs: add chattr support for send/receive Howard McLauchlan
2018-04-17 23:39 ` [PATCH] btrfs-progs: " Howard McLauchlan
2018-04-17 23:49 ` [PATCH] btrfs: " Howard McLauchlan
@ 2018-04-18 8:15 ` Filipe Manana
2018-04-20 17:33 ` Howard McLauchlan
2018-04-18 9:47 ` David Sterba
3 siblings, 1 reply; 6+ messages in thread
From: Filipe Manana @ 2018-04-18 8:15 UTC (permalink / raw)
To: Howard McLauchlan
Cc: linux-btrfs, Chris Mason, Josef Bacik, David Sterba,
Omar Sandoval, kernel-team
On Wed, Apr 18, 2018 at 12:39 AM, Howard McLauchlan <hmclauchlan@fb.com> wrote:
> Presently btrfs send/receive does not propagate inode attribute flags;
> all chattr operations are effectively discarded upon transmission.
>
> This patch adds kernel support for inode attribute flags. Userspace
> support can be found under the commit:
>
> btrfs-progs: add chattr support for send/receive
>
> An associated xfstest can be found at:
>
> btrfs: add verify chattr support for send/receive test
>
> A caveat is that a user with an updated kernel (aware of chattrs) and an
> older version of btrfs-progs (unaware of chattrs) will fail to receive
> if a chattr is included in the send stream.
So we do have several things missing in send besides attribute flags,
like hole punching for example (there's a list on the wiki).
We can't just add a new command and introduce such caveat every time
we implement one of the missing and desired features.
In 2014, while wanting to implement some of those features, I
introduced a way to bump the send stream version with room (commands)
for all those missing features, so that all could be implemented later
without adding further backward incompatibility between kernel
versions btrfs-progs versions.
Some of the threads for reference:
https://patchwork.kernel.org/patch/4021491/
https://www.spinics.net/lists/linux-btrfs/msg35169.html
It never took off, and honestly I don't remember why as no one add
more comments on the latest versions of the kernel and btrfs-progs
patchsets.
>
> Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
> ---
> Based on 4.17-rc1
>
> fs/btrfs/ctree.h | 2 +
> fs/btrfs/ioctl.c | 2 +-
> fs/btrfs/send.c | 176 +++++++++++++++++++++++++++++++++++++++--------
> fs/btrfs/send.h | 2 +
> 4 files changed, 154 insertions(+), 28 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 5474ef14d6e6..a0dc6a8a37eb 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -1436,6 +1436,8 @@ struct btrfs_map_token {
> unsigned long offset;
> };
>
> +unsigned int btrfs_flags_to_ioctl(unsigned int flags);
> +
> #define BTRFS_BYTES_TO_BLKS(fs_info, bytes) \
> ((bytes) >> (fs_info)->sb->s_blocksize_bits)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 632e26d6f7ce..36ce1e589f9e 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -106,7 +106,7 @@ static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
> /*
> * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
> */
> -static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
> +unsigned int btrfs_flags_to_ioctl(unsigned int flags)
> {
> unsigned int iflags = 0;
>
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index 221e5cdb060b..da521a5a1843 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -101,6 +101,13 @@ struct send_ctx {
> u64 cur_inode_last_extent;
> u64 cur_inode_next_write_offset;
>
> + /*
> + * state for chattr purposes
> + */
> + u64 cur_inode_flip_flags;
> + u64 cur_inode_receive_flags;
> + int receive_flags_valid;
> +
> u64 send_progress;
>
> struct list_head new_refs;
> @@ -798,7 +805,7 @@ static int send_rmdir(struct send_ctx *sctx, struct fs_path *path)
> */
> static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
> - u64 *gid, u64 *rdev)
> + u64 *gid, u64 *rdev, u64 *flags)
> {
> int ret;
> struct btrfs_inode_item *ii;
> @@ -828,6 +835,8 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> *gid = btrfs_inode_gid(path->nodes[0], ii);
> if (rdev)
> *rdev = btrfs_inode_rdev(path->nodes[0], ii);
> + if (flags)
> + *flags = btrfs_inode_flags(path->nodes[0], ii);
>
> return ret;
> }
> @@ -835,7 +844,7 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
> static int get_inode_info(struct btrfs_root *root,
> u64 ino, u64 *size, u64 *gen,
> u64 *mode, u64 *uid, u64 *gid,
> - u64 *rdev)
> + u64 *rdev, u64 *flags)
> {
> struct btrfs_path *path;
> int ret;
> @@ -844,7 +853,7 @@ static int get_inode_info(struct btrfs_root *root,
> if (!path)
> return -ENOMEM;
> ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
> - rdev);
> + rdev, flags);
> btrfs_free_path(path);
> return ret;
> }
> @@ -1233,7 +1242,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
> * accept clones from these extents.
> */
> ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> btrfs_release_path(bctx->path);
> if (ret < 0)
> return ret;
> @@ -1593,7 +1602,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
> u64 right_gen;
>
> ret = get_inode_info(sctx->send_root, ino, NULL, &left_gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> left_ret = ret;
> @@ -1602,7 +1611,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
> right_ret = -ENOENT;
> } else {
> ret = get_inode_info(sctx->parent_root, ino, NULL, &right_gen,
> - NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> right_ret = ret;
> @@ -1771,7 +1780,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
>
> if (dir_gen) {
> ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> }
> @@ -1844,7 +1853,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
> */
> if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
> ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> if (ret) {
> @@ -1872,7 +1881,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
> if (other_inode > sctx->send_progress ||
> is_waiting_for_move(sctx, other_inode)) {
> ret = get_inode_info(sctx->parent_root, other_inode, NULL,
> - who_gen, who_mode, NULL, NULL, NULL);
> + who_gen, who_mode, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -1912,7 +1921,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
>
> if (dir != BTRFS_FIRST_FREE_OBJECTID) {
> ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret < 0 && ret != -ENOENT)
> goto out;
> if (ret) {
> @@ -1935,7 +1944,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
> }
>
> ret = get_inode_info(sctx->send_root, ow_inode, NULL, &gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -2502,6 +2511,39 @@ static int send_chown(struct send_ctx *sctx, u64 ino, u64 gen, u64 uid, u64 gid)
> return ret;
> }
>
> +static int send_chattr(struct send_ctx *sctx, u64 ino, u64 gen, u64 flags)
> +{
> + struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
> + int ret = 0;
> + int __flags;
> + struct fs_path *p;
> +
> + __flags = btrfs_flags_to_ioctl(flags);
> +
> + btrfs_debug(fs_info, "send_chattr %llu flags=%llu", ino, flags);
> +
> + p = fs_path_alloc();
> + if (!p)
> + return -ENOMEM;
> +
> + ret = begin_cmd(sctx, BTRFS_SEND_C_CHATTR);
> + if (ret < 0)
> + goto out;
> +
> + ret = get_cur_path(sctx, ino, gen, p);
> + if (ret < 0)
> + goto out;
> + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
> + TLV_PUT_U64(sctx, BTRFS_SEND_A_CHATTR, __flags);
> +
> + ret = send_cmd(sctx);
> +
> +tlv_put_failure:
> +out:
> + fs_path_free(p);
> + return ret;
> +}
> +
> static int send_utimes(struct send_ctx *sctx, u64 ino, u64 gen)
> {
> struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
> @@ -2583,7 +2625,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)
>
> if (ino != sctx->cur_ino) {
> ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode,
> - NULL, NULL, &rdev);
> + NULL, NULL, &rdev, NULL);
> if (ret < 0)
> goto out;
> } else {
> @@ -3299,7 +3341,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
> * The parent inode might have been deleted in the send snapshot
> */
> ret = get_inode_info(sctx->send_root, cur->dir, NULL,
> - NULL, NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL, NULL);
> if (ret == -ENOENT) {
> ret = 0;
> continue;
> @@ -3469,11 +3511,11 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
> }
>
> ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
> - &left_gen, NULL, NULL, NULL, NULL);
> + &left_gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
> - &right_gen, NULL, NULL, NULL, NULL);
> + &right_gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0) {
> if (ret == -ENOENT)
> ret = 0;
> @@ -3617,7 +3659,7 @@ static int is_ancestor(struct btrfs_root *root,
> }
>
> ret = get_inode_info(root, parent, NULL, &parent_gen,
> - NULL, NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = check_ino_in_path(root, ino1, ino1_gen,
> @@ -3707,7 +3749,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
>
> ret = get_inode_info(sctx->parent_root, ino, NULL,
> &parent_ino_gen, NULL, NULL, NULL,
> - NULL);
> + NULL, NULL);
> if (ret < 0)
> goto out;
> if (ino_gen == parent_ino_gen) {
> @@ -4184,7 +4226,7 @@ static int record_ref(struct btrfs_root *root, u64 dir, struct fs_path *name,
> return -ENOMEM;
>
> ret = get_inode_info(root, dir, NULL, &gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -4272,7 +4314,7 @@ static int __find_iref(int num, u64 dir, int index,
> * else matches.
> */
> ret = get_inode_info(ctx->root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
> if (dir_gen != ctx->dir_gen)
> @@ -4316,7 +4358,7 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
> struct send_ctx *sctx = ctx;
>
> ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> @@ -4339,7 +4381,7 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
> struct send_ctx *sctx = ctx;
>
> ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> @@ -4915,7 +4957,7 @@ static int send_clone(struct send_ctx *sctx,
>
> if (clone_root->root == sctx->send_root) {
> ret = get_inode_info(sctx->send_root, clone_root->ino, NULL,
> - &gen, NULL, NULL, NULL, NULL);
> + &gen, NULL, NULL, NULL, NULL, NULL);
> if (ret < 0)
> goto out;
> ret = get_cur_path(sctx, clone_root->ino, gen, p);
> @@ -5777,9 +5819,11 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> u64 right_mode;
> u64 right_uid;
> u64 right_gid;
> + u64 left_flags;
> int need_chmod = 0;
> int need_chown = 0;
> int need_truncate = 1;
> + int need_chattr = 0;
> int pending_move = 0;
> int refs_processed = 0;
>
> @@ -5787,7 +5831,6 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> &refs_processed);
> if (ret < 0)
> goto out;
> -
> /*
> * We have processed the refs and thus need to advance send_progress.
> * Now, calls to get_cur_xxx will take the updated refs of the current
> @@ -5805,11 +5848,67 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>
> if (sctx->cur_ino == 0 || sctx->cur_inode_deleted)
> goto out;
> +
> + /*
> + * If possible, we want to know what flags are set for this inode on the
> + * receiving end.
> + */
> + if (sctx->parent_root && !sctx->receive_flags_valid) {
> + ret = get_inode_info(sctx->parent_root, sctx->cur_ino, NULL,
> + NULL, NULL, NULL, NULL, NULL,
> + &sctx->cur_inode_receive_flags);
> + if (ret < 0)
> + goto out;
> + sctx->receive_flags_valid = 1;
> + }
> +
> + /*
> + * The change is going to modify data and the inode already exists
> + * !at_end prevents unnecessary chattr.
> + */
> + if (!at_end && sctx->parent_root && !sctx->cur_inode_new &&
> + (sctx->cmp_key->type == BTRFS_EXTENT_DATA_KEY ||
> + sctx->cmp_key->type == BTRFS_XATTR_ITEM_KEY)) {
> +
> + ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
> + NULL, NULL, NULL, NULL, &left_flags);
> + if (ret < 0)
> + goto out;
> + /*
> + * We check against the receive flags first; then check against
> + * the left flags to see if we can save a chattr later on
> + */
> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_IMMUTABLE) {
> + sctx->cur_inode_flip_flags |= (left_flags &
> + BTRFS_INODE_IMMUTABLE);
> + left_flags &= ~BTRFS_INODE_IMMUTABLE;
> + need_chattr = 1;
> + }
> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_APPEND) {
> + sctx->cur_inode_flip_flags |= (left_flags &
> + BTRFS_INODE_APPEND);
> + left_flags &= ~BTRFS_INODE_APPEND;
> + need_chattr = 1;
> + }
> + if (need_chattr) {
> + need_chattr = 0;
> + ret = send_chattr(sctx, sctx->cur_ino,
> + sctx->cur_inode_gen, left_flags);
> + if (ret < 0)
> + goto out;
> + /*
> + * left_flags is now an accurate rep of what the
> + * receiving inode's flags are
> + */
> + sctx->cur_inode_receive_flags = left_flags;
> + }
> + }
> +
> if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)
> goto out;
>
> ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
> - &left_mode, &left_uid, &left_gid, NULL);
> + &left_mode, &left_uid, &left_gid, NULL, &left_flags);
> if (ret < 0)
> goto out;
>
> @@ -5819,12 +5918,14 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> need_chmod = 1;
> if (sctx->cur_inode_next_write_offset == sctx->cur_inode_size)
> need_truncate = 0;
> + if (left_flags)
> + need_chattr = 1;
> } else {
> u64 old_size;
>
> ret = get_inode_info(sctx->parent_root, sctx->cur_ino,
> &old_size, NULL, &right_mode, &right_uid,
> - &right_gid, NULL);
> + &right_gid, NULL, NULL);
> if (ret < 0)
> goto out;
>
> @@ -5897,6 +5998,27 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
> goto out;
> }
>
> + /*
> + * At this point, if we toggled stuff earlier, untoggle it
> + * force a chattr and fix the flags
> + */
> + if (sctx->cur_inode_flip_flags) {
> + left_flags |= sctx->cur_inode_flip_flags;
> + }
> +
> + /*
> + * We either need a chattr because this inode is new, or we need to make
> + * a change due to a discrepancy between left_flags and receive_flags
> + */
> + if (need_chattr || (sctx->cur_inode_receive_flags != left_flags)) {
> + ret = send_chattr(sctx, sctx->cur_ino, sctx->cur_inode_gen,
> + left_flags);
> + if (ret < 0)
> + goto out;
> + }
> + sctx->cur_inode_flip_flags = 0;
> + sctx->cur_inode_receive_flags = 0;
> + sctx->receive_flags_valid = 0;
> out:
> return ret;
> }
> @@ -6198,12 +6320,12 @@ static int dir_changed(struct send_ctx *sctx, u64 dir)
> int ret;
>
> ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL,
> - NULL, NULL);
> + NULL, NULL, NULL);
> if (ret)
> return ret;
>
> ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL,
> - NULL, NULL, NULL);
> + NULL, NULL, NULL, NULL);
> if (ret)
> return ret;
>
> diff --git a/fs/btrfs/send.h b/fs/btrfs/send.h
> index ead397f7034f..b6e6dcc3db70 100644
> --- a/fs/btrfs/send.h
> +++ b/fs/btrfs/send.h
> @@ -77,6 +77,7 @@ enum btrfs_send_cmd {
>
> BTRFS_SEND_C_END,
> BTRFS_SEND_C_UPDATE_EXTENT,
> + BTRFS_SEND_C_CHATTR,
> __BTRFS_SEND_C_MAX,
> };
> #define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
> @@ -114,6 +115,7 @@ enum {
> BTRFS_SEND_A_CLONE_PATH,
> BTRFS_SEND_A_CLONE_OFFSET,
> BTRFS_SEND_A_CLONE_LEN,
> + BTRFS_SEND_A_CHATTR,
>
> __BTRFS_SEND_A_MAX,
> };
> --
> 2.17.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Filipe David Manana,
“Whether you think you can, or you think you can't — you're right.”
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] btrfs: add chattr support for send/receive
2018-04-17 23:39 [PATCH] btrfs: add chattr support for send/receive Howard McLauchlan
` (2 preceding siblings ...)
2018-04-18 8:15 ` Filipe Manana
@ 2018-04-18 9:47 ` David Sterba
3 siblings, 0 replies; 6+ messages in thread
From: David Sterba @ 2018-04-18 9:47 UTC (permalink / raw)
To: Howard McLauchlan
Cc: linux-btrfs, Chris Mason, Josef Bacik, David Sterba,
Omar Sandoval, kernel-team
On Tue, Apr 17, 2018 at 04:39:35PM -0700, Howard McLauchlan wrote:
> Presently btrfs send/receive does not propagate inode attribute flags;
> all chattr operations are effectively discarded upon transmission.
>
> This patch adds kernel support for inode attribute flags. Userspace
> support can be found under the commit:
>
> btrfs-progs: add chattr support for send/receive
>
> An associated xfstest can be found at:
>
> btrfs: add verify chattr support for send/receive test
>
> A caveat is that a user with an updated kernel (aware of chattrs) and an
> older version of btrfs-progs (unaware of chattrs) will fail to receive
> if a chattr is included in the send stream.
>
> Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
This is a send protocol change and must be properly versioned. There are
more known defficiencies from v1, see
https://btrfs.wiki.kernel.org/index.php/Design_notes_on_Send/Receive#Send_stream_v2_draft
> --- a/fs/btrfs/send.h
> +++ b/fs/btrfs/send.h
> @@ -77,6 +77,7 @@ enum btrfs_send_cmd {
>
> BTRFS_SEND_C_END,
> BTRFS_SEND_C_UPDATE_EXTENT,
> + BTRFS_SEND_C_CHATTR,
> __BTRFS_SEND_C_MAX,
This change
> };
> #define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
> @@ -114,6 +115,7 @@ enum {
> BTRFS_SEND_A_CLONE_PATH,
> BTRFS_SEND_A_CLONE_OFFSET,
> BTRFS_SEND_A_CLONE_LEN,
> + BTRFS_SEND_A_CHATTR,
>
> __BTRFS_SEND_A_MAX,
and this will change numbers of __BTRFS_SEND_*_MAX and defines derived
from them, that must stay fixed for v1, because they're part of the
public API exported from progs as send.h.
Unfortunatelly for anybody who wants to implement new additions to the
send stream, the full versioning, backward compatibility, commandline
options, ioctl extensions need to happen first.
A concrete example how this can be done wrong is the Synology version of
btrfs shipped with their NAS. There are some extensions to the protocol
that work on their kernel, but the send stream cannot be used on upstram
kernels.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] btrfs: add chattr support for send/receive
2018-04-18 8:15 ` Filipe Manana
@ 2018-04-20 17:33 ` Howard McLauchlan
0 siblings, 0 replies; 6+ messages in thread
From: Howard McLauchlan @ 2018-04-20 17:33 UTC (permalink / raw)
To: fdmanana
Cc: linux-btrfs, Chris Mason, Josef Bacik, David Sterba,
Omar Sandoval, kernel-team
On 04/18/2018 01:15 AM, Filipe Manana wrote:
> On Wed, Apr 18, 2018 at 12:39 AM, Howard McLauchlan <hmclauchlan@fb.com> wrote:
>> Presently btrfs send/receive does not propagate inode attribute flags;
>> all chattr operations are effectively discarded upon transmission.
>>
>> This patch adds kernel support for inode attribute flags. Userspace
>> support can be found under the commit:
>>
>> btrfs-progs: add chattr support for send/receive
>>
>> An associated xfstest can be found at:
>>
>> btrfs: add verify chattr support for send/receive test
>>
>> A caveat is that a user with an updated kernel (aware of chattrs) and an
>> older version of btrfs-progs (unaware of chattrs) will fail to receive
>> if a chattr is included in the send stream.
> So we do have several things missing in send besides attribute flags,
> like hole punching for example (there's a list on the wiki).
> We can't just add a new command and introduce such caveat every time
> we implement one of the missing and desired features.
>
> In 2014, while wanting to implement some of those features, I
> introduced a way to bump the send stream version with room (commands)
> for all those missing features, so that all could be implemented later
> without adding further backward incompatibility between kernel
> versions btrfs-progs versions.
> Some of the threads for reference:
>
> https://patchwork.kernel.org/patch/4021491/
> https://urldefense.proofpoint.com/v2/url?u=https-3A__www.spinics.net_lists_linux-2Dbtrfs_msg35169.html&d=DwIFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=UA4c4GV4shA70-jKB4kwcF99U6K6bzKVYdicFvu-DtQ&m=pWSiTBXI54TJfXnctkAeLJUbyIs9VZqupmGKs8JsETE&s=rsgeLq1QeHGaRs6xIXtGcGV-UgjCuqnWMATCw_KaxY8&e=
>
> It never took off, and honestly I don't remember why as no one add
> more comments on the latest versions of the kernel and btrfs-progs
> patchsets.
Thanks for the heads up, I'll go dig up your patches and try to get them working on 4.17
Howard
>
>> Signed-off-by: Howard McLauchlan <hmclauchlan@fb.com>
>> ---
>> Based on 4.17-rc1
>>
>> fs/btrfs/ctree.h | 2 +
>> fs/btrfs/ioctl.c | 2 +-
>> fs/btrfs/send.c | 176 +++++++++++++++++++++++++++++++++++++++--------
>> fs/btrfs/send.h | 2 +
>> 4 files changed, 154 insertions(+), 28 deletions(-)
>>
>> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
>> index 5474ef14d6e6..a0dc6a8a37eb 100644
>> --- a/fs/btrfs/ctree.h
>> +++ b/fs/btrfs/ctree.h
>> @@ -1436,6 +1436,8 @@ struct btrfs_map_token {
>> unsigned long offset;
>> };
>>
>> +unsigned int btrfs_flags_to_ioctl(unsigned int flags);
>> +
>> #define BTRFS_BYTES_TO_BLKS(fs_info, bytes) \
>> ((bytes) >> (fs_info)->sb->s_blocksize_bits)
>>
>> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
>> index 632e26d6f7ce..36ce1e589f9e 100644
>> --- a/fs/btrfs/ioctl.c
>> +++ b/fs/btrfs/ioctl.c
>> @@ -106,7 +106,7 @@ static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
>> /*
>> * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
>> */
>> -static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
>> +unsigned int btrfs_flags_to_ioctl(unsigned int flags)
>> {
>> unsigned int iflags = 0;
>>
>> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
>> index 221e5cdb060b..da521a5a1843 100644
>> --- a/fs/btrfs/send.c
>> +++ b/fs/btrfs/send.c
>> @@ -101,6 +101,13 @@ struct send_ctx {
>> u64 cur_inode_last_extent;
>> u64 cur_inode_next_write_offset;
>>
>> + /*
>> + * state for chattr purposes
>> + */
>> + u64 cur_inode_flip_flags;
>> + u64 cur_inode_receive_flags;
>> + int receive_flags_valid;
>> +
>> u64 send_progress;
>>
>> struct list_head new_refs;
>> @@ -798,7 +805,7 @@ static int send_rmdir(struct send_ctx *sctx, struct fs_path *path)
>> */
>> static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
>> u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
>> - u64 *gid, u64 *rdev)
>> + u64 *gid, u64 *rdev, u64 *flags)
>> {
>> int ret;
>> struct btrfs_inode_item *ii;
>> @@ -828,6 +835,8 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
>> *gid = btrfs_inode_gid(path->nodes[0], ii);
>> if (rdev)
>> *rdev = btrfs_inode_rdev(path->nodes[0], ii);
>> + if (flags)
>> + *flags = btrfs_inode_flags(path->nodes[0], ii);
>>
>> return ret;
>> }
>> @@ -835,7 +844,7 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
>> static int get_inode_info(struct btrfs_root *root,
>> u64 ino, u64 *size, u64 *gen,
>> u64 *mode, u64 *uid, u64 *gid,
>> - u64 *rdev)
>> + u64 *rdev, u64 *flags)
>> {
>> struct btrfs_path *path;
>> int ret;
>> @@ -844,7 +853,7 @@ static int get_inode_info(struct btrfs_root *root,
>> if (!path)
>> return -ENOMEM;
>> ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
>> - rdev);
>> + rdev, flags);
>> btrfs_free_path(path);
>> return ret;
>> }
>> @@ -1233,7 +1242,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
>> * accept clones from these extents.
>> */
>> ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> btrfs_release_path(bctx->path);
>> if (ret < 0)
>> return ret;
>> @@ -1593,7 +1602,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
>> u64 right_gen;
>>
>> ret = get_inode_info(sctx->send_root, ino, NULL, &left_gen, NULL, NULL,
>> - NULL, NULL);
>> + NULL, NULL, NULL);
>> if (ret < 0 && ret != -ENOENT)
>> goto out;
>> left_ret = ret;
>> @@ -1602,7 +1611,7 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)
>> right_ret = -ENOENT;
>> } else {
>> ret = get_inode_info(sctx->parent_root, ino, NULL, &right_gen,
>> - NULL, NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL, NULL);
>> if (ret < 0 && ret != -ENOENT)
>> goto out;
>> right_ret = ret;
>> @@ -1771,7 +1780,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
>>
>> if (dir_gen) {
>> ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>> }
>> @@ -1844,7 +1853,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
>> */
>> if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
>> ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret < 0 && ret != -ENOENT)
>> goto out;
>> if (ret) {
>> @@ -1872,7 +1881,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
>> if (other_inode > sctx->send_progress ||
>> is_waiting_for_move(sctx, other_inode)) {
>> ret = get_inode_info(sctx->parent_root, other_inode, NULL,
>> - who_gen, who_mode, NULL, NULL, NULL);
>> + who_gen, who_mode, NULL, NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>>
>> @@ -1912,7 +1921,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
>>
>> if (dir != BTRFS_FIRST_FREE_OBJECTID) {
>> ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret < 0 && ret != -ENOENT)
>> goto out;
>> if (ret) {
>> @@ -1935,7 +1944,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,
>> }
>>
>> ret = get_inode_info(sctx->send_root, ow_inode, NULL, &gen, NULL, NULL,
>> - NULL, NULL);
>> + NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>>
>> @@ -2502,6 +2511,39 @@ static int send_chown(struct send_ctx *sctx, u64 ino, u64 gen, u64 uid, u64 gid)
>> return ret;
>> }
>>
>> +static int send_chattr(struct send_ctx *sctx, u64 ino, u64 gen, u64 flags)
>> +{
>> + struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
>> + int ret = 0;
>> + int __flags;
>> + struct fs_path *p;
>> +
>> + __flags = btrfs_flags_to_ioctl(flags);
>> +
>> + btrfs_debug(fs_info, "send_chattr %llu flags=%llu", ino, flags);
>> +
>> + p = fs_path_alloc();
>> + if (!p)
>> + return -ENOMEM;
>> +
>> + ret = begin_cmd(sctx, BTRFS_SEND_C_CHATTR);
>> + if (ret < 0)
>> + goto out;
>> +
>> + ret = get_cur_path(sctx, ino, gen, p);
>> + if (ret < 0)
>> + goto out;
>> + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
>> + TLV_PUT_U64(sctx, BTRFS_SEND_A_CHATTR, __flags);
>> +
>> + ret = send_cmd(sctx);
>> +
>> +tlv_put_failure:
>> +out:
>> + fs_path_free(p);
>> + return ret;
>> +}
>> +
>> static int send_utimes(struct send_ctx *sctx, u64 ino, u64 gen)
>> {
>> struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
>> @@ -2583,7 +2625,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)
>>
>> if (ino != sctx->cur_ino) {
>> ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode,
>> - NULL, NULL, &rdev);
>> + NULL, NULL, &rdev, NULL);
>> if (ret < 0)
>> goto out;
>> } else {
>> @@ -3299,7 +3341,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
>> * The parent inode might have been deleted in the send snapshot
>> */
>> ret = get_inode_info(sctx->send_root, cur->dir, NULL,
>> - NULL, NULL, NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL, NULL, NULL);
>> if (ret == -ENOENT) {
>> ret = 0;
>> continue;
>> @@ -3469,11 +3511,11 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
>> }
>>
>> ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
>> - &left_gen, NULL, NULL, NULL, NULL);
>> + &left_gen, NULL, NULL, NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>> ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
>> - &right_gen, NULL, NULL, NULL, NULL);
>> + &right_gen, NULL, NULL, NULL, NULL, NULL);
>> if (ret < 0) {
>> if (ret == -ENOENT)
>> ret = 0;
>> @@ -3617,7 +3659,7 @@ static int is_ancestor(struct btrfs_root *root,
>> }
>>
>> ret = get_inode_info(root, parent, NULL, &parent_gen,
>> - NULL, NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>> ret = check_ino_in_path(root, ino1, ino1_gen,
>> @@ -3707,7 +3749,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
>>
>> ret = get_inode_info(sctx->parent_root, ino, NULL,
>> &parent_ino_gen, NULL, NULL, NULL,
>> - NULL);
>> + NULL, NULL);
>> if (ret < 0)
>> goto out;
>> if (ino_gen == parent_ino_gen) {
>> @@ -4184,7 +4226,7 @@ static int record_ref(struct btrfs_root *root, u64 dir, struct fs_path *name,
>> return -ENOMEM;
>>
>> ret = get_inode_info(root, dir, NULL, &gen, NULL, NULL,
>> - NULL, NULL);
>> + NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>>
>> @@ -4272,7 +4314,7 @@ static int __find_iref(int num, u64 dir, int index,
>> * else matches.
>> */
>> ret = get_inode_info(ctx->root, dir, NULL, &dir_gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret)
>> return ret;
>> if (dir_gen != ctx->dir_gen)
>> @@ -4316,7 +4358,7 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
>> struct send_ctx *sctx = ctx;
>>
>> ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret)
>> return ret;
>>
>> @@ -4339,7 +4381,7 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
>> struct send_ctx *sctx = ctx;
>>
>> ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret)
>> return ret;
>>
>> @@ -4915,7 +4957,7 @@ static int send_clone(struct send_ctx *sctx,
>>
>> if (clone_root->root == sctx->send_root) {
>> ret = get_inode_info(sctx->send_root, clone_root->ino, NULL,
>> - &gen, NULL, NULL, NULL, NULL);
>> + &gen, NULL, NULL, NULL, NULL, NULL);
>> if (ret < 0)
>> goto out;
>> ret = get_cur_path(sctx, clone_root->ino, gen, p);
>> @@ -5777,9 +5819,11 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>> u64 right_mode;
>> u64 right_uid;
>> u64 right_gid;
>> + u64 left_flags;
>> int need_chmod = 0;
>> int need_chown = 0;
>> int need_truncate = 1;
>> + int need_chattr = 0;
>> int pending_move = 0;
>> int refs_processed = 0;
>>
>> @@ -5787,7 +5831,6 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>> &refs_processed);
>> if (ret < 0)
>> goto out;
>> -
>> /*
>> * We have processed the refs and thus need to advance send_progress.
>> * Now, calls to get_cur_xxx will take the updated refs of the current
>> @@ -5805,11 +5848,67 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>>
>> if (sctx->cur_ino == 0 || sctx->cur_inode_deleted)
>> goto out;
>> +
>> + /*
>> + * If possible, we want to know what flags are set for this inode on the
>> + * receiving end.
>> + */
>> + if (sctx->parent_root && !sctx->receive_flags_valid) {
>> + ret = get_inode_info(sctx->parent_root, sctx->cur_ino, NULL,
>> + NULL, NULL, NULL, NULL, NULL,
>> + &sctx->cur_inode_receive_flags);
>> + if (ret < 0)
>> + goto out;
>> + sctx->receive_flags_valid = 1;
>> + }
>> +
>> + /*
>> + * The change is going to modify data and the inode already exists
>> + * !at_end prevents unnecessary chattr.
>> + */
>> + if (!at_end && sctx->parent_root && !sctx->cur_inode_new &&
>> + (sctx->cmp_key->type == BTRFS_EXTENT_DATA_KEY ||
>> + sctx->cmp_key->type == BTRFS_XATTR_ITEM_KEY)) {
>> +
>> + ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
>> + NULL, NULL, NULL, NULL, &left_flags);
>> + if (ret < 0)
>> + goto out;
>> + /*
>> + * We check against the receive flags first; then check against
>> + * the left flags to see if we can save a chattr later on
>> + */
>> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_IMMUTABLE) {
>> + sctx->cur_inode_flip_flags |= (left_flags &
>> + BTRFS_INODE_IMMUTABLE);
>> + left_flags &= ~BTRFS_INODE_IMMUTABLE;
>> + need_chattr = 1;
>> + }
>> + if (sctx->cur_inode_receive_flags & BTRFS_INODE_APPEND) {
>> + sctx->cur_inode_flip_flags |= (left_flags &
>> + BTRFS_INODE_APPEND);
>> + left_flags &= ~BTRFS_INODE_APPEND;
>> + need_chattr = 1;
>> + }
>> + if (need_chattr) {
>> + need_chattr = 0;
>> + ret = send_chattr(sctx, sctx->cur_ino,
>> + sctx->cur_inode_gen, left_flags);
>> + if (ret < 0)
>> + goto out;
>> + /*
>> + * left_flags is now an accurate rep of what the
>> + * receiving inode's flags are
>> + */
>> + sctx->cur_inode_receive_flags = left_flags;
>> + }
>> + }
>> +
>> if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)
>> goto out;
>>
>> ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL,
>> - &left_mode, &left_uid, &left_gid, NULL);
>> + &left_mode, &left_uid, &left_gid, NULL, &left_flags);
>> if (ret < 0)
>> goto out;
>>
>> @@ -5819,12 +5918,14 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>> need_chmod = 1;
>> if (sctx->cur_inode_next_write_offset == sctx->cur_inode_size)
>> need_truncate = 0;
>> + if (left_flags)
>> + need_chattr = 1;
>> } else {
>> u64 old_size;
>>
>> ret = get_inode_info(sctx->parent_root, sctx->cur_ino,
>> &old_size, NULL, &right_mode, &right_uid,
>> - &right_gid, NULL);
>> + &right_gid, NULL, NULL);
>> if (ret < 0)
>> goto out;
>>
>> @@ -5897,6 +5998,27 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
>> goto out;
>> }
>>
>> + /*
>> + * At this point, if we toggled stuff earlier, untoggle it
>> + * force a chattr and fix the flags
>> + */
>> + if (sctx->cur_inode_flip_flags) {
>> + left_flags |= sctx->cur_inode_flip_flags;
>> + }
>> +
>> + /*
>> + * We either need a chattr because this inode is new, or we need to make
>> + * a change due to a discrepancy between left_flags and receive_flags
>> + */
>> + if (need_chattr || (sctx->cur_inode_receive_flags != left_flags)) {
>> + ret = send_chattr(sctx, sctx->cur_ino, sctx->cur_inode_gen,
>> + left_flags);
>> + if (ret < 0)
>> + goto out;
>> + }
>> + sctx->cur_inode_flip_flags = 0;
>> + sctx->cur_inode_receive_flags = 0;
>> + sctx->receive_flags_valid = 0;
>> out:
>> return ret;
>> }
>> @@ -6198,12 +6320,12 @@ static int dir_changed(struct send_ctx *sctx, u64 dir)
>> int ret;
>>
>> ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL,
>> - NULL, NULL);
>> + NULL, NULL, NULL);
>> if (ret)
>> return ret;
>>
>> ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL,
>> - NULL, NULL, NULL);
>> + NULL, NULL, NULL, NULL);
>> if (ret)
>> return ret;
>>
>> diff --git a/fs/btrfs/send.h b/fs/btrfs/send.h
>> index ead397f7034f..b6e6dcc3db70 100644
>> --- a/fs/btrfs/send.h
>> +++ b/fs/btrfs/send.h
>> @@ -77,6 +77,7 @@ enum btrfs_send_cmd {
>>
>> BTRFS_SEND_C_END,
>> BTRFS_SEND_C_UPDATE_EXTENT,
>> + BTRFS_SEND_C_CHATTR,
>> __BTRFS_SEND_C_MAX,
>> };
>> #define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
>> @@ -114,6 +115,7 @@ enum {
>> BTRFS_SEND_A_CLONE_PATH,
>> BTRFS_SEND_A_CLONE_OFFSET,
>> BTRFS_SEND_A_CLONE_LEN,
>> + BTRFS_SEND_A_CHATTR,
>>
>> __BTRFS_SEND_A_MAX,
>> };
>> --
>> 2.17.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2018-04-20 17:33 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-04-17 23:39 [PATCH] btrfs: add chattr support for send/receive Howard McLauchlan
2018-04-17 23:39 ` [PATCH] btrfs-progs: " Howard McLauchlan
2018-04-17 23:49 ` [PATCH] btrfs: " Howard McLauchlan
2018-04-18 8:15 ` Filipe Manana
2018-04-20 17:33 ` Howard McLauchlan
2018-04-18 9:47 ` David Sterba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox