@@ -1311,8 +1311,9 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
task_deinit(ctx.info);
}
- image_root = btrfs_mksubvol(root, subvol_name,
- CONV_IMAGE_SUBVOL_OBJECTID, true);
+ image_root = btrfs_link_subvol(root, btrfs_root_dirid(&root->root_item),
+ subvol_name, CONV_IMAGE_SUBVOL_OBJECTID,
+ true);
if (!image_root) {
error("unable to link subvolume %s", subvol_name);
goto fail;
@@ -1230,8 +1230,9 @@ int btrfs_add_orphan_item(struct btrfs_trans_handle *trans,
u64 ino);
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);
+struct btrfs_root *btrfs_link_subvol(struct btrfs_root *root, u64 parent_ino,
+ const char *name, u64 root_objectid,
+ bool retry_suffix_names);
int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
struct btrfs_root *fs_root,
u64 dirid, u64 *objectid);
@@ -601,11 +601,19 @@ out:
return ret;
}
-struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
- const char *base, u64 root_objectid,
- bool convert)
+/*
+ * Link a subvolume with @root_objectid to @parent_ino of @root, with the name
+ * @base.
+ *
+ * If @retry_suffix_names is true, and if there is already a dir item with the
+ * same name of @base, then it would retry with an increasing number as suffix,
+ * until a conflict-free name is found, or have tried too many times.
+ */
+struct btrfs_root *btrfs_link_subvol(struct btrfs_root *root, u64 parent_ino,
+ const char *name, u64 root_objectid,
+ bool retry_suffix_names)
{
- struct btrfs_trans_handle *trans;
+ struct btrfs_trans_handle *trans = NULL;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *new_root = NULL;
@@ -613,32 +621,58 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
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;
int i;
int ret;
- len = strlen(base);
- if (len == 0 || len > BTRFS_NAME_LEN)
+ len = strlen(name);
+ if (len == 0 || len > BTRFS_NAME_LEN) {
+ error("invalid name length %u", len);
return NULL;
+ }
- key.objectid = dirid;
+ /* Make sure @parent_ino of @root is a directory. */
+ key.objectid = parent_ino;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+ ret = btrfs_lookup_inode(NULL, root, &path, &key, 0);
+ if (ret > 0) {
+ ret = -ENOENT;
+ /* Fallthrough */
+ }
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to locate directory inode %llu of root %lld: %m",
+ parent_ino, root->root_key.objectid);
+ goto fail;
+ }
+ inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_inode_item);
+ if (!S_ISDIR(btrfs_inode_mode(path.nodes[0], inode_item))) {
+ ret = -EUCLEAN;
+ error("inode %llu of root %lld is not a directory",
+ parent_ino, root->root_key.objectid);
+ goto fail;
+ }
+ btrfs_release_path(&path);
+
+ /* Locate one free dir index number. */
+ key.objectid = parent_ino;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = (u64)-1;
-
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret <= 0) {
error("search for DIR_INDEX dirid %llu failed: %d",
- (unsigned long long)dirid, ret);
+ parent_ino, ret);
goto fail;
}
if (path.slots[0] > 0) {
path.slots[0]--;
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
- if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY)
+ if (key.objectid == parent_ino && key.type == BTRFS_DIR_INDEX_KEY)
index = key.offset + 1;
}
btrfs_release_path(&path);
@@ -651,14 +685,14 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
goto fail;
}
- key.objectid = dirid;
+ key.objectid = parent_ino;
key.offset = 0;
key.type = BTRFS_INODE_ITEM_KEY;
ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
if (ret) {
error("search for INODE_ITEM %llu failed: %d",
- (unsigned long long)dirid, ret);
+ parent_ino, ret);
goto fail;
}
leaf = path.nodes[0];
@@ -669,21 +703,21 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
key.offset = (u64)-1;
key.type = BTRFS_ROOT_ITEM_KEY;
- memcpy(buf, base, len);
- if (convert) {
+ memcpy(buf, name, len);
+ if (retry_suffix_names) {
for (i = 0; i < 1024; i++) {
ret = btrfs_insert_dir_item(trans, root, buf, len,
- dirid, &key, BTRFS_FT_DIR, index);
+ parent_ino, &key, BTRFS_FT_DIR, index);
if (ret != -EEXIST)
break;
- len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", base, i);
+ len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", name, i);
if (len < 1 || len > BTRFS_NAME_LEN) {
ret = -EINVAL;
break;
}
}
} else {
- ret = btrfs_insert_dir_item(trans, root, buf, len, dirid, &key,
+ ret = btrfs_insert_dir_item(trans, root, buf, len, parent_ino, &key,
BTRFS_FT_DIR, index);
}
if (ret)
@@ -698,7 +732,7 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
ret = btrfs_add_root_ref(trans, tree_root, root_objectid,
BTRFS_ROOT_BACKREF_KEY,
root->root_key.objectid,
- dirid, index, buf, len);
+ parent_ino, index, buf, len);
if (ret) {
error("unable to add root backref for %llu: %d",
root->root_key.objectid, ret);
@@ -708,7 +742,7 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
/* now add the forward ref */
ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid,
BTRFS_ROOT_REF_KEY, root_objectid,
- dirid, index, buf, len);
+ parent_ino, index, buf, len);
if (ret) {
error("unable to add root ref for %llu: %d",
root->root_key.objectid, ret);
@@ -728,6 +762,8 @@ struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root,
new_root = NULL;
}
fail:
+ if (trans && ret < 0)
+ btrfs_abort_transaction(trans, ret);
return new_root;
}
The function btrfs_mksubvol() is currently only utilized by btrfs-convert, to create a subvolume to contain the image file. With the incoming support for "mkfs.btrfs --subvolume" option, we need the following features: - Create the subvolume under a specified directory The old can only create the subvolume under the rootdir. - Add some basic sanity checks Making sure the parent inode exists and is a directory inode. And also the function name is confusing, the work of btrfs_mksubvol() is really linking a subvolume under a directory, not really create the full subvolume. This patch would add those needed features, and rename the function to btrfs_link_subvol(). Signed-off-by: Qu Wenruo <wqu@suse.com> --- convert/main.c | 5 +-- kernel-shared/ctree.h | 5 +-- kernel-shared/inode.c | 76 +++++++++++++++++++++++++++++++------------ 3 files changed, 62 insertions(+), 24 deletions(-)