OSDN Git Service

btrfs: remove stale newlines from log messages
[uclinux-h8/linux.git] / fs / btrfs / send.c
index fd38b50..a86c049 100644 (file)
@@ -360,10 +360,13 @@ static int fs_path_ensure_buf(struct fs_path *p, int len)
        /*
         * First time the inline_buf does not suffice
         */
-       if (p->buf == p->inline_buf)
+       if (p->buf == p->inline_buf) {
                tmp_buf = kmalloc(len, GFP_NOFS);
-       else
+               if (tmp_buf)
+                       memcpy(tmp_buf, p->buf, old_buf_len);
+       } else {
                tmp_buf = krealloc(p->buf, len, GFP_NOFS);
+       }
        if (!tmp_buf)
                return -ENOMEM;
        p->buf = tmp_buf;
@@ -1346,7 +1349,7 @@ static int find_extent_clone(struct send_ctx *sctx,
                ret = -EIO;
                btrfs_err(sctx->send_root->fs_info, "did not find backref in "
                                "send_root. inode=%llu, offset=%llu, "
-                               "disk_byte=%llu found extent=%llu\n",
+                               "disk_byte=%llu found extent=%llu",
                                ino, data_offset, disk_byte, found_key.objectid);
                goto out;
        }
@@ -1690,10 +1693,12 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
                goto out;
        btrfs_release_path(path);
 
-       ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL, NULL,
-                       NULL, NULL);
-       if (ret < 0)
-               goto out;
+       if (dir_gen) {
+               ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL,
+                                    NULL, NULL, NULL);
+               if (ret < 0)
+                       goto out;
+       }
 
        *dir = parent_dir;
 
