btrfs-progs: dump-tree: add option to print children nodes of a given block
[platform/upstream/btrfs-progs.git] / cmds-receive.c
index 85a6eea..68123a3 100644 (file)
@@ -49,6 +49,8 @@
 #include "send.h"
 #include "send-stream.h"
 #include "send-utils.h"
+#include "send-dump.h"
+#include "help.h"
 
 static int g_verbose = 0;
 
@@ -286,13 +288,16 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
        parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid,
                                           parent_ctransid, NULL,
                                           subvol_search_by_received_uuid);
-       if (!parent_subvol) {
+       if (IS_ERR_OR_NULL(parent_subvol)) {
                parent_subvol = subvol_uuid_search(&rctx->sus, 0, parent_uuid,
                                                   parent_ctransid, NULL,
                                                   subvol_search_by_uuid);
        }
-       if (!parent_subvol) {
-               ret = -ENOENT;
+       if (IS_ERR_OR_NULL(parent_subvol)) {
+               if (!parent_subvol)
+                       ret = -ENOENT;
+               else
+                       ret = PTR_ERR(parent_subvol);
                error("cannot find parent subvolume");
                goto out;
        }
@@ -310,8 +315,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
                sub_len = strlen(parent_subvol->path);
 
                /* First make sure the parent subvol is actually in our path */
-               if (sub_len < root_len ||
-                   strstr(parent_subvol->path, rctx->full_root_path) == NULL) {
+               if (strstr(parent_subvol->path, rctx->full_root_path) != parent_subvol->path ||
+                   (sub_len > root_len && parent_subvol->path[root_len] != '/')) {
                        error(
                "parent subvol is not reachable from inside the root subvol");
                        ret = -ENOENT;
@@ -319,7 +324,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
                }
 
                if (sub_len == root_len) {
-                       parent_subvol->path[0] = '/';
+                       parent_subvol->path[0] = '.';
                        parent_subvol->path[1] = '\0';
                } else {
                        /*
@@ -749,13 +754,16 @@ static int process_clone(const char *path, u64 offset, u64 len,
        si = subvol_uuid_search(&rctx->sus, 0, clone_uuid, clone_ctransid,
                                NULL,
                                subvol_search_by_received_uuid);
-       if (!si) {
+       if (IS_ERR_OR_NULL(si)) {
                if (memcmp(clone_uuid, rctx->cur_subvol.received_uuid,
                                BTRFS_UUID_SIZE) == 0) {
                        /* TODO check generation of extent */
                        subvol_path = strdup(rctx->cur_subvol_path);
                } else {
-                       ret = -ENOENT;
+                       if (!si)
+                               ret = -ENOENT;
+                       else
+                               ret = PTR_ERR(si);
                        error("clone: did not find source subvol");
                        goto out;
                }
@@ -775,7 +783,24 @@ static int process_clone(const char *path, u64 offset, u64 len,
                                                r->subvol_parent_name);
                        }
                }*/
-               subvol_path = strdup(si->path);
+
+               /* strip the subvolume that we are receiving to from the start of subvol_path */
+               if (rctx->full_root_path) {
+                       size_t root_len = strlen(rctx->full_root_path);
+                       size_t sub_len = strlen(si->path);
+
+                       if (sub_len > root_len &&
+                           strstr(si->path, rctx->full_root_path) == si->path &&
+                           si->path[root_len] == '/') {
+                               subvol_path = strdup(si->path + root_len + 1);
+                       } else {
+                               error("clone: source subvol path %s unreachable from %s",
+                                       si->path, rctx->full_root_path);
+                               goto out;
+                       }
+               } else {
+                       subvol_path = strdup(si->path);
+               }
        }
 
        ret = path_cat_out(full_clone_path, subvol_path, clone_path);
@@ -1066,7 +1091,7 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
        char *dest_dir_full_path;
        char root_subvol_path[PATH_MAX];
        int end = 0;
