This patch adds drop snapshot/subvol ioctl that allows
dropping non-empty snapshot/subvols. The ioctl code is
based on Aaron Straus''s patch.
Another change in this patch is add code that finds orphan
fs trees.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
---
diff -urp 4/fs/btrfs/ctree.h 5/fs/btrfs/ctree.h
--- 4/fs/btrfs/ctree.h 2009-08-24 10:39:12.200349458 +0800
+++ 5/fs/btrfs/ctree.h 2009-08-24 10:40:05.519385029 +0800
@@ -2125,6 +2125,7 @@ int btrfs_find_last_root(struct btrfs_ro
int btrfs_search_root(struct btrfs_root *root, u64 search_start,
u64 *found_objectid);
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
+int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
int btrfs_set_root_node(struct btrfs_root_item *item,
struct extent_buffer *node);
/* dir-item.c */
diff -urp 4/fs/btrfs/disk-io.c 5/fs/btrfs/disk-io.c
--- 4/fs/btrfs/disk-io.c 2009-08-24 10:39:12.202348481 +0800
+++ 5/fs/btrfs/disk-io.c 2009-08-25 15:23:52.441143639 +0800
@@ -950,13 +950,14 @@ static int find_and_setup_root(struct bt
ret = btrfs_find_last_root(tree_root, objectid,
&root->root_item, &root->root_key);
BUG_ON(ret);
+ BUG_ON(btrfs_root_refs(&root->root_item) == 0);
generation = btrfs_root_generation(&root->root_item);
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
root->node = read_tree_block(root,
btrfs_root_bytenr(&root->root_item),
blocksize, generation);
- root->commit_root = btrfs_root_node(root);
BUG_ON(!root->node);
+ root->commit_root = btrfs_root_node(root);
return 0;
}
@@ -1194,12 +1195,13 @@ struct btrfs_root *btrfs_read_fs_root_no
kfree(root);
return ERR_PTR(ret);
}
- if (!(fs_info->sb->s_flags & MS_RDONLY)) {
- ret = btrfs_find_dead_roots(fs_info->tree_root,
- root->root_key.objectid);
- BUG_ON(ret);
+ ret = btrfs_find_dead_roots(fs_info->tree_root,
+ root->root_key.objectid);
+ BUG_ON(ret);
+
+ if (!(fs_info->sb->s_flags & MS_RDONLY))
btrfs_orphan_cleanup(root);
- }
+
return root;
}
@@ -1424,9 +1426,12 @@ static int cleaner_kthread(void *arg)
break;
vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
- mutex_lock(&root->fs_info->cleaner_mutex);
- btrfs_clean_old_snapshots(root);
- mutex_unlock(&root->fs_info->cleaner_mutex);
+
+ if (!(root->fs_info->sb->s_flags & MS_RDONLY)) {
+ mutex_lock(&root->fs_info->cleaner_mutex);
+ btrfs_clean_old_snapshots(root);
+ mutex_unlock(&root->fs_info->cleaner_mutex);
+ }
if (freezing(current)) {
refrigerator();
@@ -1889,6 +1894,9 @@ struct btrfs_root *open_ctree(struct sup
}
}
+ ret = btrfs_find_orphan_roots(tree_root);
+ BUG_ON(ret);
+
if (!(sb->s_flags & MS_RDONLY)) {
ret = btrfs_recover_relocation(tree_root);
BUG_ON(ret);
@@ -2229,6 +2237,15 @@ static int del_fs_roots(struct btrfs_fs_
struct btrfs_root *gang[8];
int i;
+ while (!list_empty(&fs_info->dead_roots)) {
+ gang[0] = list_entry(fs_info->dead_roots.next,
+ struct btrfs_root, root_list);
+ list_del(&gang[0]->root_list);
+ free_extent_buffer(gang[0]->node);
+ free_extent_buffer(gang[0]->commit_root);
+ kfree(gang[0]);
+ }
+
while (1) {
ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
(void **)gang, 0,
@@ -2258,9 +2275,6 @@ int btrfs_cleanup_fs_roots(struct btrfs_
root_objectid = gang[ret - 1]->root_key.objectid + 1;
for (i = 0; i < ret; i++) {
root_objectid = gang[i]->root_key.objectid;
- ret = btrfs_find_dead_roots(fs_info->tree_root,
- root_objectid);
- BUG_ON(ret);
btrfs_orphan_cleanup(gang[i]);
}
root_objectid++;
diff -urp 4/fs/btrfs/extent-tree.c 5/fs/btrfs/extent-tree.c
--- 4/fs/btrfs/extent-tree.c 2009-08-24 10:36:43.016348000 +0800
+++ 5/fs/btrfs/extent-tree.c 2009-08-24 10:40:05.526099170 +0800
@@ -5356,6 +5356,17 @@ int btrfs_drop_snapshot(struct btrfs_roo
ret = btrfs_del_root(trans, tree_root, &root->root_key);
BUG_ON(ret);
+ if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
+ ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
+ NULL, NULL);
+ BUG_ON(ret < 0);
+ if (ret > 0) {
+ ret = btrfs_del_orphan_item(trans, tree_root,
+ root->root_key.objectid);
+ BUG_ON(ret);
+ }
+ }
+
free_extent_buffer(root->node);
free_extent_buffer(root->commit_root);
kfree(root);
diff -urp 4/fs/btrfs/inode-map.c 5/fs/btrfs/inode-map.c
--- 4/fs/btrfs/inode-map.c 2009-08-25 13:12:02.360309951 +0800
+++ 5/fs/btrfs/inode-map.c 2009-08-24 16:35:47.845349566 +0800
@@ -44,9 +44,9 @@ int btrfs_find_highest_inode(struct btrf
l = path->nodes[0];
btrfs_item_key_to_cpu(l, &found_key, slot);
*objectid = max_t(u64, found_key.objectid,
- BTRFS_FIRST_FREE_OBJECTID - 1);
+ BTRFS_FIRST_FREE_OBJECTID);
} else {
- *objectid = BTRFS_FIRST_FREE_OBJECTID - 1;
+ *objectid = BTRFS_FIRST_FREE_OBJECTID;
}
ret = 0;
error:
diff -urp 4/fs/btrfs/ioctl.c 5/fs/btrfs/ioctl.c
--- 4/fs/btrfs/ioctl.c 2009-08-24 10:38:34.286099000 +0800
+++ 5/fs/btrfs/ioctl.c 2009-08-24 10:40:05.527099450 +0800
@@ -600,7 +600,8 @@ out_unlock:
return 0;
}
-static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
+static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
+ void __user *arg)
{
u64 new_size;
u64 old_size;
@@ -783,6 +784,88 @@ out:
return ret;
}
+static noinline int btrfs_ioctl_snap_destroy(struct file *file,
+ void __user *arg)
+{
+ struct dentry *dentry;
+ struct dentry *parent = fdentry(file);
+ struct inode *inode;
+ struct inode *dir = parent->d_inode;
+ struct btrfs_root *root = BTRFS_I(dir)->root;
+ struct btrfs_ioctl_vol_args *vol_args;
+ struct btrfs_trans_handle *trans;
+ unsigned long nr = 0;
+ int namelen;
+ int ret;
+
+ vol_args = kmalloc(sizeof(*vol_args), GFP_KERNEL);
+ if (!vol_args)
+ return -ENOMEM;
+
+ if (copy_from_user(vol_args, arg, sizeof(*vol_args))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ vol_args->name[BTRFS_PATH_NAME_MAX] = ''\0'';
+ namelen = strlen(vol_args->name);
+ if (strchr(vol_args->name, ''/'')) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = mnt_want_write(file->f_path.mnt);
+ if (ret)
+ goto out;
+
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+
+ dentry = lookup_one_len(vol_args->name, parent, namelen);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out_unlock;
+ }
+
+ inode = dentry->d_inode;
+ if (!inode) {
+ ret = -ENOENT;
+ goto out_dput;
+ }
+ if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
+ ret = -EINVAL;
+ goto out_dput;
+ }
+
+ ret = d_invalidate(dentry);
+ if (ret)
+ goto out_dput;
+
+ mutex_lock(&inode->i_mutex);
+
+ trans = btrfs_start_transaction(root, 1);
+ btrfs_set_trans_block_group(trans, dir);
+
+ root->fs_info->last_trans_log_full_commit = trans->transid;
+ ret = btrfs_unlink_subvol(trans, root, dir, dentry->d_inode,
+ dentry->d_name.name, dentry->d_name.len);
+ BUG_ON(ret);
+
+ nr = trans->blocks_used;
+ btrfs_end_transaction(trans, root);
+ btrfs_btree_balance_dirty(root, nr);
+
+ ret = 0;
+ mutex_unlock(&inode->i_mutex);
+out_dput:
+ dput(dentry);
+out_unlock:
+ mutex_unlock(&dir->i_mutex);
+ mnt_drop_write(file->f_path.mnt);
+out:
+ kfree(vol_args);
+ return ret;
+}
+
static int btrfs_ioctl_defrag(struct file *file)
{
struct inode *inode = fdentry(file)->d_inode;
@@ -856,8 +939,8 @@ static long btrfs_ioctl_rm_dev(struct bt
return ret;
}
-static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
- u64 off, u64 olen, u64 destoff)
+static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
+ u64 off, u64 olen, u64 destoff)
{
struct inode *inode = fdentry(file)->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -1249,6 +1332,8 @@ long btrfs_ioctl(struct file *file, unsi
return btrfs_ioctl_snap_create(file, argp, 0);
case BTRFS_IOC_SUBVOL_CREATE:
return btrfs_ioctl_snap_create(file, argp, 1);
+ case BTRFS_IOC_SNAP_DESTROY:
+ return btrfs_ioctl_snap_destroy(file, argp);
case BTRFS_IOC_DEFRAG:
return btrfs_ioctl_defrag(file);
case BTRFS_IOC_RESIZE:
diff -urp 4/fs/btrfs/ioctl.h 5/fs/btrfs/ioctl.h
--- 4/fs/btrfs/ioctl.h 2009-08-24 10:36:43.018349000 +0800
+++ 5/fs/btrfs/ioctl.h 2009-08-24 10:40:05.528099380 +0800
@@ -65,5 +65,6 @@ struct btrfs_ioctl_clone_range_args {
#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
struct btrfs_ioctl_vol_args)
-
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+ struct btrfs_ioctl_vol_args)
#endif
diff -urp 4/fs/btrfs/root-tree.c 5/fs/btrfs/root-tree.c
--- 4/fs/btrfs/root-tree.c 2009-08-24 10:38:34.287099000 +0800
+++ 5/fs/btrfs/root-tree.c 2009-08-24 10:40:05.529100569 +0800
@@ -94,17 +94,22 @@ int btrfs_find_last_root(struct btrfs_ro
goto out;
BUG_ON(ret == 0);
+ if (path->slots[0] == 0) {
+ ret = 1;
+ goto out;
+ }
l = path->nodes[0];
- BUG_ON(path->slots[0] == 0);
slot = path->slots[0] - 1;
btrfs_item_key_to_cpu(l, &found_key, slot);
if (found_key.objectid != objectid) {
ret = 1;
goto out;
}
- read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
- sizeof(*item));
- memcpy(key, &found_key, sizeof(found_key));
+ if (item)
+ read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
+ sizeof(*item));
+ if (key)
+ memcpy(key, &found_key, sizeof(found_key));
ret = 0;
out:
btrfs_free_path(path);
@@ -249,6 +254,59 @@ err:
return ret;
}
+int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
+{
+ struct extent_buffer *leaf;
+ struct btrfs_path *path;
+ struct btrfs_key key;
+ int err = 0;
+ int ret;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = BTRFS_ORPHAN_OBJECTID;
+ key.type = BTRFS_ORPHAN_ITEM_KEY;
+ key.offset = 0;
+
+ while (1) {
+ ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
+ if (ret < 0) {
+ err = ret;
+ break;
+ }
+
+ leaf = path->nodes[0];
+ if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(tree_root, path);
+ if (ret < 0)
+ err = ret;
+ if (ret != 0)
+ break;
+ leaf = path->nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+ btrfs_release_path(tree_root, path);
+
+ if (key.objectid != BTRFS_ORPHAN_OBJECTID ||
+ key.type != BTRFS_ORPHAN_ITEM_KEY)
+ break;
+
+ ret = btrfs_find_dead_roots(tree_root, key.offset);
+ if (ret) {
+ err = ret;
+ break;
+ }
+
+ key.offset++;
+ }
+
+ btrfs_free_path(path);
+ return err;
+}
+
/* drop the root item for ''key'' from ''root''
*/
int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_key *key)
diff -urp 4/fs/btrfs/transaction.c 5/fs/btrfs/transaction.c
--- 4/fs/btrfs/transaction.c 2009-08-24 10:38:34.288098000 +0800
+++ 5/fs/btrfs/transaction.c 2009-08-24 10:40:05.530100150 +0800
@@ -1087,8 +1087,12 @@ int btrfs_clean_old_snapshots(struct btr
while (!list_empty(&list)) {
root = list_entry(list.next, struct btrfs_root, root_list);
- list_del_init(&root->root_list);
- btrfs_drop_snapshot(root, 0);
+ list_del(&root->root_list);
+ if (btrfs_header_backref_rev(root->node) <
+ BTRFS_MIXED_BACKREF_REV)
+ btrfs_drop_snapshot(root, 0);
+ else
+ btrfs_drop_snapshot(root, 1);
}
return 0;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html