summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h67
-rw-r--r--fs/btrfs/inode.c6
-rw-r--r--fs/btrfs/ioctl.c24
-rw-r--r--fs/btrfs/root-tree.c76
-rw-r--r--fs/btrfs/transaction.c31
5 files changed, 172 insertions, 32 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index b3bc65b08c6a..ad2cbe63503c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -469,6 +469,15 @@ struct btrfs_root_item {
u8 level;
} __attribute__ ((__packed__));
+/*
+ * this is used for both forward and backward root refs
+ */
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
#define BTRFS_FILE_EXTENT_INLINE 0
#define BTRFS_FILE_EXTENT_REG 1
#define BTRFS_FILE_EXTENT_PREALLOC 2
@@ -814,27 +823,27 @@ struct btrfs_root {
* the FS
*/
#define BTRFS_INODE_ITEM_KEY 1
-#define BTRFS_INODE_REF_KEY 2
-#define BTRFS_XATTR_ITEM_KEY 8
-#define BTRFS_ORPHAN_ITEM_KEY 9
+#define BTRFS_INODE_REF_KEY 12
+#define BTRFS_XATTR_ITEM_KEY 24
+#define BTRFS_ORPHAN_ITEM_KEY 48
/* reserve 2-15 close to the inode for later flexibility */
/*
* dir items are the name -> inode pointers in a directory. There is one
* for every name in a directory.
*/
-#define BTRFS_DIR_LOG_ITEM_KEY 14
-#define BTRFS_DIR_LOG_INDEX_KEY 15
-#define BTRFS_DIR_ITEM_KEY 16
-#define BTRFS_DIR_INDEX_KEY 17
+#define BTRFS_DIR_LOG_ITEM_KEY 60
+#define BTRFS_DIR_LOG_INDEX_KEY 72
+#define BTRFS_DIR_ITEM_KEY 84
+#define BTRFS_DIR_INDEX_KEY 96
/*
* extent data is for file data
*/
-#define BTRFS_EXTENT_DATA_KEY 18
+#define BTRFS_EXTENT_DATA_KEY 108
/*
* csum items have the checksums for data in the extents
*/
-#define BTRFS_CSUM_ITEM_KEY 19
+#define BTRFS_CSUM_ITEM_KEY 120
/* reserve 21-31 for other file/dir stuff */
@@ -843,23 +852,37 @@ struct btrfs_root {
* root items point to tree roots. There are typically in the root
* tree used by the super block to find all the other trees
*/
-#define BTRFS_ROOT_ITEM_KEY 32
+#define BTRFS_ROOT_ITEM_KEY 132
+
+/*
+ * root backrefs tie subvols and snapshots to the directory entries that
+ * reference them
+ */
+#define BTRFS_ROOT_BACKREF_KEY 144
+
+/*
+ * root refs make a fast index for listing all of the snapshots and
+ * subvolumes referenced by a given root. They point directly to the
+ * directory item in the root that references the subvol
+ */
+#define BTRFS_ROOT_REF_KEY 156
+
/*
* extent items are in the extent map tree. These record which blocks
* are used, and how many references there are to each block
*/
-#define BTRFS_EXTENT_ITEM_KEY 33
-#define BTRFS_EXTENT_REF_KEY 34
+#define BTRFS_EXTENT_ITEM_KEY 168
+#define BTRFS_EXTENT_REF_KEY 180
/*
* block groups give us hints into the extent allocation trees. Which
* blocks are free etc etc
*/
-#define BTRFS_BLOCK_GROUP_ITEM_KEY 50
+#define BTRFS_BLOCK_GROUP_ITEM_KEY 192
-#define BTRFS_DEV_EXTENT_KEY 75
-#define BTRFS_DEV_ITEM_KEY 76
-#define BTRFS_CHUNK_ITEM_KEY 77
+#define BTRFS_DEV_EXTENT_KEY 204
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
/*
* string items are for debugging. They just store a short string of
@@ -1274,6 +1297,13 @@ static inline void btrfs_set_item_key(struct extent_buffer *eb,
BTRFS_SETGET_FUNCS(dir_log_end, struct btrfs_dir_log_item, end, 64);
+/*
+ * struct btrfs_root_ref
+ */
+BTRFS_SETGET_FUNCS(root_ref_dirid, struct btrfs_root_ref, dirid, 64);
+BTRFS_SETGET_FUNCS(root_ref_sequence, struct btrfs_root_ref, sequence, 64);
+BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16);
+
/* struct btrfs_dir_item */
BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
@@ -1771,6 +1801,11 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
struct extent_buffer *node,
struct extent_buffer *parent);
/* root-item.c */
+int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *tree_root,
+ u64 root_id, u8 type, u64 ref_id,
+ u64 dirid, u64 sequence,
+ const char *name, int name_len);
int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_key *key);
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7ef79ce86e25..6854bf41856a 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4580,6 +4580,12 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,
return -ENOTEMPTY;
}
+ /* to rename a snapshot or subvolume, we need to juggle the
+ * backrefs. This isn't coded yet
+ */
+ if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
+ return -EXDEV;
+
ret = btrfs_check_free_space(root, 1, 0);
if (ret)
goto out_unlock;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 773db07b5f72..536ae8837801 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -145,13 +145,23 @@ static noinline int create_subvol(struct btrfs_root *root,
BTRFS_FT_DIR, index);
if (ret)
goto fail;
-#if 0
- ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
- name, namelen, objectid,
- root->fs_info->sb->s_root->d_inode->i_ino, 0);
- if (ret)
- goto fail;
-#endif
+
+ /* add the backref first */
+ ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
+ objectid, BTRFS_ROOT_BACKREF_KEY,
+ root->root_key.objectid,
+ dir->i_ino, index, name, namelen);
+
+ BUG_ON(ret);
+
+ /* now add the forward ref */
+ ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
+ root->root_key.objectid, BTRFS_ROOT_REF_KEY,
+ objectid,
+ dir->i_ino, index, name, namelen);
+
+ BUG_ON(ret);
+
ret = btrfs_commit_transaction(trans, root);
if (ret)
goto fail_commit;
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index eb7f7655e9d5..e9be3abcb368 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -84,7 +84,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
int slot;
search_key.objectid = objectid;
- search_key.type = (u8)-1;
+ search_key.type = BTRFS_ROOT_ITEM_KEY;
search_key.offset = (u64)-1;
path = btrfs_alloc_path();
@@ -275,3 +275,77 @@ out:
btrfs_free_path(path);
return ret;
}
+
+int btrfs_del_root_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *tree_root,
+ u64 root_id, u8 type, u64 ref_id)
+{
+ struct btrfs_key key;
+ int ret;
+ struct btrfs_path *path;
+
+ path = btrfs_alloc_path();
+
+ key.objectid = root_id;
+ key.type = type;
+ key.offset = ref_id;
+
+ ret = btrfs_search_slot(trans, tree_root, &key, path, -1, 1);
+ BUG_ON(ret);
+
+ ret = btrfs_del_item(trans, tree_root, path);
+ BUG_ON(ret);
+
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * add a btrfs_root_ref item. type is either BTRFS_ROOT_REF_KEY
+ * or BTRFS_ROOT_BACKREF_KEY.
+ *
+ * The dirid, sequence, name and name_len refer to the directory entry
+ * that is referencing the root.
+ *
+ * For a forward ref, the root_id is the id of the tree referencing
+ * the root and ref_id is the id of the subvol or snapshot.
+ *
+ * For a back ref the root_id is the id of the subvol or snapshot and
+ * ref_id is the id of the tree referencing it.
+ */
+int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *tree_root,
+ u64 root_id, u8 type, u64 ref_id,
+ u64 dirid, u64 sequence,
+ const char *name, int name_len)
+{
+ struct btrfs_key key;
+ int ret;
+ struct btrfs_path *path;
+ struct btrfs_root_ref *ref;
+ struct extent_buffer *leaf;
+ unsigned long ptr;
+
+
+ path = btrfs_alloc_path();
+
+ key.objectid = root_id;
+ key.type = type;
+ key.offset = ref_id;
+
+ ret = btrfs_insert_empty_item(trans, tree_root, path, &key,
+ sizeof(*ref) + name_len);
+ BUG_ON(ret);
+
+ leaf = path->nodes[0];
+ ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
+ btrfs_set_root_ref_dirid(leaf, ref, dirid);
+ btrfs_set_root_ref_sequence(leaf, ref, sequence);
+ btrfs_set_root_ref_name_len(leaf, ref, name_len);
+ ptr = (unsigned long)(ref + 1);
+ write_extent_buffer(leaf, name, ptr, name_len);
+ btrfs_mark_buffer_dirty(leaf);
+
+ btrfs_free_path(path);
+ return ret;
+}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 93f23a456a36..e9c8ebeedd7e 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -831,28 +831,43 @@ static noinline int finish_pending_snapshot(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans;
struct inode *parent_inode;
struct inode *inode;
+ struct btrfs_root *parent_root;
parent_inode = pending->dentry->d_parent->d_inode;
- trans = btrfs_start_transaction(BTRFS_I(parent_inode)->root, 1);
+ parent_root = BTRFS_I(parent_inode)->root;
+ trans = btrfs_start_transaction(parent_root, 1);
/*
* insert the directory item
*/
namelen = strlen(pending->name);
ret = btrfs_set_inode_index(parent_inode, &index);
- ret = btrfs_insert_dir_item(trans,
- BTRFS_I(parent_inode)->root,
+ ret = btrfs_insert_dir_item(trans, parent_root,
pending->name, namelen,
parent_inode->i_ino,
&pending->root_key, BTRFS_FT_DIR, index);
if (ret)
goto fail;
-#if 0
- ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
- pending->name, strlen(pending->name), objectid,
- root->fs_info->sb->s_root->d_inode->i_ino, 0);
-#endif
+
+ /* add the backref first */
+ ret = btrfs_add_root_ref(trans, parent_root->fs_info->tree_root,
+ pending->root_key.objectid,
+ BTRFS_ROOT_BACKREF_KEY,
+ parent_root->root_key.objectid,
+ parent_inode->i_ino, index, pending->name,
+ namelen);
+
+ BUG_ON(ret);
+
+ /* now add the forward ref */
+ ret = btrfs_add_root_ref(trans, parent_root->fs_info->tree_root,
+ parent_root->root_key.objectid,
+ BTRFS_ROOT_REF_KEY,
+ pending->root_key.objectid,
+ parent_inode->i_ino, index, pending->name,
+ namelen);
+
inode = btrfs_lookup_dentry(parent_inode, pending->dentry);
d_instantiate(pending->dentry, inode);
fail: