btrfs-progs: convert/ext2: Remove check for ext2_ext_attr_entry->e_value_block
[platform/upstream/btrfs-progs.git] / cmds-send.c
index 5e334cb..c5ecdaa 100644 (file)
@@ -43,8 +43,9 @@
 
 #include "send.h"
 #include "send-utils.h"
+#include "help.h"
 
-#define SEND_BUFFER_SIZE       (64 * 1024)
+#define SEND_BUFFER_SIZE       SZ_64K
 
 /*
  * Default is 1 for historical reasons, changing may break scripts that expect
@@ -70,8 +71,12 @@ static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
 
        si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
                        subvol_search_by_path);
-       if (!si)
-               return -ENOENT;
+       if (IS_ERR_OR_NULL(si)) {
+               if (!si)
+                       return -ENOENT;
+               else
+                       return PTR_ERR(si);
+       }
        *root_id = si->root_id;
        free(si->path);
        free(si);
@@ -85,8 +90,8 @@ static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
 
        si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
                        subvol_search_by_root_id);
-       if (!si_tmp)
-               return NULL;
+       if (IS_ERR_OR_NULL(si_tmp))
+               return si_tmp;
 
        si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
                        subvol_search_by_uuid);
@@ -101,13 +106,15 @@ static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
        struct subvol_info *parent = NULL;
        struct subvol_info *parent2 = NULL;
        struct subvol_info *best_parent = NULL;
-       __s64 tmp;
        u64 best_diff = (u64)-1;
        int i;
 
        parent = get_parent(sctx, root_id);
-       if (!parent) {
-               ret = -ENOENT;
+       if (IS_ERR_OR_NULL(parent)) {
+               if (!parent)
+                       ret = -ENOENT;
+               else
+                       ret = PTR_ERR(parent);
                goto out;
        }
 
@@ -120,8 +127,10 @@ static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
        }
 
        for (i = 0; i < sctx->clone_sources_count; i++) {
+               s64 tmp;
+
                parent2 = get_parent(sctx, sctx->clone_sources[i]);
-               if (!parent2)
+               if (IS_ERR_OR_NULL(parent2))
                        continue;
                if (parent2->root_id != parent->root_id) {
                        free(parent2->path);
@@ -135,14 +144,16 @@ static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
                parent2 = subvol_uuid_search(&sctx->sus,
                                sctx->clone_sources[i], NULL, 0, NULL,
                                subvol_search_by_root_id);
-
-               if (!parent2) {
-                       ret = -ENOENT;
+               if (IS_ERR_OR_NULL(parent2)) {
+                       if (!parent2)
+                               ret = -ENOENT;
+                       else
+                               ret = PTR_ERR(parent2);
                        goto out;
                }
                tmp = parent2->ctransid - parent->ctransid;
                if (tmp < 0)
-                       tmp *= -1;
+                       tmp = -tmp;
                if (tmp < best_diff) {
                        if (best_parent) {
                                free(best_parent->path);
@@ -196,6 +207,7 @@ static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
        return 0;
 }
 
+#if 0
 static int write_buf(int fd, const char *buf, size_t size)
 {
        int ret;
@@ -223,7 +235,7 @@ out:
        return ret;
 }
 
-static void *dump_thread(void *arg)
+static void* read_sent_data_copy(void *arg)
 {
        int ret;
        struct btrfs_send *sctx = (struct btrfs_send*)arg;
@@ -235,7 +247,7 @@ static void *dump_thread(void *arg)
                rbytes = read(sctx->send_fd, buf, sizeof(buf));
                if (rbytes < 0) {
                        ret = -errno;
-                       error("failed to read stream from kernel: %s\n",
+                       error("failed to read stream from kernel: %s",
                                strerror(-ret));
                        goto out;
                }
@@ -254,6 +266,37 @@ out:
 
        return ERR_PTR(ret);
 }
+#endif
+
+static void *read_sent_data(void *arg)
+{
+       int ret;
+       struct btrfs_send *sctx = (struct btrfs_send*)arg;
+
+       while (1) {
+               ssize_t sbytes;
+
+               /* Source is a pipe, output is either file or stdout */
+               sbytes = splice(sctx->send_fd, NULL, sctx->dump_fd,
+                               NULL, SEND_BUFFER_SIZE, SPLICE_F_MORE);
+               if (sbytes < 0) {
+                       ret = -errno;
+                       error("failed to read stream from kernel: %s",
+                               strerror(-ret));
+                       goto out;
+               }
+               if (!sbytes) {
+                       ret = 0;
+                       goto out;
+               }
+       }
+
+out:
+       if (ret < 0)
+               exit(-ret);
+
+       return ERR_PTR(ret);
+}
 
 static int do_send(struct btrfs_send *send, u64 parent_root_id,
                   int is_first_subvol, int is_last_subvol, const char *subvol,
@@ -285,8 +328,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, NULL, dump_thread,
-                                       send);
+               ret = pthread_create(&t_read, NULL, read_sent_data, send);
        if (ret) {
                ret = -ret;
                error("thread setup failed: %s", strerror(-ret));
@@ -416,6 +458,37 @@ out:
        return ret;
 }
 
