Btrfs-progs: remove unsed pthread attribute objects
[platform/upstream/btrfs-progs.git] / cmds-send.c
index 8a0c110..1cd457d 100644 (file)
@@ -18,6 +18,8 @@
 
 #define _GNU_SOURCE
 
+#include "kerncompat.h"
+
 #include <unistd.h>
 #include <stdint.h>
 #include <dirent.h>
@@ -28,6 +30,8 @@
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <libgen.h>
+#include <mntent.h>
+#include <assert.h>
 
 #include <uuid/uuid.h>
 
@@ -35,6 +39,7 @@
 #include "ioctl.h"
 #include "commands.h"
 #include "list.h"
+#include "utils.h"
 
 #include "send.h"
 #include "send-utils.h"
@@ -53,86 +58,6 @@ struct btrfs_send {
        struct subvol_uuid_search sus;
 };
 
-int find_mount_root(const char *path, char **mount_root)
-{
-       int ret;
-       char *cur;
-       char fsid[BTRFS_FSID_SIZE];
-       int fd;
-       struct stat st;
-       char *tmp;
-       char *dup = NULL;
-
-       struct btrfs_ioctl_fs_info_args args;
-
-       fd = open(path, O_RDONLY | O_NOATIME);
-       if (fd < 0) {
-               ret = -errno;
-               goto out;
-       }
-
-       ret = fstat(fd, &st);
-       if (fd < 0) {
-               ret = -errno;
-               goto out;
-       }
-       if (!S_ISDIR(st.st_mode)) {
-               ret = -ENOTDIR;
-               goto out;
-       }
-
-       ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
-       if (fd < 0) {
-               ret = -errno;
-               goto out;
-       }
-       memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
-       close(fd);
-       fd = -1;
-
-       cur = strdup(path);
-
-       while (1) {
-               dup = strdup(cur);
-               tmp = dirname(dup);
-
-               if (!tmp)
-                       break;
-               fd = open(tmp, O_RDONLY | O_NOATIME);
-               if (fd < 0) {
-                       ret = -errno;
-                       goto out;
-               }
-
-               ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
-               close(fd);
-               fd = -1;
-               if (ret < 0)
-                       break;
-               if (memcmp(fsid, args.fsid, BTRFS_FSID_SIZE) != 0)
-                       break;
-
-               free(cur);
-               cur = strdup(tmp);
-               free(dup);
-               dup = NULL;
-               if (strcmp(cur, "/") == 0)
-                       break;
-               if (strcmp(cur, ".") == 0)
-                       break;
-       }
-
-       ret = 0;
-       *mount_root = realpath(cur, NULL);
-
-out:
-       if (dup)
-               free(dup);
-       if (fd != -1)
-               close(fd);
-       return ret;
-}
-
 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
 {
        struct subvol_info *si;
@@ -142,30 +67,33 @@ static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
        if (!si)
                return -ENOENT;
        *root_id = si->root_id;
+       free(si->path);
+       free(si);
        return 0;
 }
 
 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
 {
+       struct subvol_info *si_tmp;
        struct subvol_info *si;
 
-       si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
+       si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
                        subvol_search_by_root_id);
-       if (!si)
+       if (!si_tmp)
                return NULL;
 
-       si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
+       si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
                        subvol_search_by_uuid);
-       if (!si)
-               return NULL;
+       free(si_tmp->path);
+       free(si_tmp);
        return si;
 }
 
 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
 {
        int ret;
-       struct subvol_info *parent;
-       struct subvol_info *parent2;
+       struct subvol_info *parent = NULL;
+       struct subvol_info *parent2 = NULL;
        struct subvol_info *best_parent = NULL;
        __s64 tmp;
        u64 best_diff = (u64)-1;
@@ -180,24 +108,43 @@ static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
        for (i = 0; i < s->clone_sources_count; i++) {
                if (s->clone_sources[i] == parent->root_id) {
                        best_parent = parent;
+                       parent = NULL;
                        goto out_found;
                }
        }
 
        for (i = 0; i < s->clone_sources_count; i++) {
                parent2 = get_parent(s, s->clone_sources[i]);
-               if (parent2 != parent)
+               if (!parent2)
+                       continue;
+               if (parent2->root_id != parent->root_id) {
+                       free(parent2->path);
+                       free(parent2);
+                       parent2 = NULL;
                        continue;
+               }
 
+               free(parent2->path);
+               free(parent2);
                parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
                                0, NULL, subvol_search_by_root_id);
 
+               assert(parent2);
                tmp = parent2->ctransid - parent->ctransid;
                if (tmp < 0)
                        tmp *= -1;
                if (tmp < best_diff) {
-                       best_parent = parent;
+                       if (best_parent) {
+                               free(best_parent->path);
+                               free(best_parent);
+                       }
+                       best_parent = parent2;
+                       parent2 = NULL;
                        best_diff = tmp;
+               } else {
+                       free(parent2->path);
+                       free(parent2);
+                       parent2 = NULL;
                }
        }
 
