@@ -82,6 +82,8 @@ struct btrfs_receive
bool force_decompress;
+ bool self_contained;
+
#if COMPRESSION_ZSTD
/* Reuse stream objects for encoded_write decompression fallback */
ZSTD_DStream *zstd_dstream;
@@ -1518,11 +1520,13 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
}
root_subvol_path[0] = 0;
- ret = btrfs_subvolid_resolve(rctx->mnt_fd, root_subvol_path,
- PATH_MAX, subvol_id);
- if (ret) {
- error("cannot resolve our subvol path");
- goto out;
+ if (!rctx->self_contained) {
+ ret = btrfs_subvolid_resolve(rctx->mnt_fd, root_subvol_path,
+ PATH_MAX, subvol_id);
+ if (ret) {
+ error("cannot resolve our subvol path");
+ goto out;
+ }
}
/*
@@ -1645,6 +1649,9 @@ static const char * const cmd_receive_usage[] = {
"this file system is mounted."),
OPTLINE("--force-decompress", "if the stream contains compressed data, always "
"decompress it instead of writing it with encoded I/O"),
+ OPTLINE("--self-contained", "don't resolve snapshot and reflink sources "
+ "relative to the true FS root, only use what is visible to the fs "
+ "as mounted."),
OPTLINE("--dump", "dump stream metadata, one line per operation, "
"does not require the MOUNT parameter"),
OPTLINE("-v", "deprecated, alias for global -v option"),
@@ -1705,6 +1712,7 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
enum {
GETOPT_VAL_DUMP = GETOPT_VAL_FIRST,
GETOPT_VAL_FORCE_DECOMPRESS,
+ GETOPT_VAL_SELF_CONTAINED,
};
static const struct option long_opts[] = {
{ "max-errors", required_argument, NULL, 'E' },
@@ -1712,6 +1720,7 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
{ "dump", no_argument, NULL, GETOPT_VAL_DUMP },
{ "quiet", no_argument, NULL, 'q' },
{ "force-decompress", no_argument, NULL, GETOPT_VAL_FORCE_DECOMPRESS },
+ { "self-contained", no_argument, NULL, GETOPT_VAL_SELF_CONTAINED },
{ NULL, 0, NULL, 0 }
};
@@ -1757,6 +1766,9 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
case GETOPT_VAL_FORCE_DECOMPRESS:
rctx.force_decompress = true;
break;
+ case GETOPT_VAL_SELF_CONTAINED:
+ rctx.self_contained = true;
+ break;
default:
usage_unknown_option(cmd, argv);
}
Currently receive detects the case where it is mounted with -o subvolid and resolves the path to the mounted subvolume from the root subvolume. This path is then used to find the absolute paths for sources of data/metadata for snapshots and reflinks. Another interpretation of a sendstream is more divorced from the source filesystem and aims to interpret it in a more self contained way, using the filesystem as visible to the user. Think usecases in a chroot, user namespace, mount namespace, etc. applying a generically crafted sendstream that will apply successfully in the local context. For such receives, the complexity of resolving this path is unhelpful. First of all, the backref resolution using inode->subvol resolution uses CAP_SYS_ADMIN, so it cannot be done in a user namespace. Even if you could overcome this technically, and the receiving user had permissions higher up the chain of dirs/subvols back to the root subvol, it's still unhelpful if you want to do reflinks/snapshots relative to the paths in the receiving subvolume, not the global paths. Therefore, provide an option for such a self contained mode: --self-contained Signed-off-by: Boris Burkov <boris@bur.io> --- Changelog v2: - actually add the SELF_CONTAINED option to the enum; re-test a non hacked up version for once.. Sorry! cmds/receive.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)