@@ -1709,13 +1714,12 @@ static int is_first_ref(struct btrfs_root *root,
        int ret;
        struct fs_path *tmp_name;
        u64 tmp_dir;
-       u64 tmp_dir_gen;
 
        tmp_name = fs_path_alloc();
        if (!tmp_name)
                return -ENOMEM;
 
-       ret = get_first_ref(root, ino, &tmp_dir, &tmp_dir_gen, tmp_name);
+       ret = get_first_ref(root, ino, &tmp_dir, NULL, tmp_name);
        if (ret < 0)
                goto out;
 
@@ -2026,7 +2030,6 @@ static int __get_cur_name_and_parent(struct send_ctx *sctx,
 {
        int ret;
        int nce_ret;
-       struct btrfs_path *path = NULL;
        struct name_cache_entry *nce = NULL;
 
        /*
@@ -2052,10 +2055,6 @@ static int __get_cur_name_and_parent(struct send_ctx *sctx,
                }
        }
 
-       path = alloc_path_for_send();
-       if (!path)
-               return -ENOMEM;
-
        /*
         * If the inode is not existent yet, add the orphan name and return 1.
         * This should only happen for the parent dir that we determine in
@@ -2131,7 +2130,6 @@ out_cache:
        name_cache_clean_unused(sctx);
 
 out:
-       btrfs_free_path(path);
        return ret;
 }
 
@@ -2942,7 +2940,9 @@ static void free_waiting_dir_move(struct send_ctx *sctx,
 static int add_pending_dir_move(struct send_ctx *sctx,
                                u64 ino,
                                u64 ino_gen,
-                               u64 parent_ino)
+                               u64 parent_ino,
+                               struct list_head *new_refs,
+                               struct list_head *deleted_refs)
 {
        struct rb_node **p = &sctx->pending_dir_moves.rb_node;
        struct rb_node *parent = NULL;
@@ -2974,12 +2974,12 @@ static int add_pending_dir_move(struct send_ctx *sctx,
                }
        }
 
-       list_for_each_entry(cur, &sctx->deleted_refs, list) {
+       list_for_each_entry(cur, deleted_refs, list) {
                ret = dup_ref(cur, &pm->update_refs);
                if (ret < 0)
                        goto out;
        }
-       list_for_each_entry(cur, &sctx->new_refs, list) {
+       list_for_each_entry(cur, new_refs, list) {
                ret = dup_ref(cur, &pm->update_refs);
                if (ret < 0)
                        goto out;
@@ -3022,6 +3022,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx,
        return NULL;
 }
 
+static int path_loop(struct send_ctx *sctx, struct fs_path *name,
+                    u64 ino, u64 gen, u64 *ancestor_ino)
+{
+       int ret = 0;
+       u64 parent_inode = 0;
+       u64 parent_gen = 0;
+       u64 start_ino = ino;
+
+       *ancestor_ino = 0;
+       while (ino != BTRFS_FIRST_FREE_OBJECTID) {
+               fs_path_reset(name);
+
+               if (is_waiting_for_rm(sctx, ino))
+                       break;
+               if (is_waiting_for_move(sctx, ino)) {
+                       if (*ancestor_ino == 0)
+                               *ancestor_ino = ino;
+                       ret = get_first_ref(sctx->parent_root, ino,
+                                           &parent_inode, &parent_gen, name);
+               } else {
+                       ret = __get_cur_name_and_parent(sctx, ino, gen,
+                                                       &parent_inode,
+                                                       &parent_gen, name);
+                       if (ret > 0) {
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret < 0)
+                       break;
+               if (parent_inode == start_ino) {
+                       ret = 1;
+                       if (*ancestor_ino == 0)
+                               *ancestor_ino = ino;
+                       break;
+               }
+               ino = parent_inode;
+               gen = parent_gen;
+       }
+       return ret;
+}
+
 static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
 {
        struct fs_path *from_path = NULL;
@@ -3033,6 +3075,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        struct waiting_dir_move *dm = NULL;
        u64 rmdir_ino = 0;
        int ret;
+       u64 ancestor = 0;
 
        name = fs_path_alloc();
        from_path = fs_path_alloc();
@@ -3051,34 +3094,33 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        if (ret < 0)
                goto out;
 
-       if (parent_ino == sctx->cur_ino) {
-               /* child only renamed, not moved */
-               ASSERT(parent_gen == sctx->cur_inode_gen);
-               ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen,
-                                  from_path);
-               if (ret < 0)
-                       goto out;
-               ret = fs_path_add_path(from_path, name);
-               if (ret < 0)
-                       goto out;
-       } else {
-               /* child moved and maybe renamed too */
-               sctx->send_progress = pm->ino;
-               ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
+       ret = get_cur_path(sctx, parent_ino, parent_gen,
+                          from_path);
+       if (ret < 0)
+               goto out;
+       ret = fs_path_add_path(from_path, name);
+       if (ret < 0)
+               goto out;
+
+       sctx->send_progress = sctx->cur_ino + 1;
+       ret = path_loop(sctx, name, pm->ino, pm->gen, &ancestor);
+       if (ret) {
+               LIST_HEAD(deleted_refs);
+               ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
+               ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
+                                          &pm->update_refs, &deleted_refs);
                if (ret < 0)
                        goto out;
-       }
-
-       fs_path_free(name);
-       name = NULL;
-
-       to_path = fs_path_alloc();
-       if (!to_path) {
-               ret = -ENOMEM;
+               if (rmdir_ino) {
+                       dm = get_waiting_dir_move(sctx, pm->ino);
+                       ASSERT(dm);
+                       dm->rmdir_ino = rmdir_ino;
+               }
                goto out;
        }
-
-       sctx->send_progress = sctx->cur_ino + 1;
+       fs_path_reset(name);
+       to_path = name;
+       name = NULL;
        ret = get_cur_path(sctx, pm->ino, pm->gen, to_path);
        if (ret < 0)
                goto out;
@@ -3202,127 +3244,74 @@ out:
 static int wait_for_parent_move(struct send_ctx *sctx,
                                struct recorded_ref *parent_ref)
 {
-       int ret;
+       int ret = 0;
        u64 ino = parent_ref->dir;
        u64 parent_ino_before, parent_ino_after;
-       u64 old_gen;
        struct fs_path *path_before = NULL;
        struct fs_path *path_after = NULL;
        int len1, len2;
-       int register_upper_dirs;
-       u64 gen;
-
-       if (is_waiting_for_move(sctx, ino))
-               return 1;
-
-       if (parent_ref->dir <= sctx->cur_ino)
-               return 0;
-
-       ret = get_inode_info(sctx->parent_root, ino, NULL, &old_gen,
-                            NULL, NULL, NULL, NULL);
-       if (ret == -ENOENT)
-               return 0;
-       else if (ret < 0)
-               return ret;
-
-       if (parent_ref->dir_gen != old_gen)
-               return 0;
-
-       path_before = fs_path_alloc();
-       if (!path_before)
-               return -ENOMEM;
-
-       ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before,
-                           NULL, path_before);
-       if (ret == -ENOENT) {
-               ret = 0;
-               goto out;
-       } else if (ret < 0) {
-               goto out;
-       }
 
        path_after = fs_path_alloc();
-       if (!path_after) {
+       path_before = fs_path_alloc();
+       if (!path_after || !path_before) {
                ret = -ENOMEM;
                goto out;
        }
 
-       ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
-                           &gen, path_after);
-       if (ret == -ENOENT) {
-               ret = 0;
-               goto out;
-       } else if (ret < 0) {
-               goto out;
-       }
-
-       len1 = fs_path_len(path_before);
-       len2 = fs_path_len(path_after);
-       if (parent_ino_before != parent_ino_after || len1 != len2 ||
-            memcmp(path_before->start, path_after->start, len1)) {
-               ret = 1;
-               goto out;
-       }
-       ret = 0;
-
        /*
-        * Ok, our new most direct ancestor has a higher inode number but
-        * wasn't moved/renamed. So maybe some of the new ancestors higher in
-        * the hierarchy have an higher inode number too *and* were renamed
-        * or moved - in this case we need to wait for the ancestor's rename
-        * or move operation before we can do the move/rename for the current
-        * inode.
+        * Our current directory inode may not yet be renamed/moved because some
+        * ancestor (immediate or not) has to be renamed/moved first. So find if
+        * such ancestor exists and make sure our own rename/move happens after
+        * that ancestor is processed.
         */
-       register_upper_dirs = 0;
-       ino = parent_ino_after;
-again:
-       while ((ret == 0 || register_upper_dirs) && ino > sctx->cur_ino) {
-               u64 parent_gen;
+       while (ino > BTRFS_FIRST_FREE_OBJECTID) {
+               if (is_waiting_for_move(sctx, ino)) {
+                       ret = 1;
+                       break;
+               }
 
                fs_path_reset(path_before);
                fs_path_reset(path_after);
 
                ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
-                                   &parent_gen, path_after);
+                                   NULL, path_after);
                if (ret < 0)
                        goto out;
                ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before,
                                    NULL, path_before);