@@ -211,6 +158,14 @@ out_found:
        ret = 0;
 
 out:
+       if (parent) {
+               free(parent->path);
+               free(parent);
+       }
+       if (best_parent) {
+               free(best_parent->path);
+               free(best_parent);
+       }
        return ret;
 }
 
@@ -280,36 +235,24 @@ out:
        return ERR_PTR(ret);
 }
 
-static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
+static int do_send(struct btrfs_send *send, u64 parent_root_id,
+                  int is_first_subvol, int is_last_subvol, char *subvol)
 {
        int ret;
        pthread_t t_read;
-       pthread_attr_t t_attr;
        struct btrfs_ioctl_send_args io_send;
-       struct subvol_info *si;
        void *t_err = NULL;
        int subvol_fd = -1;
-       int pipefd[2];
+       int pipefd[2] = {-1, -1};
 
-       si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
-                       subvol_search_by_root_id);
-       if (!si) {
-               ret = -ENOENT;
-               fprintf(stderr, "ERROR: could not find subvol info for %llu",
-                               root_id);
-               goto out;
-       }
-
-       subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
+       subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
        if (subvol_fd < 0) {
                ret = -errno;
-               fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
+               fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
                                strerror(-ret));
                goto out;
        }
 
-       ret = pthread_attr_init(&t_attr);
-
        ret = pipe(pipefd);
        if (ret < 0) {
                ret = -errno;
@@ -317,11 +260,12 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
                goto out;
        }
 
+       memset(&io_send, 0, sizeof(io_send));
        io_send.send_fd = pipefd[1];
        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;
@@ -332,12 +276,19 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
 
        io_send.clone_sources = (__u64*)send->clone_sources;
        io_send.clone_sources_count = send->clone_sources_count;
-       io_send.parent_root = parent_root;
+       io_send.parent_root = parent_root_id;
+       if (!is_first_subvol)
+               io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
+       if (!is_last_subvol)
+               io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
        ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
        if (ret) {
                ret = -errno;
                fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
                        strerror(-ret));
+               if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
+                       fprintf(stderr,
+                               "Try upgrading your kernel or don't use -e.\n");
                goto out;
        }
        if (g_verbose > 0)
@@ -347,7 +298,7 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
                fprintf(stderr, "joining genl thread\n");
 
        close(pipefd[1]);
-       pipefd[1] = 0;
+       pipefd[1] = -1;
 
        ret = pthread_join(t_read, &t_err);
        if (ret) {
@@ -363,26 +314,24 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
                goto out;
        }
 
-       pthread_attr_destroy(&t_attr);
-
        ret = 0;
 
 out:
        if (subvol_fd != -1)
                close(subvol_fd);
-       if (pipefd[0])
+       if (pipefd[0] != -1)
                close(pipefd[0]);
-       if (pipefd[1])
+       if (pipefd[1] != -1)
                close(pipefd[1]);
        return ret;
 }
 
-static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
+char *get_subvol_name(char *mnt, char *full_path)
 {
-       int len = strlen(s->root_path);
+       int len = strlen(mnt);
        if (!len)
                return full_path;
-       if (s->root_path[len - 1] != '/')
+       if (mnt[len - 1] != '/')
                len += 1;
 
        return full_path + len;
@@ -457,10 +406,10 @@ out:
        return ret;
 }
 
