diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5611f8e035a4..b3bc65b08c6a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -799,6 +799,12 @@ struct btrfs_root { spinlock_t list_lock; struct list_head dead_list; struct list_head orphan_list; + + /* + * right now this just gets used so that a root has its own devid + * for stat. It may be used for more later + */ + struct super_block anon_super; }; /* diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 0a5350573f61..8d7866b733d6 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -877,6 +877,12 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, root->defrag_running = 0; root->defrag_level = 0; root->root_key.objectid = objectid; + root->anon_super.s_root = NULL; + root->anon_super.s_dev = 0; + INIT_LIST_HEAD(&root->anon_super.s_list); + INIT_LIST_HEAD(&root->anon_super.s_instances); + init_rwsem(&root->anon_super.s_umount); + return 0; } @@ -1083,6 +1089,9 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info, root = btrfs_read_fs_root_no_radix(fs_info->tree_root, location); if (IS_ERR(root)) return root; + + set_anon_super(&root->anon_super, NULL); + ret = radix_tree_insert(&fs_info->fs_roots_radix, (unsigned long)root->root_key.objectid, root); @@ -1950,6 +1959,10 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root) { radix_tree_delete(&fs_info->fs_roots_radix, (unsigned long)root->root_key.objectid); + if (root->anon_super.s_dev) { + down_write(&root->anon_super.s_umount); + kill_anon_super(&root->anon_super); + } if (root->in_sysfs) btrfs_sysfs_del_root(root); if (root->node) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e163b1b74707..7ef79ce86e25 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2212,7 +2212,12 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) struct btrfs_trans_handle *trans; unsigned long nr = 0; - if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) { + /* + * the FIRST_FREE_OBJECTID check makes sure we don't try to rmdir + * the root of a subvolume or snapshot + */ + if (inode->i_size > BTRFS_EMPTY_DIR_SIZE || + inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) { return -ENOTEMPTY; } @@ -4410,7 +4415,6 @@ int btrfs_create_subvol_root(struct btrfs_root *new_root, struct dentry *dentry, if (error) return error; - atomic_inc(&inode->i_count); d_instantiate(dentry, inode); return 0; } @@ -4548,6 +4552,7 @@ static int btrfs_getattr(struct vfsmount *mnt, { struct inode *inode = dentry->d_inode; generic_fillattr(inode, stat); + stat->dev = BTRFS_I(inode)->root->anon_super.s_dev; stat->blksize = PAGE_CACHE_SIZE; stat->blocks = (inode_get_bytes(inode) + BTRFS_I(inode)->delalloc_bytes) >> 9; @@ -4565,6 +4570,11 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry, u64 index = 0; int ret; + /* we're not allowed to rename between subvolumes */ + if (BTRFS_I(old_inode)->root->root_key.objectid != + BTRFS_I(new_dir)->root->root_key.objectid) + return -EXDEV; + if (S_ISDIR(old_inode->i_mode) && new_inode && new_inode->i_size > BTRFS_EMPTY_DIR_SIZE) { return -ENOTEMPTY; @@ -4920,6 +4930,7 @@ static int btrfs_permission(struct inode *inode, int mask) } static struct inode_operations btrfs_dir_inode_operations = { + .getattr = btrfs_getattr, .lookup = btrfs_lookup, .create = btrfs_create, .unlink = btrfs_unlink, diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ec45b3086136..773db07b5f72 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -127,7 +127,6 @@ static noinline int create_subvol(struct btrfs_root *root, key.objectid = objectid; key.offset = 1; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); -printk("inserting root objectid %Lu\n", objectid); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, &root_item); if (ret) @@ -175,7 +174,6 @@ fail: ret = err; fail_commit: btrfs_btree_balance_dirty(root, nr); -printk("all done ret %d\n", ret); return ret; } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index eec8b2465039..93f23a456a36 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -832,13 +832,13 @@ static noinline int finish_pending_snapshot(struct btrfs_fs_info *fs_info, struct inode *parent_inode; struct inode *inode; - trans = btrfs_start_transaction(fs_info->fs_root, 1); + parent_inode = pending->dentry->d_parent->d_inode; + trans = btrfs_start_transaction(BTRFS_I(parent_inode)->root, 1); /* * insert the directory item */ namelen = strlen(pending->name); - parent_inode = pending->dentry->d_parent->d_inode; ret = btrfs_set_inode_index(parent_inode, &index); ret = btrfs_insert_dir_item(trans, BTRFS_I(parent_inode)->root,