Skip to content

Commit 4c45c11

Browse files
committed
btrfs-progs: mkfs: add --inode-flags option
This new option allows end users to specify certain per-inode flags for specified file/directory inside rootdir. And mkfs will follow the kernel behavior by inheriting the inode flag from the parent. For example: rootdir |- file1 |- file2 |- dir1/ | |- file3 |- subv/ << will be created as a subvolume using --subvol option |- dir2/ | |- file4 |- file5 When `mkfs.btrfs --rootdir rootdir --subvol subv --inode-flags nodatacow:dir1 --inode-flags nodatacow:subv", then the following files and directory will have *nodatacow* flag set: - dir1 - file3 - subv - dir2 - file4 - file5 For now only two flags are supported: - nodatacow Disable data COW, implies *nodatasum* for regular files - nodatasum Disable data checksum only. This also works with --compress option, and files with nodatasum or nodatacow flag will skip compression. Signed-off-by: Qu Wenruo <[email protected]>
1 parent 523b56e commit 4c45c11

File tree

4 files changed

+243
-5
lines changed

4 files changed

+243
-5
lines changed

Documentation/mkfs.btrfs.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,41 @@ OPTIONS
213213
:file:`hardlink1` and :file:`hardlink2` because :file:`hardlink3` will
214214
be inside a new subvolume.
215215

216+
--inode-flags <flags>:<path>
217+
Specify that *path* to have inode *flags*, other than the default one (which
218+
implies data CoW and data checksum). The option *--rootdir* must also be
219+
specified. This option can be speicified multiple times.
220+
221+
The supported flag(s) are:
222+
223+
* *nodatacow*: disable data CoW, implies *nodatasum* for regular files.
224+
* *nodatasum*: disable data checksum only.
225+
226+
*flags* can be separated by comma (',').
227+
228+
Children inodes will inherit the flags from their parent inodes, like the
229+
following case:
230+
231+
.. code-block:: none
232+
233+
rootdir/
234+
|- file1
235+
|- file2
236+
|- dir/
237+
|- file3
238+
239+
In that case, if *--inode-flags nodatacow:dir* is specified, both
240+
:file:`dir` and :file:`file3` will have the *nodatacow* flag.
241+
242+
And this option also works with *--subvol* option, but the inode flag of
243+
each subvolume is independent and will not inherit from the parent directory.
244+
(The same as the kernel behavior)
245+
246+
.. note::
247+
Both *--inode-flags* and *--subvol* options are memory hungry,
248+
will consume at least 8KiB for each option.
249+
Please keep the usage of both options to minimal.
250+
216251
--shrink
217252
Shrink the filesystem to its minimal size, only works with *--rootdir* option.
218253

mkfs/main.c

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,63 @@ static int parse_subvolume(const char *path, struct list_head *subvols,
11641164
return 0;
11651165
}
11661166

1167+
static int parse_inode_flags(const char *option, struct list_head *inode_flags_list)
1168+
{
1169+
struct rootdir_inode_flags_entry *entry = NULL;
1170+
char *colon;
1171+
char *dumpped = NULL;
1172+
char *token;
1173+
int ret;
1174+
1175+
dumpped = strdup(option);
1176+
if (!dumpped) {
1177+
ret = -ENOMEM;
1178+
error_msg(ERROR_MSG_MEMORY, NULL);
1179+
goto cleanup;
1180+
}
1181+
entry = calloc(1, sizeof(*entry));
1182+
if (!entry) {
1183+
ret = -ENOMEM;
1184+
error_msg(ERROR_MSG_MEMORY, NULL);
1185+
goto cleanup;
1186+
}
1187+
colon = strstr(dumpped, ":");
1188+
if (!colon) {
1189+
error("invalid option: %s", option);
1190+
ret = -EINVAL;
1191+
goto cleanup;
1192+
}
1193+
*colon = '\0';
1194+
1195+
token = strtok(dumpped, ",");
1196+
while (token) {
1197+
if (token == NULL)
1198+
break;
1199+
if (strcmp(token, "nodatacow") == 0) {
1200+
entry->nodatacow = true;
1201+
} else if (strcmp(token, "nodatasum") == 0) {
1202+
entry->nodatasum = true;
1203+
} else {
1204+
error("unknown flag: %s", token);
1205+
ret = -EINVAL;
1206+
goto cleanup;
1207+
}
1208+
token = strtok(NULL, ",");
1209+
}
1210+
1211+
if (arg_copy_path(entry->inode_path, colon + 1, sizeof(entry->inode_path))) {
1212+
error("--inode-flags path too long");
1213+
ret = -E2BIG;
1214+
goto cleanup;
1215+
}
1216+
list_add_tail(&entry->list, inode_flags_list);
1217+
return 0;
1218+
cleanup:
1219+
free(dumpped);
1220+
free(entry);
1221+
return ret;
1222+
}
1223+
11671224
int BOX_MAIN(mkfs)(int argc, char **argv)
11681225
{
11691226
char *file;
@@ -1206,10 +1263,12 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12061263
int nr_global_roots = sysconf(_SC_NPROCESSORS_ONLN);
12071264
char *source_dir = NULL;
12081265
struct rootdir_subvol *rds;
1266+
struct rootdir_inode_flags_entry *rif;
12091267
bool has_default_subvol = false;
12101268
enum btrfs_compression_type compression = BTRFS_COMPRESS_NONE;
12111269
unsigned int compression_level = 0;
12121270
LIST_HEAD(subvols);
1271+
LIST_HEAD(inode_flags_list);
12131272

12141273
cpu_detect_flags();
12151274
hash_init_accel();
@@ -1223,6 +1282,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12231282
GETOPT_VAL_CHECKSUM,
12241283
GETOPT_VAL_GLOBAL_ROOTS,
12251284
GETOPT_VAL_DEVICE_UUID,
1285+
GETOPT_VAL_INODE_FLAGS,
12261286
GETOPT_VAL_COMPRESS,
12271287
};
12281288
static const struct option long_options[] = {
@@ -1241,6 +1301,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12411301
{ "version", no_argument, NULL, 'V' },
12421302
{ "rootdir", required_argument, NULL, 'r' },
12431303
{ "subvol", required_argument, NULL, 'u' },
1304+
{ "inode-flags", required_argument, NULL, GETOPT_VAL_INODE_FLAGS },
12441305
{ "nodiscard", no_argument, NULL, 'K' },
12451306
{ "features", required_argument, NULL, 'O' },
12461307
{ "runtime-features", required_argument, NULL, 'R' },
@@ -1374,6 +1435,11 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
13741435
case 'q':
13751436
bconf_be_quiet();
13761437
break;
1438+
case GETOPT_VAL_INODE_FLAGS:
1439+
ret = parse_inode_flags(optarg, &inode_flags_list);
1440+
if (ret)
1441+
goto error;
1442+
break;
13771443
case GETOPT_VAL_COMPRESS:
13781444
if (parse_compression(optarg, &compression, &compression_level)) {
13791445
ret = 1;
@@ -1438,6 +1504,11 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
14381504
ret = 1;
14391505
goto error;
14401506
}
1507+
if (!list_empty(&inode_flags_list) && source_dir == NULL) {
1508+
error("option --inode-flags must be used with --rootdir");
1509+
ret = 1;
1510+
goto error;
1511+
}
14411512

14421513
if (source_dir) {
14431514
char *canonical = realpath(source_dir, NULL);
@@ -1503,6 +1574,41 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
15031574
}
15041575
}
15051576

1577+
list_for_each_entry(rif, &inode_flags_list, list) {
1578+
char path[PATH_MAX];
1579+
struct rootdir_inode_flags_entry *rif2;
1580+
1581+
if (path_cat_out(path, source_dir, rif->inode_path)) {
1582+
ret = -EINVAL;
1583+
error("path invalid: %s", path);
1584+
goto error;
1585+
}
1586+
if (!realpath(path, rif->full_path)) {
1587+
ret = -errno;
1588+
error("could not get canonical path: %s: %m", path);
1589+
goto error;
1590+
}
1591+
if (!path_exists(rif->full_path)) {
1592+
ret = -ENOENT;
1593+
error("inode path does not exist: %s", rif->full_path);
1594+
goto error;
1595+
}
1596+
list_for_each_entry(rif2, &inode_flags_list, list) {
1597+
/*
1598+
* Only compare entryies before us. So we won't compare
1599+
* the same pair twice.
1600+
*/
1601+
if (rif2 == rif)
1602+
break;
1603+
if (strcmp(rif2->full_path, rif->full_path) == 0) {
1604+
error("duplicated inode flag entries for %s",
1605+
rif->full_path);
1606+
ret = -EEXIST;
1607+
goto error;
1608+
}
1609+
}
1610+
}
1611+
15061612
if (*fs_uuid) {
15071613
uuid_t dummy_uuid;
15081614

@@ -2084,9 +2190,15 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
20842190
rds->is_default ? "" : " ",
20852191
rds->dir);
20862192
}
2193+
list_for_each_entry(rif, &inode_flags_list, list) {
2194+
pr_verbose(LOG_DEFAULT, " Inode flags (%s): %s\n",
2195+
rif->nodatacow ? "NODATACOW" : "",
2196+
rif->inode_path);
2197+
}
20872198

20882199
ret = btrfs_mkfs_fill_dir(trans, source_dir, root,
2089-
&subvols, compression,
2200+
&subvols, &inode_flags_list,
2201+
compression,
20902202
compression_level);
20912203
if (ret) {
20922204
errno = -ret;
@@ -2229,6 +2341,12 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
22292341
list_del(&head->list);
22302342
free(head);
22312343
}
2344+
while (!list_empty(&inode_flags_list)) {
2345+
rif = list_entry(inode_flags_list.next,
2346+
struct rootdir_inode_flags_entry, list);
2347+
list_del(&rif->list);
2348+
free(rif);
2349+
}
22322350

22332351
return !!ret;
22342352

mkfs/rootdir.c

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ static struct rootdir_path current_path = {
153153

154154
static struct btrfs_trans_handle *g_trans = NULL;
155155
static struct list_head *g_subvols;
156+
static struct list_head *g_inode_flags_list;
156157
static u64 next_subvol_id = BTRFS_FIRST_FREE_OBJECTID;
157158
static u64 default_subvol_id;
158159
static enum btrfs_compression_type g_compression;
@@ -1296,6 +1297,40 @@ static u8 ftype_to_btrfs_type(mode_t ftype)
12961297
return BTRFS_FT_UNKNOWN;
12971298
}
12981299

1300+
static void update_inode_flags(const struct rootdir_inode_flags_entry *rif,
1301+
struct btrfs_inode_item *stack_inode)
1302+
{
1303+
u64 inode_flags;
1304+
1305+
inode_flags = btrfs_stack_inode_flags(stack_inode);
1306+
if (rif->nodatacow) {
1307+
inode_flags |= BTRFS_INODE_NODATACOW;
1308+
1309+
if (S_ISREG(btrfs_stack_inode_mode(stack_inode)))
1310+
inode_flags |= BTRFS_INODE_NODATASUM;
1311+
}
1312+
if (rif->nodatasum)
1313+
inode_flags |= BTRFS_INODE_NODATASUM;
1314+
1315+
btrfs_set_stack_inode_flags(stack_inode, inode_flags);
1316+
}
1317+
1318+
static void search_and_update_inode_flags(struct btrfs_inode_item *stack_inode,
1319+
const char *full_path)
1320+
{
1321+
struct rootdir_inode_flags_entry *rif;
1322+
1323+
list_for_each_entry(rif, g_inode_flags_list, list) {
1324+
if (strcmp(rif->full_path, full_path) == 0) {
1325+
update_inode_flags(rif, stack_inode);
1326+
1327+
list_del(&rif->list);
1328+
free(rif);
1329+
return;
1330+
}
1331+
}
1332+
}
1333+
12991334
static int ftw_add_subvol(const char *full_path, const struct stat *st,
13001335
int typeflag, struct FTW *ftwbuf,
13011336
struct rootdir_subvol *subvol)
@@ -1354,6 +1389,7 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
13541389
}
13551390
stat_to_inode_item(&inode_item, st);
13561391

1392+
search_and_update_inode_flags(&inode_item, full_path);
13571393
btrfs_set_stack_inode_nlink(&inode_item, 1);
13581394
ret = update_inode_item(g_trans, new_root, &inode_item, ino);
13591395
if (ret < 0) {
@@ -1373,6 +1409,31 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
13731409
return 0;
13741410
}
13751411

1412+
static int read_inode_item(struct btrfs_root *root, struct btrfs_inode_item *inode_item,
1413+
u64 ino)
1414+
{
1415+
struct btrfs_path path = { 0 };
1416+
struct btrfs_key key;
1417+
int ret;
1418+
1419+
key.objectid = ino;
1420+
key.type = BTRFS_INODE_ITEM_KEY;
1421+
key.offset = 0;
1422+
1423+
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
1424+
if (ret > 0)
1425+
ret = -ENOENT;
1426+
if (ret < 0)
1427+
goto out;
1428+
1429+
read_extent_buffer(path.nodes[0], inode_item,
1430+
btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
1431+
sizeof(*inode_item));
1432+
out:
1433+
btrfs_release_path(&path);
1434+
return ret;
1435+
}
1436+
13761437
static int ftw_add_inode(const char *full_path, const struct stat *st,
13771438
int typeflag, struct FTW *ftwbuf)
13781439
{
@@ -1520,6 +1581,7 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
15201581
return ret;
15211582
}
15221583
stat_to_inode_item(&inode_item, st);
1584+
search_and_update_inode_flags(&inode_item, full_path);
15231585

15241586
ret = btrfs_insert_inode(g_trans, root, ino, &inode_item);
15251587
if (ret < 0) {
@@ -1552,11 +1614,17 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
15521614
}
15531615

15541616
/*
1555-
* btrfs_add_link() has increased the nlink to 1 in the metadata.
1556-
* Also update the value in case we need to update the inode item
1557-
* later.
1617+
* btrfs_add_link() has increased the nlink, and may even updated the
1618+
* inode flags (inheritted from the parent).
1619+
* Read out the latest version of inode item.
15581620
*/
1559-
btrfs_set_stack_inode_nlink(&inode_item, 1);
1621+
ret = read_inode_item(root, &inode_item, ino);
1622+
if (ret < 0) {
1623+
errno = -ret;
1624+
error("failed to read inode item for subvol %llu inode %llu ('%s'): %m",
1625+
btrfs_root_id(root), ino, full_path);
1626+
return ret;
1627+
}
15601628

15611629
ret = add_xattr_item(g_trans, root, ino, full_path);
15621630
if (ret < 0) {
@@ -1649,6 +1717,7 @@ static int set_default_subvolume(struct btrfs_trans_handle *trans)
16491717

16501718
int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir,
16511719
struct btrfs_root *root, struct list_head *subvols,
1720+
struct list_head *inode_flags_list,
16521721
enum btrfs_compression_type compression,
16531722
unsigned int compression_level)
16541723
{
@@ -1695,6 +1764,7 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
16951764

16961765
g_trans = trans;
16971766
g_subvols = subvols;
1767+
g_inode_flags_list = inode_flags_list;
16981768
g_compression = compression;
16991769
g_compression_level = compression_level;
17001770
INIT_LIST_HEAD(&current_path.inode_list);

mkfs/rootdir.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,23 @@ struct rootdir_subvol {
4545
bool readonly;
4646
};
4747

48+
/*
49+
* Represent a flag for specified inode at @full_path.
50+
*/
51+
struct rootdir_inode_flags_entry {
52+
struct list_head list;
53+
/* Fully canonicalized path to the source file. */
54+
char full_path[PATH_MAX];
55+
/* Path inside the source directory. */
56+
char inode_path[PATH_MAX];
57+
58+
bool nodatacow;
59+
bool nodatasum;
60+
};
61+
4862
int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir,
4963
struct btrfs_root *root, struct list_head *subvols,
64+
struct list_head *inode_flags_list,
5065
enum btrfs_compression_type compression,
5166
unsigned int compression_level);
5267
u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,

0 commit comments

Comments
 (0)