+static int set_root_info(struct btrfs_send *sctx, const char *subvol,
+               u64 *root_id)
+{
+       int ret;
+
+       ret = init_root_path(sctx, subvol);
+       if (ret < 0)
+               goto out;
+
+       ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol),
+               root_id);
+       if (ret < 0) {
+               error("cannot resolve rootid for %s", subvol);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+static void free_send_info(struct btrfs_send *sctx)
+{
+       if (sctx->mnt_fd >= 0) {
+               close(sctx->mnt_fd);
+               sctx->mnt_fd = -1;
+       }
+       free(sctx->root_path);
+       sctx->root_path = NULL;
+       subvol_uuid_search_finit(&sctx->sus);
+}
+
 int cmd_send(int argc, char **argv)
 {
        char *subvol = NULL;
@@ -465,18 +538,10 @@ int cmd_send(int argc, char **argv)
                                goto out;
                        }
 
-                       ret = init_root_path(&send, subvol);
+                       ret = set_root_info(&send, subvol, &root_id);
                        if (ret < 0)
                                goto out;
 
-                       ret = get_root_id(&send,
-                               subvol_strip_mountpoint(send.root_path, subvol),
-                               &root_id);
-                       if (ret < 0) {
-                               error("cannot resolve rootid for %s", subvol);
-                               goto out;
-                       }
-
                        ret = is_subvol_ro(&send, subvol);
                        if (ret < 0)
                                goto out;
@@ -491,15 +556,9 @@ int cmd_send(int argc, char **argv)
                                error("cannot add clone source: %s", strerror(-ret));
                                goto out;
                        }
-                       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;
+                       free_send_info(&send);
                        full_send = 0;
                        break;
                case 'f':
@@ -553,7 +612,20 @@ int cmd_send(int argc, char **argv)
                usage(cmd_send_usage);
 
        if (outname[0]) {
-               send.dump_fd = creat(outname, 0600);
+               int tmpfd;
+
+               /*
+                * Try to use an existing file first. Even if send runs as
+                * root, it might not have permissions to create file (eg. on a
+                * NFS) but it should still be able to use a pre-created file.
+                */
+               tmpfd = open(outname, O_WRONLY | O_TRUNC);
+               if (tmpfd < 0) {
+                       if (errno == ENOENT)
+                               tmpfd = open(outname,
+                                       O_CREAT | O_WRONLY | O_TRUNC, 0600);
+               }
+               send.dump_fd = tmpfd;
                if (send.dump_fd == -1) {
                        ret = -errno;
                        error("cannot create '%s': %s", outname, strerror(-ret));
@@ -655,7 +727,11 @@ int cmd_send(int argc, char **argv)
                        goto out;
                }
 
-               if (!full_send && root_id) {
+               if (!full_send && !snapshot_parent) {
+                       ret = set_root_info(&send, subvol, &root_id);
+                       if (ret < 0)
+                               goto out;
+
                        ret = find_good_parent(&send, root_id, &parent_root_id);
                        if (ret < 0) {
                                error("parent determination failed for %lld",
@@ -678,13 +754,14 @@ int cmd_send(int argc, char **argv)
                if (ret < 0)
                        goto out;
 
-               if (!full_send && root_id) {
+               if (!full_send && !snapshot_parent) {
                        /* 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;
                        }
+                       free_send_info(&send);
                }
        }
 
@@ -694,10 +771,7 @@ out:
        free(subvol);
        free(snapshot_parent);
        free(send.clone_sources);
-       if (send.mnt_fd >= 0)
-               close(send.mnt_fd);
-       free(send.root_path);
-       subvol_uuid_search_finit(&send.sus);
+       free_send_info(&send);
        return !!ret;
 }