]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/btrfs/send.c
Merge branch 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty...
[karo-tx-linux.git] / fs / btrfs / send.c
index b0f9df30f24d50e220d8b0257405e89cc95e3853..c7beb543a4a89300f1e586492b767ea8f9bef683 100644 (file)
@@ -386,7 +386,7 @@ static struct btrfs_path *alloc_path_for_send(void)
        return path;
 }
 
-static int write_buf(struct send_ctx *sctx, const void *buf, u32 len)
+int write_buf(struct file *filp, const void *buf, u32 len, loff_t *off)
 {
        int ret;
        mm_segment_t old_fs;
@@ -396,8 +396,7 @@ static int write_buf(struct send_ctx *sctx, const void *buf, u32 len)
        set_fs(KERNEL_DS);
 
        while (pos < len) {
-               ret = vfs_write(sctx->send_filp, (char *)buf + pos, len - pos,
-                               &sctx->send_off);
+               ret = vfs_write(filp, (char *)buf + pos, len - pos, off);
                /* TODO handle that correctly */
                /*if (ret == -ERESTARTSYS) {
                        continue;
@@ -553,7 +552,8 @@ static int send_header(struct send_ctx *sctx)
        strcpy(hdr.magic, BTRFS_SEND_STREAM_MAGIC);
        hdr.version = cpu_to_le32(BTRFS_SEND_STREAM_VERSION);
 
-       return write_buf(sctx, &hdr, sizeof(hdr));
+       return write_buf(sctx->send_filp, &hdr, sizeof(hdr),
+                                       &sctx->send_off);
 }
 
 /*
@@ -590,7 +590,8 @@ static int send_cmd(struct send_ctx *sctx)
        crc = crc32c(0, (unsigned char *)sctx->send_buf, sctx->send_size);
        hdr->crc = cpu_to_le32(crc);
 
-       ret = write_buf(sctx, sctx->send_buf, sctx->send_size);
+       ret = write_buf(sctx->send_filp, sctx->send_buf, sctx->send_size,
+                                       &sctx->send_off);
 
        sctx->total_send_size += sctx->send_size;
        sctx->cmd_send_size[le16_to_cpu(hdr->cmd)] += sctx->send_size;
@@ -1035,7 +1036,7 @@ struct backref_ctx {
 
 static int __clone_root_cmp_bsearch(const void *key, const void *elt)
 {
-       u64 root = (u64)key;
+       u64 root = (u64)(uintptr_t)key;
        struct clone_root *cr = (struct clone_root *)elt;
 
        if (root < cr->root->objectid)
@@ -1069,7 +1070,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
        u64 i_size;
 
        /* First check if the root is in the list of accepted clone sources */
-       found = bsearch((void *)root, bctx->sctx->clone_roots,
+       found = bsearch((void *)(uintptr_t)root, bctx->sctx->clone_roots,
                        bctx->sctx->clone_roots_cnt,
                        sizeof(struct clone_root),
                        __clone_root_cmp_bsearch);
@@ -1149,14 +1150,17 @@ static int find_extent_clone(struct send_ctx *sctx,
        int ret;
        int extent_type;
        u64 logical;
+       u64 disk_byte;
        u64 num_bytes;
        u64 extent_item_pos;
+       u64 flags = 0;
        struct btrfs_file_extent_item *fi;
        struct extent_buffer *eb = path->nodes[0];
        struct backref_ctx *backref_ctx = NULL;
        struct clone_root *cur_clone_root;
        struct btrfs_key found_key;
        struct btrfs_path *tmp_path;
+       int compressed;
        u32 i;
 
        tmp_path = alloc_path_for_send();
@@ -1186,22 +1190,23 @@ static int find_extent_clone(struct send_ctx *sctx,
                ret = -ENOENT;
                goto out;
        }
+       compressed = btrfs_file_extent_compression(eb, fi);
 
        num_bytes = btrfs_file_extent_num_bytes(eb, fi);
-       logical = btrfs_file_extent_disk_bytenr(eb, fi);
-       if (logical == 0) {
+       disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
+       if (disk_byte == 0) {
                ret = -ENOENT;
                goto out;
        }
-       logical += btrfs_file_extent_offset(eb, fi);
+       logical = disk_byte + btrfs_file_extent_offset(eb, fi);
 
-       ret = extent_from_logical(sctx->send_root->fs_info,
-                       logical, tmp_path, &found_key);
+       ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path,
+                                 &found_key, &flags);
        btrfs_release_path(tmp_path);
 
        if (ret < 0)
                goto out;
-       if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
                ret = -EIO;
                goto out;
        }
@@ -1234,10 +1239,16 @@ static int find_extent_clone(struct send_ctx *sctx,
        /*
         * Now collect all backrefs.
         */
+       if (compressed == BTRFS_COMPRESS_NONE)
+               extent_item_pos = logical - found_key.objectid;
+       else
+               extent_item_pos = 0;
+
        extent_item_pos = logical - found_key.objectid;
        ret = iterate_extent_inodes(sctx->send_root->fs_info,
                                        found_key.objectid, extent_item_pos, 1,
                                        __iterate_backrefs, backref_ctx);
+
        if (ret < 0)
                goto out;
 
@@ -1246,8 +1257,8 @@ static int find_extent_clone(struct send_ctx *sctx,
                ret = -EIO;
                printk(KERN_ERR "btrfs: ERROR did not find backref in "
                                "send_root. inode=%llu, offset=%llu, "
-                               "logical=%llu\n",
-                               ino, data_offset, logical);
+                               "disk_byte=%llu found extent=%llu\n",
+                               ino, data_offset, disk_byte, found_key.objectid);
                goto out;
        }
 
@@ -1759,6 +1770,7 @@ out:
  * Insert a name cache entry. On 32bit kernels the radix tree index is 32bit,
  * so we need to do some special handling in case we have clashes. This function
  * takes care of this with the help of name_cache_entry::radix_list.
+ * In case of error, nce is kfreed.
  */
 static int name_cache_insert(struct send_ctx *sctx,
                             struct name_cache_entry *nce)
@@ -1775,8 +1787,11 @@ static int name_cache_insert(struct send_ctx *sctx,
                INIT_LIST_HEAD(nce_head);
 
                ret = radix_tree_insert(&sctx->name_cache, nce->ino, nce_head);
-               if (ret < 0)
+               if (ret < 0) {
+                       kfree(nce_head);
+                       kfree(nce);
                        return ret;
+               }
        }
        list_add_tail(&nce->radix_list, nce_head);
        list_add_tail(&nce->list, &sctx->name_cache_list);
@@ -2623,6 +2638,12 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 send_progress)
        struct btrfs_key loc;
        struct btrfs_dir_item *di;
 
+       /*
+        * Don't try to rmdir the top/root subvolume dir.
+        */
+       if (dir == BTRFS_FIRST_FREE_OBJECTID)
+               return 0;
+
        path = alloc_path_for_send();
        if (!path)
                return -ENOMEM;
@@ -2683,6 +2704,12 @@ static int process_recorded_refs(struct send_ctx *sctx)
 
 verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
 
+       /*
+        * This should never happen as the root dir always has the same ref
+        * which is always '..'
+        */
+       BUG_ON(sctx->cur_ino <= BTRFS_FIRST_FREE_OBJECTID);
+
        valid_path = fs_path_alloc(sctx);
        if (!valid_path) {
                ret = -ENOMEM;
@@ -3202,8 +3229,8 @@ static int process_all_refs(struct send_ctx *sctx,
                    found_key.type != key.type)
                        break;
 
-               ret = iterate_inode_ref(sctx, sctx->parent_root, path,
-                               &found_key, 0, cb, sctx);
+               ret = iterate_inode_ref(sctx, root, path, &found_key, 0, cb,
+                               sctx);
                btrfs_release_path(path);
                if (ret < 0)
                        goto out;
@@ -3662,10 +3689,17 @@ static int send_write_or_clone(struct send_ctx *sctx,
        ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
                        struct btrfs_file_extent_item);
        type = btrfs_file_extent_type(path->nodes[0], ei);
-       if (type == BTRFS_FILE_EXTENT_INLINE)
+       if (type == BTRFS_FILE_EXTENT_INLINE) {
                len = btrfs_file_extent_inline_len(path->nodes[0], ei);
-       else
+               /*
+                * it is possible the inline item won't cover the whole page,
+                * but there may be items after this page.  Make
+                * sure to send the whole thing
+                */
+               len = PAGE_CACHE_ALIGN(len);
+       } else {
                len = btrfs_file_extent_num_bytes(path->nodes[0], ei);
+       }
 
        if (offset + len > sctx->cur_inode_size)
                len = sctx->cur_inode_size - offset;
@@ -3713,6 +3747,8 @@ static int is_extent_unchanged(struct send_ctx *sctx,
        u64 left_offset_fixed;
        u64 left_len;
        u64 right_len;
+       u64 left_gen;
+       u64 right_gen;
        u8 left_type;
        u8 right_type;
 
@@ -3722,17 +3758,17 @@ static int is_extent_unchanged(struct send_ctx *sctx,
 
        eb = left_path->nodes[0];
        slot = left_path->slots[0];
-
        ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
        left_type = btrfs_file_extent_type(eb, ei);
-       left_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
-       left_len = btrfs_file_extent_num_bytes(eb, ei);
-       left_offset = btrfs_file_extent_offset(eb, ei);
 
        if (left_type != BTRFS_FILE_EXTENT_REG) {
                ret = 0;
                goto out;
        }
+       left_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
+       left_len = btrfs_file_extent_num_bytes(eb, ei);
+       left_offset = btrfs_file_extent_offset(eb, ei);
+       left_gen = btrfs_file_extent_generation(eb, ei);
 
        /*
         * Following comments will refer to these graphics. L is the left
@@ -3788,6 +3824,7 @@ static int is_extent_unchanged(struct send_ctx *sctx,
                right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
                right_len = btrfs_file_extent_num_bytes(eb, ei);
                right_offset = btrfs_file_extent_offset(eb, ei);
+               right_gen = btrfs_file_extent_generation(eb, ei);
 
                if (right_type != BTRFS_FILE_EXTENT_REG) {
                        ret = 0;
@@ -3798,7 +3835,7 @@ static int is_extent_unchanged(struct send_ctx *sctx,
                 * Are we at extent 8? If yes, we know the extent is changed.
                 * This may only happen on the first iteration.
                 */
-               if (found_key.offset + right_len < ekey->offset) {
+               if (found_key.offset + right_len <= ekey->offset) {
                        ret = 0;
                        goto out;
                }
@@ -3815,8 +3852,9 @@ static int is_extent_unchanged(struct send_ctx *sctx,
                /*
                 * Check if we have the same extent.
                 */
-               if (left_disknr + left_offset_fixed !=
-                               right_disknr + right_offset) {
+               if (left_disknr != right_disknr ||
+                   left_offset_fixed != right_offset ||
+                   left_gen != right_gen) {
                        ret = 0;
                        goto out;
                }
@@ -4090,7 +4128,14 @@ static int changed_inode(struct send_ctx *sctx,
 
                right_gen = btrfs_inode_generation(sctx->right_path->nodes[0],
                                right_ii);
-               if (left_gen != right_gen)
+
+               /*
+                * The cur_ino = root dir case is special here. We can't treat
+                * the inode as deleted+reused because it would generate a
+                * stream that tries to delete/mkdir the root dir.
+                */
+               if (left_gen != right_gen &&
+                   sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID)
                        sctx->cur_inode_new_gen = 1;
        }
 
@@ -4283,6 +4328,11 @@ static int changed_cb(struct btrfs_root *left_root,
        if (ret < 0)
                goto out;
 
+       /* Ignore non-FS objects */
+       if (key->objectid == BTRFS_FREE_INO_OBJECTID ||
+           key->objectid == BTRFS_FREE_SPACE_OBJECTID)
+               goto out;
+
        if (key->type == BTRFS_INODE_ITEM_KEY)
                ret = changed_inode(sctx, result);
        else if (key->type == BTRFS_INODE_REF_KEY)