-       int count;
+       int iterations = 0;
 
        dest_dir_full_path = realpath(tomnt, NULL);
        if (!dest_dir_full_path) {
@@ -1161,7 +1186,6 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
        if (ret < 0)
                goto out;
 
-       count = 0;
        while (!end) {
                if (rctx->cached_capabilities_len) {
                        if (g_verbose >= 3)
@@ -1175,22 +1199,28 @@ static int do_receive(struct btrfs_receive *rctx, const char *tomnt,
                                                         rctx,
                                                         rctx->honor_end_cmd,
                                                         max_errors);
-               if (ret < 0)
-                       goto out;
-               /* Empty stream is invalid */
-               if (ret && count == 0) {
-                       error("empty stream is not considered valid");
-                       ret = -EINVAL;
-                       goto out;
+               if (ret < 0) {
+                       if (ret != -ENODATA)
+                               goto out;
+
+                       /* Empty stream is invalid */
+                       if (iterations == 0) {
+                               error("empty stream is not considered valid");
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       ret = 1;
                }
-               count++;
-               if (ret)
+               if (ret > 0)
                        end = 1;
 
                close_inode_for_write(rctx);
                ret = finish_subvol(rctx);
                if (ret < 0)
                        goto out;
+
+               iterations++;
        }
        ret = 0;
 
@@ -1226,6 +1256,7 @@ int cmd_receive(int argc, char **argv)
        struct btrfs_receive rctx;
        int receive_fd = fileno(stdin);
        u64 max_errors = 1;
+       int dump = 0;
        int ret = 0;
 
        memset(&rctx, 0, sizeof(rctx));
@@ -1238,13 +1269,15 @@ int cmd_receive(int argc, char **argv)
 
        while (1) {
                int c;
+               enum { GETOPT_VAL_DUMP = 257 };
                static const struct option long_opts[] = {
                        { "max-errors", required_argument, NULL, 'E' },
                        { "chroot", no_argument, NULL, 'C' },
+                       { "dump", no_argument, NULL, GETOPT_VAL_DUMP },
                        { NULL, 0, NULL, 0 }
                };
 
-               c = getopt_long(argc, argv, "Cevf:m:", long_opts, NULL);
+               c = getopt_long(argc, argv, "Cevf:m:E:", long_opts, NULL);
                if (c < 0)
                        break;
 
@@ -1277,6 +1310,9 @@ int cmd_receive(int argc, char **argv)
                                goto out;
                        }
                        break;
+               case GETOPT_VAL_DUMP:
+                       dump = 1;
+                       break;
                case '?':
                default:
                        error("receive args invalid");
@@ -1284,7 +1320,9 @@ int cmd_receive(int argc, char **argv)
                }
        }
 
-       if (check_argc_exact(argc - optind, 1))
+       if (dump && check_argc_exact(argc - optind, 0))
+               usage(cmd_receive_usage);
+       if (!dump && check_argc_exact(argc - optind, 1))
                usage(cmd_receive_usage);
 
        tomnt = argv[optind];
@@ -1292,47 +1330,61 @@ int cmd_receive(int argc, char **argv)
        if (fromfile[0]) {
                receive_fd = open(fromfile, O_RDONLY | O_NOATIME);
                if (receive_fd < 0) {
-                       error("cannot open %s: %s", fromfile, strerror(errno));
+                       error("cannot open %s: %m", fromfile);
                        goto out;
                }
        }
 
-       ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors);
+       if (dump) {
+               struct btrfs_dump_send_args dump_args;
+
+               dump_args.root_path[0] = '.';
+               dump_args.root_path[1] = '\0';
+               dump_args.full_subvol_path[0] = '.';
+               dump_args.full_subvol_path[1] = '\0';
+               ret = btrfs_read_and_process_send_stream(receive_fd,
+                               &btrfs_print_send_ops, &dump_args, 0, 0);
+               if (ret < 0)
+                       error("failed to dump the send stream: %s",
+                             strerror(-ret));
+       } else {
+               ret = do_receive(&rctx, tomnt, realmnt, receive_fd, max_errors);
+       }
+
        if (receive_fd != fileno(stdin))
                close(receive_fd);
-
 out:
 
        return !!ret;
 }
 
 const char * const cmd_receive_usage[] = {
-       "btrfs receive [-ve] [-f <infile>] [--max-errors <N>] <mount>",
-       "Receive subvolumes from stdin.",
+       "btrfs receive [options] <mount>\n"
+       "btrfs receive --dump [options]",
+       "Receive subvolumes from a stream",
        "Receives one or more subvolumes that were previously",
        "sent with btrfs send. The received subvolumes are stored",
-       "into <mount>.",
-       "btrfs receive will fail in case a receiving subvolume",
+       "into MOUNT.",
+       "The receive will fail in case the receiving subvolume",
        "already exists. It will also fail in case a previously",
-       "received subvolume was changed after it was received.",
+       "received subvolume has been changed after it was received.",
        "After receiving a subvolume, it is immediately set to",
-       "read only.\n",
-       "-v               Enable verbose debug output. Each",
-       "                 occurrence of this option increases the",
-       "                 verbose level more.",
-       "-f <infile>      By default, btrfs receive uses stdin",
-       "                 to receive the subvolumes. Use this",
-       "                 option to specify a file to use instead.",
-       "-e               Terminate after receiving an <end cmd>",
-       "                 in the data stream. Without this option,",
-       "                 the receiver terminates only if an error",
-       "                 is recognized or on EOF.",
+       "read-only.",
+       "",
+       "-v               increase verbosity about performed actions",
+       "-f FILE          read the stream from FILE instead of stdin",
+       "-e               terminate after receiving an <end cmd> marker in the stream.",
+       "                 Without this option the receiver side terminates only in case",
+       "                 of an error on end of file.",
        "-C|--chroot      confine the process to <mount> using chroot",
-       "--max-errors <N> Terminate as soon as N errors happened while",
-       "                 processing commands from the send stream.",
+       "-E|--max-errors NERR",
+       "                 terminate as soon as NERR errors occur while",
+       "                 stream processing commands from the stream.",
        "                 Default value is 1. A value of 0 means no limit.",
-       "-m <mountpoint>  The root mount point of the destination fs.",
-       "                 If you do not have /proc use this to tell us where ",
+       "-m ROOTMOUNT     the root mount point of the destination filesystem.",
+       "                 If /proc is not accessible, use this to tell us where",
        "                 this file system is mounted.",
+       "--dump           dump stream metadata, one line per operation,",
+       "                 does not require the MOUNT parameter",
        NULL
 };