X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-send.c;h=07070ad82e220ef6aca33aeafc41a09354e7ef1a;hb=bd2cc320aff5789fe4034736fa6da8b4ebae475f;hp=3d2171518db8bd0e00a4fc90f79b925f916cac85;hpb=387d5f32347e426214389e0251a1240e076d250d;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-send.c b/cmds-send.c index 3d21715..07070ad 100644 --- a/cmds-send.c +++ b/cmds-send.c @@ -16,7 +16,6 @@ * Boston, MA 021110-1307, USA. */ -#define _GNU_SOURCE #include "kerncompat.h" @@ -32,13 +31,15 @@ #include #include #include - +#include #include +#include #include "ctree.h" #include "ioctl.h" #include "commands.h" #include "list.h" +#include "utils.h" #include "send.h" #include "send-utils.h" @@ -57,54 +58,6 @@ struct btrfs_send { struct subvol_uuid_search sus; }; -int find_mount_root(const char *path, char **mount_root) -{ - FILE *mnttab; - int fd; - struct mntent *ent; - int len; - int ret; - int longest_matchlen = 0; - char *longest_match = NULL; - - fd = open(path, O_RDONLY | O_NOATIME); - if (fd < 0) - return -errno; - close(fd); - - mnttab = setmntent("/proc/self/mounts", "r"); - if (!mnttab) - return -errno; - - while ((ent = getmntent(mnttab))) { - len = strlen(ent->mnt_dir); - if (strncmp(ent->mnt_dir, path, len) == 0) { - /* match found */ - if (longest_matchlen < len) { - free(longest_match); - longest_matchlen = len; - longest_match = strdup(ent->mnt_dir); - } - } - } - endmntent(mnttab); - - if (!longest_match) { - fprintf(stderr, - "ERROR: Failed to find mount root for path %s.\n", - path); - return -ENOENT; - } - - ret = 0; - *mount_root = realpath(longest_match, NULL); - if (!*mount_root) - ret = -errno; - - free(longest_match); - return ret; -} - static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id) { struct subvol_info *si; @@ -176,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; @@ -216,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) @@ -232,14 +198,12 @@ 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", - strerror(-ret)); + error("failed to dump stream: %s", strerror(-ret)); goto out; } if (!ret) { ret = -EIO; - fprintf(stderr, "ERROR: failed to dump stream. %s", - strerror(-ret)); + error("failed to dump stream: %s", strerror(-ret)); goto out; } pos += ret; @@ -261,8 +225,8 @@ static void *dump_thread(void *arg_) readed = read(s->send_fd, buf, sizeof(buf)); if (readed < 0) { ret = -errno; - fprintf(stderr, "ERROR: failed to read stream from " - "kernel. %s\n", strerror(-ret)); + error("failed to read stream from kernel: %s\n", + strerror(-ret)); goto out; } if (!readed) { @@ -282,41 +246,28 @@ out: return ERR_PTR(ret); } -static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id, - int is_first_subvol, int is_last_subvol) +static int do_send(struct btrfs_send *send, u64 parent_root_id, + 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; - struct subvol_info *si; void *t_err = NULL; int subvol_fd = -1; 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, - strerror(-ret)); + error("cannot open %s: %s", subvol, strerror(-ret)); goto out; } - ret = pthread_attr_init(&t_attr); - ret = pipe(pipefd); if (ret < 0) { ret = -errno; - fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret)); + error("pipe failed: %s", strerror(-ret)); goto out; } @@ -325,15 +276,15 @@ static int do_send(struct btrfs_send *send, u64 root_id, 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; - fprintf(stderr, "ERROR: thread setup failed: %s\n", - strerror(-ret)); + error("thread setup failed: %s", strerror(-ret)); 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; @@ -342,10 +293,9 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id, if (!is_last_subvol) io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD; ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send); - if (ret) { + if (ret < 0) { ret = -errno; - fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret, - strerror(-ret)); + error("send ioctl failed with %d: %s", ret, strerror(-ret)); if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol)) fprintf(stderr, "Try upgrading your kernel or don't use -e.\n"); @@ -363,19 +313,16 @@ static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id, ret = pthread_join(t_read, &t_err); if (ret) { ret = -ret; - fprintf(stderr, "ERROR: pthread_join failed: %s\n", - strerror(-ret)); + error("pthread_join failed: %s", strerror(-ret)); goto out; } if (t_err) { ret = (long int)t_err; - fprintf(stderr, "ERROR: failed to process send stream, ret=%ld " - "(%s)\n", (long int)t_err, strerror(-ret)); + error("failed to process send stream, ret=%ld (%s)", + (long int)t_err, strerror(-ret)); goto out; } - pthread_attr_destroy(&t_attr); - ret = 0; out: @@ -385,24 +332,9 @@ out: close(pipefd[0]); if (pipefd[1] != -1) close(pipefd[1]); - if (si) { - free(si->path); - free(si); - } return ret; } -char *get_subvol_name(char *mnt, char *full_path) -{ - int len = strlen(mnt); - if (!len) - return full_path; - if (mnt[len - 1] != '/') - len += 1; - - return full_path + len; -} - static int init_root_path(struct btrfs_send *s, const char *subvol) { int ret = 0; @@ -412,24 +344,28 @@ static int init_root_path(struct btrfs_send *s, const char *subvol) ret = find_mount_root(subvol, &s->root_path); if (ret < 0) { + error("failed to determine mount point for %s: %s", + subvol, strerror(-ret)); + ret = -EINVAL; + goto out; + } + if (ret > 0) { + error("%s doesn't belong to btrfs mount point", subvol); ret = -EINVAL; - fprintf(stderr, "ERROR: failed to determine mount point " - "for %s\n", subvol); goto out; } s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME); if (s->mnt_fd < 0) { ret = -errno; - fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path, - strerror(-ret)); + error("cannot open '%s': %s", s->root_path, strerror(-ret)); goto out; } ret = subvol_uuid_search_init(s->mnt_fd, &s->sus); if (ret < 0) { - fprintf(stderr, "ERROR: failed to initialize subvol search. " - "%s\n", strerror(-ret)); + error("failed to initialize subvol search: %s", + strerror(-ret)); goto out; } @@ -447,16 +383,15 @@ static int is_subvol_ro(struct btrfs_send *s, char *subvol) fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME); if (fd < 0) { ret = -errno; - fprintf(stderr, "ERROR: failed to open %s. %s\n", - subvol, strerror(-ret)); + error("cannot open %s: %s", subvol, strerror(-ret)); goto out; } ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags); if (ret < 0) { ret = -errno; - fprintf(stderr, "ERROR: failed to get flags for subvolume. " - "%s\n", strerror(-ret)); + error("failed to get flags for subvolume %s: %s", + subvol, strerror(-ret)); goto out; } @@ -475,22 +410,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++; @@ -502,8 +447,7 @@ int cmd_send(int argc, char **argv) subvol = realpath(optarg, NULL); if (!subvol) { ret = -errno; - fprintf(stderr, "ERROR: realpath %s failed. " - "%s\n", optarg, strerror(-ret)); + error("realpath %s failed: %s\n", optarg, strerror(-ret)); goto out; } @@ -511,14 +455,28 @@ int cmd_send(int argc, char **argv) if (ret < 0) goto out; - ret = get_root_id(&send, get_subvol_name(send.root_path, subvol), - &root_id); + ret = get_root_id(&send, + subvol_strip_mountpoint(send.root_path, subvol), + &root_id); if (ret < 0) { - fprintf(stderr, "ERROR: could not resolve " - "root_id for %s\n", subvol); + error("cannot resolve rootid for %s", subvol); + goto out; + } + + ret = is_subvol_ro(&send, subvol); + if (ret < 0) + goto out; + if (!ret) { + ret = -EINVAL; + error("cloned subvolume %s is not read-only", subvol); + goto out; + } + + ret = add_clone_source(&send, root_id); + if (ret < 0) { + error("cannot add clone source: %s", strerror(-ret)); goto out; } - add_clone_source(&send, root_id); subvol_uuid_search_finit(&send.sus); free(subvol); subvol = NULL; @@ -531,56 +489,67 @@ int cmd_send(int argc, char **argv) full_send = 0; break; case 'f': - outname = optarg; + if (arg_copy_path(outname, optarg, sizeof(outname))) { + error("output file path too long (%zu)", strlen(optarg)); + ret = 1; + goto out; + } break; case 'p': if (snapshot_parent) { - fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n"); + error("you cannot have more than one parent (-p)"); ret = 1; goto out; } snapshot_parent = realpath(optarg, NULL); if (!snapshot_parent) { ret = -errno; - fprintf(stderr, "ERROR: realpath %s failed. " - "%s\n", optarg, strerror(-ret)); + error("realpath %s failed: %s", optarg, strerror(-ret)); + goto out; + } + + ret = is_subvol_ro(&send, snapshot_parent); + if (ret < 0) + goto out; + if (!ret) { + ret = -EINVAL; + error("parent subvolume %s is not read-only", + snapshot_parent); goto out; } + full_send = 0; break; case 'i': - fprintf(stderr, - "ERROR: -i was removed, use -c instead\n"); + error("option -i was removed, use -c instead"); 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"); + error("send arguments invalid"); ret = 1; goto out; } } - if (optind == argc) { - fprintf(stderr, "ERROR: send needs path to snapshot\n"); - ret = 1; - goto out; - } + 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; - fprintf(stderr, "ERROR: can't create '%s': %s\n", - outname, strerror(-ret)); + error("cannot create '%s': %s", outname, strerror(-ret)); goto out; } } if (isatty(send.dump_fd)) { - fprintf(stderr, - "ERROR: not dumping send stream into a terminal, " - "redirect it into a file\n"); + error( + "not dumping send stream into a terminal, redirect it into a file"); ret = 1; goto out; } @@ -591,7 +560,7 @@ int cmd_send(int argc, char **argv) subvol = realpath(argv[optind], NULL); if (!subvol) { ret = -errno; - fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]); + error("unable to resolve %s", argv[optind]); goto out; } @@ -601,15 +570,18 @@ int cmd_send(int argc, char **argv) if (snapshot_parent != NULL) { ret = get_root_id(&send, - get_subvol_name(send.root_path, snapshot_parent), - &parent_root_id); + subvol_strip_mountpoint(send.root_path, snapshot_parent), + &parent_root_id); if (ret < 0) { - fprintf(stderr, "ERROR: could not resolve root_id " - "for %s\n", snapshot_parent); + error("could not resolve rootid for %s", snapshot_parent); goto out; } - add_clone_source(&send, parent_root_id); + ret = add_clone_source(&send, parent_root_id); + if (ret < 0) { + error("cannot add clone source: %s", strerror(-ret)); + goto out; + } } for (i = optind; i < argc; i++) { @@ -617,21 +589,25 @@ int cmd_send(int argc, char **argv) subvol = realpath(argv[i], NULL); if (!subvol) { ret = -errno; - fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]); + error("unable to resolve %s", argv[i]); goto out; } ret = find_mount_root(subvol, &mount_root); if (ret < 0) { - fprintf(stderr, "ERROR: find_mount_root failed on %s: " - "%s\n", subvol, + error("find_mount_root failed on %s: %s", subvol, strerror(-ret)); goto out; } + if (ret > 0) { + error("%s does not belong to btrfs mount point", + subvol); + ret = -EINVAL; + goto out; + } if (strcmp(send.root_path, mount_root) != 0) { ret = -EINVAL; - fprintf(stderr, "ERROR: all subvols must be from the " - "same fs.\n"); + error("all subvolumes must be from the same filesystem"); goto out; } free(mount_root); @@ -641,12 +617,14 @@ int cmd_send(int argc, char **argv) goto out; if (!ret) { ret = -EINVAL; - fprintf(stderr, "ERROR: %s is not read-only.\n", - subvol); + error("subvolume %s is not read-only", subvol); goto out; } } + 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; @@ -659,23 +637,14 @@ int cmd_send(int argc, char **argv) subvol = realpath(subvol, NULL); if (!subvol) { ret = -errno; - fprintf(stderr, "ERROR: realpath %s failed. " - "%s\n", argv[i], strerror(-ret)); - goto out; - } - - 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); + error("realpath %s failed: %s", argv[i], strerror(-ret)); goto out; } if (!full_send && !parent_root_id) { ret = find_good_parent(&send, root_id, &parent_root_id); if (ret < 0) { - fprintf(stderr, "ERROR: parent determination failed for %lld\n", + error("parent determination failed for %lld", root_id); goto out; } @@ -686,8 +655,7 @@ int cmd_send(int argc, char **argv) goto out; if (!ret) { ret = -EINVAL; - fprintf(stderr, "ERROR: %s is not read-only.\n", - subvol); + error("subvolume %s is not read-only", subvol); goto out; } @@ -700,16 +668,21 @@ int cmd_send(int argc, char **argv) is_first_subvol = 1; is_last_subvol = 1; } - ret = do_send(&send, root_id, parent_root_id, - is_first_subvol, is_last_subvol); + ret = do_send(&send, parent_root_id, is_first_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); + if (!full_send) { + /* done with this subvol, so add it to the clone sources */ + ret = add_clone_source(&send, root_id); + if (ret < 0) { + error("cannot add clone source: %s", strerror(-ret)); + goto out; + } + } parent_root_id = 0; - full_send = 0; } ret = 0; @@ -726,9 +699,10 @@ out: } const char * const cmd_send_usage[] = { - "btrfs send [-ve] [-p ] [-c ] [-f ] ", - "Send the subvolume to stdout.", - "Sends the subvolume specified by to stdout.", + "btrfs send [-ve] [-p ] [-c ] [-f ] [...]", + "Send the subvolume(s) to stdout.", + "Sends the subvolume(s) specified by to stdout.", + " should be read-only here.", "By default, this will send the whole subvolume. To do an incremental", "send, use '-p '. If you want to allow btrfs to clone from", "any additional local snapshots, use '-c ' (multiple times", @@ -750,5 +724,9 @@ const char * const cmd_send_usage[] = { "-f 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 };