-int cmd_send_start(int argc, char **argv)
+int cmd_send(int argc, char **argv)
 {
        char *subvol = NULL;
-       char c;
+       int c;
        int ret;
        char *outname = NULL;
        struct btrfs_send send;
@@ -469,16 +418,21 @@ int cmd_send_start(int argc, char **argv)
        char *snapshot_parent = NULL;
        u64 root_id;
        u64 parent_root_id = 0;
+       int full_send = 1;
+       int new_end_cmd_semantic = 0;
 
        memset(&send, 0, sizeof(send));
        send.dump_fd = fileno(stdout);
 
-       while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
+       while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
                switch (c) {
                case 'v':
                        g_verbose++;
                        break;
-               case 'i': {
+               case 'e':
+                       new_end_cmd_semantic = 1;
+                       break;
+               case 'c':
                        subvol = realpath(optarg, NULL);
                        if (!subvol) {
                                ret = -errno;
@@ -491,21 +445,46 @@ int cmd_send_start(int argc, char **argv)
                        if (ret < 0)
                                goto out;
 
-                       ret = get_root_id(&send, get_subvol_name(&send, subvol),
+                       ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
                                        &root_id);
                        if (ret < 0) {
                                fprintf(stderr, "ERROR: could not resolve "
                                                "root_id for %s\n", subvol);
                                goto out;
                        }
+
+                       ret = is_subvol_ro(&send, subvol);
+                       if (ret < 0)
+                               goto out;
+                       if (!ret) {
+                               ret = -EINVAL;
+                               fprintf(stderr,
+                               "ERROR: cloned subvol %s is not read-only.\n",
+                                       subvol);
+                               goto out;
+                       }
+
                        add_clone_source(&send, root_id);
+                       subvol_uuid_search_finit(&send.sus);
                        free(subvol);
+                       subvol = NULL;
+                       if (send.mnt_fd >= 0) {
+                               close(send.mnt_fd);
+                               send.mnt_fd = -1;
+                       }
+                       free(send.root_path);
+                       send.root_path = NULL;
+                       full_send = 0;
                        break;
-               }
                case 'f':
                        outname = optarg;
                        break;
                case 'p':
+                       if (snapshot_parent) {
+                               fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
+                               ret = 1;
+                               goto out;
+                       }
                        snapshot_parent = realpath(optarg, NULL);
                        if (!snapshot_parent) {
                                ret = -errno;
@@ -513,18 +492,35 @@ int cmd_send_start(int argc, char **argv)
                                                "%s\n", optarg, strerror(-ret));
                                goto out;
                        }
+
+                       ret = is_subvol_ro(&send, snapshot_parent);
+                       if (ret < 0)
+                               goto out;
+                       if (!ret) {
+                               ret = -EINVAL;
+                               fprintf(stderr,
+                                       "ERROR: parent %s is not read-only.\n",
+                                       snapshot_parent);
+                               goto out;
+                       }
+
+                       full_send = 0;
                        break;
+               case 'i':
+                       fprintf(stderr,
+                               "ERROR: -i was removed, use -c instead\n");
+                       ret = 1;
+                       goto out;
                case '?':
                default:
                        fprintf(stderr, "ERROR: send args invalid.\n");
-                       return 1;
+                       ret = 1;
+                       goto out;
                }
        }
 
-       if (optind == argc) {
-               fprintf(stderr, "ERROR: send needs path to snapshot\n");
-               return 1;
-       }
+       if (optind == argc)
+               usage(cmd_send_usage);
 
        if (outname != NULL) {
                send.dump_fd = creat(outname, 0600);
@@ -536,11 +532,20 @@ int cmd_send_start(int argc, char **argv)
                }
        }
 
+       if (isatty(send.dump_fd)) {
+               fprintf(stderr, 
+                       "ERROR: not dumping send stream into a terminal, "
+                       "redirect it into a file\n");
+               ret = 1;
+               goto out;
+       }
+
        /* use first send subvol to determine mount_root */
        subvol = argv[optind];
 
        subvol = realpath(argv[optind], NULL);
        if (!subvol) {
+               ret = -errno;
                fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
                goto out;
        }
@@ -551,7 +556,7 @@ int cmd_send_start(int argc, char **argv)
 
        if (snapshot_parent != NULL) {
                ret = get_root_id(&send,
-                               get_subvol_name(&send, snapshot_parent),
+                               get_subvol_name(send.root_path, snapshot_parent),
                                &parent_root_id);
                if (ret < 0) {
                        fprintf(stderr, "ERROR: could not resolve root_id "
@@ -563,8 +568,10 @@ int cmd_send_start(int argc, char **argv)
        }
 
        for (i = optind; i < argc; i++) {
+               free(subvol);
                subvol = realpath(argv[i], NULL);
                if (!subvol) {
+                       ret = -errno;
                        fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
                        goto out;
                }
@@ -593,10 +600,13 @@ int cmd_send_start(int argc, char **argv)
                                        subvol);
                        goto out;
                }
-               free(subvol);
        }
 
        for (i = optind; i < argc; i++) {
+               int is_first_subvol;
+               int is_last_subvol;
+
+               free(subvol);
                subvol = argv[i];
 
                fprintf(stderr, "At subvol %s\n", subvol);
@@ -609,18 +619,13 @@ int cmd_send_start(int argc, char **argv)
                        goto out;
                }
 
