Skip to content

Commit d6c523b

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: mkfs/rootdir: enhance subvols detection
Currently for --subvol parameter, we save the full path of the host fs into rootdir_subvol sturcture, then compare each inode we hit with that saved full path. This string comparison can be time consuming (up to PATH_MAX characters), and we're doing it for every directory inode. On the other hand, nftw() also provides a stat structure of the current inode, stat::st_dev and stat::st_ino pair can always uniquely locate an inode in the host filesystem. With that said, we can just save the st_dev/st_ino pair when validating the subvol parameters, and use st_dev/st_ino to check if we hit the target subvolume. This has two benefits: - Reduce the memory usage of each rootdir_subvol Now we need no full_path member, replacing it with two u64. This saves (4K - 16) bytes per rootdir_subvol. - Reduce the runtime of direct inode comparison Instead of doing strcmp() for up to 4K bytes, it's just two u64 comparison. Signed-off-by: Qu Wenruo <[email protected]>
1 parent a363b7c commit d6c523b

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

mkfs/rootdir.c

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,8 @@ int btrfs_mkfs_validate_subvols(const char *source_dir, struct list_head *subvol
10831083

10841084
list_for_each_entry(rds, subvols, list) {
10851085
char path[PATH_MAX];
1086+
char full_path[PATH_MAX];
1087+
struct stat stbuf;
10861088
struct rootdir_subvol *rds2;
10871089
int ret;
10881090

@@ -1092,29 +1094,37 @@ int btrfs_mkfs_validate_subvols(const char *source_dir, struct list_head *subvol
10921094
error("path invalid '%s': %m", path);
10931095
return ret;
10941096
}
1095-
if (!realpath(path, rds->full_path)) {
1097+
if (!realpath(path, full_path)) {
10961098
ret = -errno;
10971099
error("could not get canonical path of '%s': %m", rds->dir);
10981100
return ret;
10991101
}
1100-
ret = path_exists(rds->full_path);
1102+
ret = path_exists(full_path);
11011103
if (ret < 0) {
11021104
error("subvolume path does not exist: %s", rds->dir);
11031105
return ret;
11041106
}
1105-
ret = path_is_dir(rds->full_path);
1107+
ret = path_is_dir(full_path);
11061108
if (ret < 0) {
11071109
error("subvolume is not a directory: %s", rds->dir);
11081110
return ret;
11091111
}
1112+
ret = lstat(full_path, &stbuf);
1113+
if (ret < 0) {
1114+
ret = -errno;
1115+
error("failed to get stat of '%s': %m", full_path);
1116+
return ret;
1117+
}
1118+
rds->st_dev = stbuf.st_dev;
1119+
rds->st_ino = stbuf.st_ino;
11101120
list_for_each_entry(rds2, subvols, list) {
11111121
/*
11121122
* Only compare entries before us, So we won't compare
11131123
* the same pair twice.
11141124
*/
11151125
if (rds2 == rds)
11161126
break;
1117-
if (strcmp(rds2->full_path, rds->full_path) == 0) {
1127+
if (rds2->st_dev == rds->st_dev && rds2->st_ino == rds->st_ino) {
11181128
error("subvolume specified more than once: %s", rds->dir);
11191129
return -EINVAL;
11201130
}
@@ -1424,15 +1434,26 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
14241434
struct btrfs_root *new_root;
14251435
struct inode_entry *parent;
14261436
struct btrfs_inode_item inode_item = { 0 };
1437+
char *path_dump;
1438+
char *base_path;
14271439
u64 subvol_id, ino;
14281440

14291441
subvol_id = next_subvol_id++;
1442+
path_dump = strdup(full_path);
1443+
if (!path_dump)
1444+
return -ENOMEM;
1445+
base_path = path_basename(path_dump);
1446+
if (!base_path) {
1447+
ret = -errno;
1448+
error("failed to get basename of '%s': %m", path_dump);
1449+
goto out;
1450+
}
14301451

14311452
ret = btrfs_make_subvolume(g_trans, subvol_id, subvol->readonly);
14321453
if (ret < 0) {
14331454
errno = -ret;
14341455
error("failed to create subvolume: %m");
1435-
return ret;
1456+
goto out;
14361457
}
14371458

14381459
if (subvol->is_default)
@@ -1447,19 +1468,18 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
14471468
ret = PTR_ERR(new_root);
14481469
errno = -ret;
14491470
error("unable to read fs root id %llu: %m", subvol_id);
1450-
return ret;
1471+
goto out;
14511472
}
14521473

14531474
parent = rootdir_path_last(&current_path);
14541475

14551476
ret = btrfs_link_subvolume(g_trans, parent->root, parent->ino,
1456-
path_basename(subvol->full_path),
1457-
strlen(path_basename(subvol->full_path)),
1477+
base_path, strlen(base_path),
14581478
new_root);
14591479
if (ret) {
14601480
errno = -ret;
1461-
error("unable to link subvolume %s: %m", path_basename(subvol->full_path));
1462-
return ret;
1481+
error("unable to link subvolume %s: %m", base_path);
1482+
goto out;
14631483
}
14641484

14651485
ino = btrfs_root_dirid(&new_root->root_item);
@@ -1469,7 +1489,7 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
14691489
errno = -ret;
14701490
error("failed to add xattr item for the top level inode in subvol %llu: %m",
14711491
subvol_id);
1472-
return ret;
1492+
goto out;
14731493
}
14741494
stat_to_inode_item(&inode_item, st);
14751495

@@ -1479,18 +1499,20 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
14791499
if (ret < 0) {
14801500
errno = -ret;
14811501
error("failed to update root dir for root %llu: %m", subvol_id);
1482-
return ret;
1502+
goto out;
14831503
}
14841504

14851505
ret = rootdir_path_push(&current_path, new_root, ino);
14861506
if (ret < 0) {
14871507
errno = -ret;
14881508
error("failed to allocate new entry for subvolume %llu ('%s'): %m",
14891509
subvol_id, full_path);
1490-
return ret;
1510+
goto out;
14911511
}
14921512

1493-
return 0;
1513+
out:
1514+
free(path_dump);
1515+
return ret;
14941516
}
14951517

14961518
static int read_inode_item(struct btrfs_root *root, struct btrfs_inode_item *inode_item,
@@ -1611,7 +1633,7 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
16111633

16121634
if (S_ISDIR(st->st_mode)) {
16131635
list_for_each_entry(rds, g_subvols, list) {
1614-
if (!strcmp(full_path, rds->full_path)) {
1636+
if (st->st_dev == rds->st_dev && st->st_ino == rds->st_ino) {
16151637
ret = ftw_add_subvol(full_path, st, typeflag,
16161638
ftwbuf, rds);
16171639

mkfs/rootdir.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ struct rootdir_subvol {
4141
struct list_head list;
4242
/* The path inside the source_dir. */
4343
char dir[PATH_MAX];
44-
char full_path[PATH_MAX];
44+
/* st_dev and st_ino is going to uniquely determine an inode inside the host fs. */
45+
dev_t st_dev;
46+
ino_t st_ino;
4547
bool is_default;
4648
bool readonly;
4749
};

0 commit comments

Comments
 (0)