btrfs-progs: image: close all_devices at the end
[platform/upstream/btrfs-progs.git] / cmds-send.c
index dcb6607..581b25e 100644 (file)
@@ -16,7 +16,6 @@
  * Boston, MA 021110-1307, USA.
  */
 
-#define _GNU_SOURCE
 
 #include "kerncompat.h"
 
@@ -32,8 +31,9 @@
 #include <libgen.h>
 #include <mntent.h>
 #include <assert.h>
-
+#include <getopt.h>
 #include <uuid/uuid.h>
+#include <limits.h>
 
 #include "ctree.h"
 #include "ioctl.h"
@@ -129,7 +129,10 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
                parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
                                0, NULL, subvol_search_by_root_id);
 
-               assert(parent2);
+               if (!parent2) {
+                       ret = -ENOENT;
+                       goto out;
+               }
                tmp = parent2->ctransid - parent->ctransid;
                if (tmp < 0)
                        tmp *= -1;
@@ -169,11 +172,21 @@ out:
        return ret;
 }
 
-static void add_clone_source(struct btrfs_send *s, u64 root_id)
+static int add_clone_source(struct btrfs_send *s, u64 root_id)
 {
+       void *tmp;
+
+       tmp = s->clone_sources;
        s->clone_sources = realloc(s->clone_sources,
                sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
+
+       if (!s->clone_sources) {
+               free(tmp);
+               return -ENOMEM;
+       }
        s->clone_sources[s->clone_sources_count++] = root_id;
+
+       return 0;
 }
 
 static int write_buf(int fd, const void *buf, int size)
@@ -185,13 +198,13 @@ static int write_buf(int fd, const void *buf, int size)
                ret = write(fd, (char*)buf + pos, size - pos);
                if (ret < 0) {
                        ret = -errno;
-                       fprintf(stderr, "ERROR: failed to dump stream. %s",
+                       fprintf(stderr, "ERROR: failed to dump stream. %s\n",
                                        strerror(-ret));
                        goto out;
                }
                if (!ret) {
                        ret = -EIO;
-                       fprintf(stderr, "ERROR: failed to dump stream. %s",
+                       fprintf(stderr, "ERROR: failed to dump stream. %s\n",
                                        strerror(-ret));
                        goto out;
                }
@@ -236,11 +249,11 @@ out:
 }
 
 static int do_send(struct btrfs_send *send, u64 parent_root_id,
-                  int is_first_subvol, int is_last_subvol, char *subvol)
+                  int is_first_subvol, int is_last_subvol, char *subvol,
+                  u64 flags)
 {
        int ret;
        pthread_t t_read;
-       pthread_attr_t t_attr;
        struct btrfs_ioctl_send_args io_send;
        void *t_err = NULL;
        int subvol_fd = -1;
@@ -254,8 +267,6 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
                goto out;
        }
 
-       ret = pthread_attr_init(&t_attr);
-
        ret = pipe(pipefd);
        if (ret < 0) {
                ret = -errno;
@@ -268,7 +279,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
        send->send_fd = pipefd[0];
 
        if (!ret)
-               ret = pthread_create(&t_read, &t_attr, dump_thread,
+               ret = pthread_create(&t_read, NULL, dump_thread,
                                        send);
        if (ret) {
                ret = -ret;
@@ -277,6 +288,7 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
                goto out;
        }
 
+       io_send.flags = flags;
        io_send.clone_sources = (__u64*)send->clone_sources;
        io_send.clone_sources_count = send->clone_sources_count;
        io_send.parent_root = parent_root_id;
@@ -317,8 +329,6 @@ static int do_send(struct btrfs_send *send, u64 parent_root_id,
                goto out;
        }
 
-       pthread_attr_destroy(&t_attr);
-
        ret = 0;
 
 out:
@@ -351,9 +361,17 @@ static int init_root_path(struct btrfs_send *s, const char *subvol)
 
        ret = find_mount_root(subvol, &s->root_path);
        if (ret < 0) {
+               fprintf(stderr,
+                       "ERROR: failed to determine mount point for %s: %s\n",
+                       subvol, strerror(-ret));
+               ret = -EINVAL;
+               goto out;
+       }
+       if (ret > 0) {
+               fprintf(stderr,
+                       "ERROR: %s doesn't belong to btrfs mount point\n",
+                       subvol);
                ret = -EINVAL;
-               fprintf(stderr, "ERROR: failed to determine mount point "
-                               "for %s\n", subvol);
                goto out;
        }
 
@@ -414,22 +432,32 @@ out:
 int cmd_send(int argc, char **argv)
 {
        char *subvol = NULL;
-       int c;
        int ret;
-       char *outname = NULL;
+       char outname[PATH_MAX];
        struct btrfs_send send;
        u32 i;
        char *mount_root = NULL;
        char *snapshot_parent = NULL;
-       u64 root_id;
+       u64 root_id = 0;
        u64 parent_root_id = 0;
        int full_send = 1;
        int new_end_cmd_semantic = 0;
+       u64 send_flags = 0;
 
        memset(&send, 0, sizeof(send));
        send.dump_fd = fileno(stdout);
+       outname[0] = 0;
+
+       while (1) {
+               enum { GETOPT_VAL_SEND_NO_DATA = 256 };
+               static const struct option long_options[] = {
+                       { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
+               };
+               int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
+
+               if (c < 0)
+                       break;
 
-       while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
                switch (c) {
                case 'v':
                        g_verbose++;
@@ -469,7 +497,11 @@ int cmd_send(int argc, char **argv)
                                goto out;
                        }
 
-                       add_clone_source(&send, root_id);
+                       ret = add_clone_source(&send, root_id);
+                       if (ret < 0) {
+                               fprintf(stderr, "ERROR: not enough memory\n");
+                               goto out;
+                       }
                        subvol_uuid_search_finit(&send.sus);
                        free(subvol);
                        subvol = NULL;
@@ -482,7 +514,13 @@ int cmd_send(int argc, char **argv)
                        full_send = 0;
                        break;
                case 'f':
-                       outname = optarg;
+                       if (arg_copy_path(outname, optarg, sizeof(outname))) {
+                               fprintf(stderr,
+                                   "ERROR: output file path too long (%zu)\n",
+                                   strlen(optarg));
+                               ret = 1;
+                               goto out;
+                       }
                        break;
                case 'p':
                        if (snapshot_parent) {
@@ -516,6 +554,9 @@ int cmd_send(int argc, char **argv)
                                "ERROR: -i was removed, use -c instead\n");
                        ret = 1;
                        goto out;
+               case GETOPT_VAL_SEND_NO_DATA:
+                       send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
+                       break;
                case '?':
                default:
                        fprintf(stderr, "ERROR: send args invalid.\n");
@@ -524,10 +565,10 @@ int cmd_send(int argc, char **argv)
                }
        }
 
-       if (optind == argc)
+       if (check_argc_min(argc - optind, 1))
                usage(cmd_send_usage);
 
-       if (outname != NULL) {
+       if (outname[0]) {
                send.dump_fd = creat(outname, 0600);
                if (send.dump_fd == -1) {
                        ret = -errno;
@@ -569,7 +610,11 @@ int cmd_send(int argc, char **argv)
                        goto out;
                }
 
-               add_clone_source(&send, parent_root_id);
+               ret = add_clone_source(&send, parent_root_id);
+               if (ret < 0) {
+                       fprintf(stderr, "ERROR: not enough memory\n");
+                       goto out;
+               }
        }
 
        for (i = optind; i < argc; i++) {
@@ -588,6 +633,13 @@ int cmd_send(int argc, char **argv)
                                strerror(-ret));
                        goto out;
                }
+               if (ret > 0) {
+                       fprintf(stderr,
+                       "ERROR: %s doesn't belong to btrfs mount point\n",
+                               subvol);
+                       ret = -EINVAL;
+                       goto out;
+               }
                if (strcmp(send.root_path, mount_root) != 0) {
                        ret = -EINVAL;
                        fprintf(stderr, "ERROR: all subvols must be from the "
@@ -607,6 +659,9 @@ int cmd_send(int argc, char **argv)
                }
        }
 
+       if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
+               printf("Mode NO_FILE_DATA enabled\n");
+
        for (i = optind; i < argc; i++) {
                int is_first_subvol;
                int is_last_subvol;
@@ -653,12 +708,16 @@ int cmd_send(int argc, char **argv)
                        is_last_subvol = 1;
                }
                ret = do_send(&send, parent_root_id, is_first_subvol,
-                             is_last_subvol, subvol);
+                             is_last_subvol, subvol, send_flags);
                if (ret < 0)
                        goto out;
 
                /* done with this subvol, so add it to the clone sources */
-               add_clone_source(&send, root_id);
+               ret = add_clone_source(&send, root_id);
+               if (ret < 0) {
+                       fprintf(stderr, "ERROR: not enough memory\n");
+                       goto out;
+               }
 
                parent_root_id = 0;
                full_send = 0;
@@ -702,5 +761,9 @@ const char * const cmd_send_usage[] = {
        "-f <outfile>     Output is normally written to stdout. To write to",
        "                 a file, use this option. An alternative would be to",
        "                 use pipes.",
+       "--no-data        send in NO_FILE_DATA mode, Note: the output stream",
+       "                 does not contain any file data and thus cannot be used",
+       "                 to transfer changes. This mode is faster and useful to",
+       "                 show the differences in metadata.",
        NULL
 };