-               ret = get_root_id(&send, get_subvol_name(&send, subvol),
-                               &root_id);
-               if (ret < 0) {
-                       fprintf(stderr, "ERROR: could not resolve root_id "
-                                       "for %s\n", subvol);
-                       goto out;
-               }
-
-               if (!parent_root_id) {
+               if (!full_send && !parent_root_id) {
                        ret = find_good_parent(&send, root_id, &parent_root_id);
-                       if (ret < 0)
-                               parent_root_id = 0;
+                       if (ret < 0) {
+                               fprintf(stderr, "ERROR: parent determination failed for %lld\n",
+                                       root_id);
+                               goto out;
+                       }
                }
 
                ret = is_subvol_ro(&send, subvol);
@@ -633,7 +638,17 @@ int cmd_send_start(int argc, char **argv)
                        goto out;
                }
 
-               ret = do_send(&send, root_id, parent_root_id);
+               if (new_end_cmd_semantic) {
+                       /* require new kernel */
+                       is_first_subvol = (i == optind);
+                       is_last_subvol = (i == argc - 1);
+               } else {
+                       /* be compatible to old and new kernel */
+                       is_first_subvol = 1;
+                       is_last_subvol = 1;
+               }
+               ret = do_send(&send, parent_root_id, is_first_subvol,
+                             is_last_subvol, subvol);
                if (ret < 0)
                        goto out;
 
@@ -641,60 +656,46 @@ int cmd_send_start(int argc, char **argv)
                add_clone_source(&send, root_id);
 
                parent_root_id = 0;
-               free(subvol);
+               full_send = 0;
        }
 
        ret = 0;
 
 out:
+       free(subvol);
+       free(snapshot_parent);
+       free(send.clone_sources);
        if (send.mnt_fd >= 0)
                close(send.mnt_fd);
-       return ret;
+       free(send.root_path);
+       subvol_uuid_search_finit(&send.sus);
+       return !!ret;
 }
 
-static const char * const send_cmd_group_usage[] = {
-       "btrfs send <command> <args>",
-       NULL
-};
-
-static const char * const cmd_send_usage[] = {
-       "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
-       "Send the subvolume to stdout.",
-       "Sends the subvolume specified by <subvol> to stdout.",
-       "By default, this will send the whole subvolume. To do",
-       "an incremental send, one or multiple '-i <clone_source>'",
-       "arguments have to be specified. A 'clone source' is",
-       "a subvolume that is known to exist on the receiving",
-       "side in exactly the same state as on the sending side.\n",
-       "Normally, a good snapshot parent is searched automatically",
-       "in the list of 'clone sources'. To override this, use",
-       "'-p <parent>' to manually specify a snapshot parent.",
-       "A manually specified snapshot parent is also regarded",
-       "as 'clone source'.\n",
-       "-v               Enable verbose debug output. Each",
-       "                 occurrency of this option increases the",
-       "                 verbose level more.",
-       "-i <subvol>      Informs btrfs send that this subvolume,",
-       "                 can be taken as 'clone source'. This can",
-       "                 be used for incremental sends.",
-       "-p <subvol>      Disable automatic snaphot parent",
-       "                 determination and use <subvol> as parent.",
-       "                 This subvolume is also added to the list",
-       "                 of 'clone sources' (see -i).",
-       "-f <outfile>     Output is normally written to stdout.",
-       "                 To write to a file, use this option.",
-       "                 An alternative would be to use pipes.",
+const char * const cmd_send_usage[] = {
+       "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
+       "Send the subvolume(s) to stdout.",
+       "Sends the subvolume(s) specified by <subvol> to stdout.",
+       "By default, this will send the whole subvolume. To do an incremental",
+       "send, use '-p <parent>'. If you want to allow btrfs to clone from",
+       "any additional local snapshots, use '-c <clone-src>' (multiple times",
+       "where applicable). You must not specify clone sources unless you",
+       "guarantee that these snapshots are exactly in the same state on both",
+       "sides, the sender and the receiver. It is allowed to omit the",
+       "'-p <parent>' option when '-c <clone-src>' options are given, in",
+       "which case 'btrfs send' will determine a suitable parent among the",
+       "clone sources itself.",
+       "\n",
+       "-v               Enable verbose debug output. Each occurrence of",
+       "                 this option increases the verbose level more.",
+       "-e               If sending multiple subvols at once, use the new",
+       "                 format and omit the end-cmd between the subvols.",
+       "-p <parent>      Send an incremental stream from <parent> to",
+       "                 <subvol>.",
+       "-c <clone-src>   Use this snapshot as a clone source for an ",
+       "                 incremental send (multiple allowed)",
+       "-f <outfile>     Output is normally written to stdout. To write to",
+       "                 a file, use this option. An alternative would be to",
+       "                 use pipes.",
        NULL
 };
-
-const struct cmd_group send_cmd_group = {
-       send_cmd_group_usage, NULL, {
-               { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
-               { 0, 0, 0, 0, 0 },
-        },
-};
-
-int cmd_send(int argc, char **argv)
-{
-       return cmd_send_start(argc, argv);
-}