Btrfs: Give each subvol and snapshot their own anonymous devid
Each subvolume has its own private inode number space, and so we need
to fill in different device numbers for each subvolume to avoid confusing
applications.
This commit puts a struct super_block into struct btrfs_root so it can
call set_anon_super() and get a different device number generated for
each root.
btrfs_rename is changed to prevent renames across subvols.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 5611f8e..b3bc65b 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -799,6 +799,12 @@
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 0a53505..8d7866b 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -877,6 +877,12 @@
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 @@
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 @@
{
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 e163b1b..7ef79ce 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2212,7 +2212,12 @@
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 @@
if (error)
return error;
- atomic_inc(&inode->i_count);
d_instantiate(dentry, inode);
return 0;
}
@@ -4548,6 +4552,7 @@
{
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 @@
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 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 ec45b30..773db07 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -127,7 +127,6 @@
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 @@
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 eec8b24..93f23a4 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -832,13 +832,13 @@
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,