Btrfs: prevent loops in the directory tree when creating snapshots
[linux-2.6-block.git] / fs / btrfs / ioctl.c
index 536ae883780122002d4b06abc4070bc46567ff1f..8828109fa58efa9475d52cb2383015e71eadcfd0 100644 (file)
@@ -284,6 +284,56 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name,
         * subvolume with specific mode bits.
         */
        if (snap_src) {
+               struct dentry *dir = dentry->d_parent;
+               struct dentry *test = dir->d_parent;
+               struct btrfs_path *path = btrfs_alloc_path();
+               int ret;
+               u64 test_oid;
+               u64 parent_oid = BTRFS_I(dir->d_inode)->root->root_key.objectid;
+
+               test_oid = snap_src->root_key.objectid;
+
+               ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
+                                         path, parent_oid, test_oid);
+               if (ret == 0)
+                       goto create;
+               btrfs_release_path(snap_src->fs_info->tree_root, path);
+
+               /* we need to make sure we aren't creating a directory loop
+                * by taking a snapshot of something that has our current
+                * subvol in its directory tree.  So, this loops through
+                * the dentries and checks the forward refs for each subvolume
+                * to see if is references the subvolume where we are
+                * placing this new snapshot.
+                */
+               while(1) {
+                       if (!test ||
+                           dir == snap_src->fs_info->sb->s_root ||
+                           test == snap_src->fs_info->sb->s_root ||
+                           test->d_inode->i_sb != snap_src->fs_info->sb) {
+                               break;
+                       }
+                       if (S_ISLNK(test->d_inode->i_mode)) {
+                               printk("Symlink in snapshot path, failed\n");
+                               error = -EMLINK;
+                               btrfs_free_path(path);
+                               goto out_drop_write;
+                       }
+                       test_oid =
+                               BTRFS_I(test->d_inode)->root->root_key.objectid;
+                       ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
+                                 path, test_oid, parent_oid);
+                       if (ret == 0) {
+                               printk("Snapshot creation failed, looping\n");
+                               error = -EMLINK;
+                               btrfs_free_path(path);
+                               goto out_drop_write;
+                       }
+                       btrfs_release_path(snap_src->fs_info->tree_root, path);
+                       test = test->d_parent;
+               }
+create:
+               btrfs_free_path(path);
                error = create_snapshot(snap_src, dentry, name, namelen);
        } else {
                error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root,