@@ -1002,6 +1002,63 @@ err:
return ret;
}
+/*
+ * Link the subvolume specified by @root_objectid to the root_dir of @root.
+ *
+ * @root the root of the file tree which the subvolume will
+ * be linked to.
+ * @subvol_name the name of the subvolume which will be linked.
+ * @root_objectid specify the subvolume which will be linked.
+ * @convert the flag to determine whether to try to resolve
+ * the name conflict.
+ *
+ * Return the root of the subvolume if success, otherwise return NULL.
+ */
+static struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
+ const char *subvol_name,
+ u64 root_objectid,
+ bool convert)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *subvol_root = NULL;
+ struct btrfs_key key;
+ u64 dirid = btrfs_root_dirid(&root->root_item);
+ int ret;
+
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ error("unable to start transaction");
+ goto fail;
+ }
+
+ ret = btrfs_link_subvol(trans, root, subvol_name, root_objectid, dirid,
+ true);
+ if (ret) {
+ error("unable to link subvolume %s", subvol_name);
+ goto fail;
+ }
+
+ ret = btrfs_commit_transaction(trans, root);
+ if (ret) {
+ error("transaction commit failed: %d", ret);
+ goto fail;
+ }
+
+ key.objectid = root_objectid;
+ key.offset = (u64)-1;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+
+ subvol_root = btrfs_read_fs_root(root->fs_info, &key);
+ if (!subvol_root) {
+ error("unable to link subvolume %s", subvol_name);
+ goto fail;
+ }
+
+fail:
+ return subvol_root;
+}
+
/*
* Migrate super block to its default position and zero 0 ~ 16k
*/
@@ -2788,8 +2788,9 @@ int btrfs_del_orphan_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 offset);
int btrfs_mkdir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
char *name, int namelen, u64 parent_ino, u64 *ino, int mode);
-struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root, const char *base,
- u64 root_objectid, bool convert);
+int btrfs_link_subvol(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+ const char *base, u64 root_objectid, u64 dirid,
+ bool convert);
/* file.c */
int btrfs_get_extent(struct btrfs_trans_handle *trans,
@@ -606,19 +606,28 @@ out:
return ret;
}
-struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
- const char *base, u64 root_objectid,
- bool convert)
+/*
+ * Link the subvolume specified by @root_objectid to the directory specified by
+ * @dirid on the file tree specified by @root.
+ *
+ * @root the root of the file tree where the directory on.
+ * @base the name of the subvolume which will be linked.
+ * @root_objectid specify the subvolume which will be linked.
+ * @dirid specify the directory which the subvolume will be
+ * linked to.
+ * @convert the flag to determine whether to try to resolve
+ * the name conflict.
+ */
+int btrfs_link_subvol(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+ const char *base, u64 root_objectid, u64 dirid,
+ bool convert)
{
- struct btrfs_trans_handle *trans;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *tree_root = fs_info->tree_root;
- struct btrfs_root *new_root = NULL;
struct btrfs_path path;
struct btrfs_inode_item *inode_item;
struct extent_buffer *leaf;
struct btrfs_key key;
- u64 dirid = btrfs_root_dirid(&root->root_item);
u64 index = 2;
char buf[BTRFS_NAME_LEN + 1]; /* for snprintf null */
int len;
@@ -627,8 +636,9 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
len = strlen(base);
if (len == 0 || len > BTRFS_NAME_LEN)
- return NULL;
+ return -EINVAL;
+ /* find the free dir_index */
btrfs_init_path(&path);
key.objectid = dirid;
key.type = BTRFS_DIR_INDEX_KEY;
@@ -649,12 +659,7 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
}
btrfs_release_path(&path);
- trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- error("unable to start transaction");
- goto fail;
- }
-
+ /* add the dir_item/dir_index */
key.objectid = dirid;
key.offset = 0;
key.type = BTRFS_INODE_ITEM_KEY;
@@ -675,6 +680,7 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
memcpy(buf, base, len);
if (convert) {
+ /* try to resolve name conflict by adding the number suffix */
for (i = 0; i < 1024; i++) {
ret = btrfs_insert_dir_item(trans, root, buf, len,
dirid, &key, BTRFS_FT_DIR, index);
@@ -719,18 +725,8 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
goto fail;
}
- ret = btrfs_commit_transaction(trans, root);
- if (ret) {
- error("transaction commit failed: %d", ret);
- goto fail;
- }
- new_root = btrfs_read_fs_root(fs_info, &key);
- if (IS_ERR(new_root)) {
- error("unable to fs read root: %lu", PTR_ERR(new_root));
- new_root = NULL;
- }
fail:
- btrfs_init_path(&path);
- return new_root;
+ btrfs_release_path(&path);
+ return ret;
}
The original btrfs_mksubvol is too specific to specify the directory that the subvolume will link to. Furthermore, in this transaction, we don't only need to create root_ref/dir-item, but also update the refs or flags of root_item. Extract a generic btrfs_link_subvol that allow the caller pass a trans argument for later subvolume undelete. No functional changes for the btrfs_mksubvol. Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com> --- convert/main.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ctree.h | 5 +++-- inode.c | 46 +++++++++++++++++++++------------------------- 3 files changed, 81 insertions(+), 27 deletions(-)