Skip to content

Commit 7e729e9

Browse files
committed
btrfs-progs: use btrfs_link_subvolume() to replace btrfs_mksubvol()
The function btrfs_mksubvol() is very different between btrfs-progs and kernel, the former version is really just linking a subvolume to another directory inode, but the kernel version is really to make a completely new subvolume. Instead of same-named function, introduce btrfs_link_subvolume() and use it to replace the old btrfs_mksubvol(). This is done by: - Introduce btrfs_link_subvolume() Which would do extra checks before doing any modification: * Make sure the target inode is a directory * Make sure no filename conflict Then do the linkage: * Add the dir_item/dir_index into the parent inode * Add the forward and backward root refs into tree root - Introduce link_image_subvolume() helper Currently btrfs_mksubvol() has a dedicated convert filename retry behavior, which is unnecessary and should be done by the convert code. Now move the filename retry behavior into the helper. - Remove btrfs_mksubvol() Since there is only one caller utilizing btrfs_mksubvol(), and it's now gone, we can remove the old btrfs_mksubvol(). Signed-off-by: Qu Wenruo <[email protected]>
1 parent 299b3b3 commit 7e729e9

File tree

6 files changed

+184
-147
lines changed

6 files changed

+184
-147
lines changed

check/main.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -2361,10 +2361,10 @@ static int repair_inode_backrefs(struct btrfs_root *root,
23612361
struct btrfs_trans_handle *trans;
23622362
struct btrfs_key location;
23632363

2364-
ret = check_dir_conflict(root, backref->name,
2365-
backref->namelen,
2366-
backref->dir,
2367-
backref->index);
2364+
ret = btrfs_check_dir_conflict(root, backref->name,
2365+
backref->namelen,
2366+
backref->dir,
2367+
backref->index);
23682368
if (ret) {
23692369
/*
23702370
* let nlink fixing routine to handle it,

common/root-tree-utils.c

+107
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,110 @@ int btrfs_make_subvolume(struct btrfs_trans_handle *trans, u64 objectid)
105105
btrfs_abort_transaction(trans, ret);
106106
return ret;
107107
}
108+
109+
/*
110+
* Link subvoume @subvol as @name under directory inode @parent_dir of
111+
* subvolume @parent_root.
112+
*/
113+
int btrfs_link_subvolume(struct btrfs_trans_handle *trans,
114+
struct btrfs_root *parent_root,
115+
u64 parent_dir, const char *name,
116+
int namelen, struct btrfs_root *subvol)
117+
{
118+
struct btrfs_root *tree_root = trans->fs_info->tree_root;
119+
struct btrfs_path path = { 0 };
120+
struct btrfs_key key;
121+
struct btrfs_inode_item *ii;
122+
u64 index;
123+
u64 isize;
124+
u32 imode;
125+
int ret;
126+
127+
UASSERT(namelen && namelen <= BTRFS_NAME_LEN);
128+
129+
/* Make sure @parent_dir is a directory. */
130+
key.objectid = parent_dir;
131+
key.type = BTRFS_INODE_ITEM_KEY;
132+
key.offset = 0;
133+
ret = btrfs_search_slot(NULL, parent_root, &key, &path, 0, 0);
134+
if (ret > 0)
135+
ret = -ENOENT;
136+
if (ret < 0) {
137+
btrfs_release_path(&path);
138+
return ret;
139+
}
140+
ii = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item);
141+
imode = btrfs_inode_mode(path.nodes[0], ii);
142+
btrfs_release_path(&path);
143+
144+
if (!S_ISDIR(imode)) {
145+
ret = -EUCLEAN;
146+
error("%s: inode %llu of subvolume %llu is not a directory",
147+
__func__, parent_dir, parent_root->root_key.objectid);
148+
return ret;
149+
}
150+
151+
ret = btrfs_find_free_dir_index(parent_root, parent_dir, &index);
152+
if (ret < 0)
153+
return ret;
154+
155+
/* Filename conflicts check. */
156+
ret = btrfs_check_dir_conflict(parent_root, name, namelen, parent_dir,
157+
index);
158+
if (ret < 0)
159+
return ret;
160+
161+
/*
162+
* Now everything is fine, add the link.
163+
* From now on, every error would lead to transaction abort.
164+
*
165+
* Add the dir_item/index first.
166+
*/
167+
ret = btrfs_insert_dir_item(trans, parent_root, name, namelen,
168+
parent_dir, &subvol->root_key,
169+
BTRFS_FT_DIR, index);
170+
if (ret < 0)
171+
goto abort;
172+
173+
/* Update inode size of the parent inode */
174+
key.objectid = parent_dir;
175+
key.type = BTRFS_INODE_ITEM_KEY;
176+
key.offset = 0;
177+
ret = btrfs_search_slot(trans, parent_root, &key, &path, 1, 1);
178+
if (ret > 0)
179+
ret = -ENOENT;
180+
if (ret < 0) {
181+
btrfs_release_path(&path);
182+
goto abort;
183+
}
184+
ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
185+
struct btrfs_inode_item);
186+
isize = btrfs_inode_size(path.nodes[0], ii);
187+
isize += namelen * 2;
188+
btrfs_set_inode_size(path.nodes[0], ii, isize);
189+
btrfs_mark_buffer_dirty(path.nodes[0]);
190+
btrfs_release_path(&path);
191+
192+
/* Add the root backref. */
193+
ret = btrfs_add_root_ref(trans, tree_root, subvol->root_key.objectid,
194+
BTRFS_ROOT_BACKREF_KEY,
195+
parent_root->root_key.objectid, parent_dir,
196+
index, name, namelen);
197+
if (ret < 0)
198+
goto abort;
199+
200+
/* Then forward ref*/
201+
ret = btrfs_add_root_ref(trans, tree_root,
202+
parent_root->root_key.objectid,
203+
BTRFS_ROOT_REF_KEY, subvol->root_key.objectid,
204+
parent_dir, index, name, namelen);
205+
if (ret < 0)
206+
goto abort;
207+
/* For now, all root should have its refs == 1 already.
208+
* So no need to update the root refs. */
209+
UASSERT(btrfs_root_refs(&subvol->root_item) == 1);
210+
return 0;
211+
abort:
212+
btrfs_abort_transaction(trans, ret);
213+
return ret;
214+
}