-               if (ret == -ENOENT) {
-                       ret = 0;
-                       break;
-               } else if (ret < 0) {
+               if (ret < 0 && ret != -ENOENT) {
                        goto out;
+               } else if (ret == -ENOENT) {
+                       ret = 1;
+                       break;
                }
 
                len1 = fs_path_len(path_before);
                len2 = fs_path_len(path_after);
-               if (parent_ino_before != parent_ino_after || len1 != len2 ||
-                   memcmp(path_before->start, path_after->start, len1)) {
+               if (ino > sctx->cur_ino &&
+                   (parent_ino_before != parent_ino_after || len1 != len2 ||
+                    memcmp(path_before->start, path_after->start, len1))) {
                        ret = 1;
-                       if (register_upper_dirs) {
-                               break;
-                       } else {
-                               register_upper_dirs = 1;
-                               ino = parent_ref->dir;
-                               gen = parent_ref->dir_gen;
-                               goto again;
-                       }
-               } else if (register_upper_dirs) {
-                       ret = add_pending_dir_move(sctx, ino, gen,
-                                                  parent_ino_after);
-                       if (ret < 0 && ret != -EEXIST)
-                               goto out;
+                       break;
                }
-
                ino = parent_ino_after;
-               gen = parent_gen;
        }
 
 out:
        fs_path_free(path_before);
        fs_path_free(path_after);
 
+       if (ret == 1) {
+               ret = add_pending_dir_move(sctx,
+                                          sctx->cur_ino,
+                                          sctx->cur_inode_gen,
+                                          ino,
+                                          &sctx->new_refs,
+                                          &sctx->deleted_refs);
+               if (!ret)
+                       ret = 1;
+       }
+
        return ret;
 }
 
@@ -3483,10 +3472,6 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
                                if (ret < 0)
                                        goto out;
                                if (ret) {
-                                       ret = add_pending_dir_move(sctx,
-                                                          sctx->cur_ino,
-                                                          sctx->cur_inode_gen,
-                                                          cur->dir);
                                        *pending_move = 1;
                                } else {
                                        ret = send_rename(sctx, valid_path,
@@ -5487,7 +5472,7 @@ static void btrfs_root_dec_send_in_progress(struct btrfs_root* root)
         */
        if (root->send_in_progress < 0)
                btrfs_err(root->fs_info,
-                       "send_in_progres unbalanced %d root %llu\n",
+                       "send_in_progres unbalanced %d root %llu",
                        root->send_in_progress, root->root_key.objectid);
        spin_unlock(&root->root_item_lock);
 }
@@ -5515,7 +5500,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
 
        /*
         * The subvolume must remain read-only during send, protect against
-        * making it RW.
+        * making it RW. This also protects against deletion.
         */
        spin_lock(&send_root->root_item_lock);
        send_root->send_in_progress++;
@@ -5575,6 +5560,15 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
        }
 
        sctx->send_root = send_root;
+       /*
+        * Unlikely but possible, if the subvolume is marked for deletion but
+        * is slow to remove the directory entry, send can still be started
+        */
+       if (btrfs_root_dead(sctx->send_root)) {
+               ret = -EPERM;
+               goto out;
+       }
+
        sctx->clone_roots_cnt = arg->clone_sources_count;
 
        sctx->send_max_size = BTRFS_SEND_BUF_SIZE;
@@ -5664,7 +5658,8 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
 
                spin_lock(&sctx->parent_root->root_item_lock);
                sctx->parent_root->send_in_progress++;
-               if (!btrfs_root_readonly(sctx->parent_root)) {
+               if (!btrfs_root_readonly(sctx->parent_root) ||
+                               btrfs_root_dead(sctx->parent_root)) {
                        spin_unlock(&sctx->parent_root->root_item_lock);
                        srcu_read_unlock(&fs_info->subvol_srcu, index);
                        ret = -EPERM;