common/root-tree-utils.h

+4
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@
2222
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
2323
struct btrfs_root *root, u64 objectid);
2424
int btrfs_make_subvolume(struct btrfs_trans_handle *trans, u64 objectid);
25+
int btrfs_link_subvolume(struct btrfs_trans_handle *trans,
26+
struct btrfs_root *parent_root,
27+
u64 parent_dir, const char *name,
28+
int namelen, struct btrfs_root *subvol);
2529

2630
#endif

convert/main.c

+60-4
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,62 @@ static int convert_open_fs(const char *devname,
11101110
return -1;
11111111
}
11121112

1113+
static int link_image_subvolume(struct btrfs_root *image_root, char *name)
1114+
{
1115+
struct btrfs_fs_info *fs_info = image_root->fs_info;
1116+
struct btrfs_root *fs_root = fs_info->fs_root;
1117+
struct btrfs_trans_handle *trans;
1118+
char buf[BTRFS_NAME_LEN + 1]; /* for snprintf, null terminated. */
1119+
int ret;
1120+
1121+
/*
1122+
* 1 root backref 1 root forward ref, 1 dir item 1 dir index,
1123+
* and finally one root item.
1124+
*/
1125+
trans = btrfs_start_transaction(fs_root, 5);
1126+
if (IS_ERR(trans)) {
1127+
ret = PTR_ERR(trans);
1128+
errno = -ret;
1129+
error("failed to start transaction to link subvolume: %m");
1130+
return PTR_ERR(trans);
1131+
}
1132+
ret = btrfs_link_subvolume(trans, fs_root, btrfs_root_dirid(&fs_root->root_item),
1133+
name, strlen(name), image_root);
1134+
/* No filename conflicts, all done. */
1135+
if (!ret)
1136+
goto commit;
1137+
/* Other unexpected errors. */
1138+
if (ret != -EEXIST) {
1139+
btrfs_abort_transaction(trans, ret);
1140+
return ret;
1141+
}
1142+
1143+
/* Filename conflicts detected, try different names. */
1144+
for (int i = 0; i < 1024; i++) {
1145+
int len;
1146+
1147+
len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", name, i);
1148+
if (len == 0 || len > BTRFS_NAME_LEN) {
1149+
error("failed to find an alternative name for image_root");
1150+
ret = -EINVAL;
1151+
goto abort;
1152+
}
1153+
ret = btrfs_link_subvolume(trans, fs_root,
1154+
btrfs_root_dirid(&fs_root->root_item),
1155+
buf, len, image_root);
1156+
if (!ret)
1157+
break;
1158+
if (ret != -EEXIST)
1159+
goto abort;
1160+
}
1161+
commit:
1162+
ret = btrfs_commit_transaction(trans, fs_info->tree_root);
1163+
return ret;
1164+
abort:
1165+
btrfs_abort_transaction(trans, ret);
1166+
return ret;
1167+
}
1168+
11131169
static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
11141170
const char *fslabel, int progress,
11151171
struct btrfs_mkfs_features *features, u16 csum_type,
@@ -1276,10 +1332,10 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
12761332
task_deinit(ctx.info);
12771333
}
12781334

1279-
image_root = btrfs_mksubvol(root, subvol_name,
1280-
CONV_IMAGE_SUBVOL_OBJECTID, true);
1281-
if (!image_root) {
1282-
error("unable to link subvolume %s", subvol_name);
1335+
ret = link_image_subvolume(image_root, subvol_name);
1336+
if (ret < 0) {
1337+
errno = -ret;
1338+
error("unable to link subvolume %s: %m", subvol_name);
12831339
goto fail;
12841340
}
12851341

kernel-shared/ctree.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -1212,8 +1212,10 @@ static inline int is_fstree(u64 rootid)
12121212
void btrfs_uuid_to_key(const u8 *uuid, u8 type, struct btrfs_key *key);
12131213

12141214
/* inode.c */
1215-
int check_dir_conflict(struct btrfs_root *root, char *name, int namelen,
1216-
u64 dir, u64 index);
1215+
int btrfs_find_free_dir_index(struct btrfs_root *root, u64 dir_ino,
1216+
u64 *ret_ino);
1217+
int btrfs_check_dir_conflict(struct btrfs_root *root, const char *name,
1218+
int namelen, u64 dir, u64 index);
12171219
int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
12181220
u64 ino, u32 mode);
12191221
int btrfs_change_inode_flags(struct btrfs_trans_handle *trans,
@@ -1229,8 +1231,6 @@ int btrfs_add_orphan_item(struct btrfs_trans_handle *trans,
12291231
u64 ino);
12301232
int btrfs_mkdir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
12311233
char *name, int namelen, u64 parent_ino, u64 *ino, int mode);
1232-
struct btrfs_root *btrfs_mksubvol(struct btrfs_root *root, const char *base,
1233-
u64 root_objectid, bool convert);
12341234
int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
12351235
struct btrfs_root *fs_root,
12361236
u64 dirid, u64 *objectid);

0 commit comments

Comments
 (0)