Skip to content

Commit 16ee6b5

Browse files
maharmstonekdave
authored andcommitted
btrfs-progs: mkfs: add --reflink option
When we are using mkfs.btrfs with --rootdir to create an image, we read each file into memory and write it back into the new image. Add an option --reflink, which uses FICLONERANGE to do this instead. This obviously only works if the source directory and image are located on the same volume, and if the filesystem supports FICLONERANGE. We also have to handle the last sector manually if the file isn't a whole number of sectors, as the ioctl can't handle this if we're writing into the middle of a file. Pull-request: #1023 Signed-off-by: Mark Harmstone <mark@harmstone.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 7ab87a0 commit 16ee6b5

File tree

3 files changed

+105
-7
lines changed

3 files changed

+105
-7
lines changed

mkfs/main.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ static const char * const mkfs_usage[] = {
473473
OPTLINE("", "- nodatacow - disable data CoW, implies nodatasum for regular files"),
474474
OPTLINE("", "- nodatasum - disable data checksum only"),
475475
OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"),
476+
OPTLINE("--reflink", "(with --rootdir) write file data by cloning ranges"),
476477
OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"),
477478
OPTLINE("-f|--force", "force overwrite of existing filesystem"),
478479
"",
@@ -1235,7 +1236,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12351236
int close_ret;
12361237
int i;
12371238
bool ssd = false;
1238-
bool shrink_rootdir = false;
1239+
bool shrink_rootdir = false, do_reflink = false;
12391240
u64 source_dir_size = 0;
12401241
u64 min_dev_size;
12411242
u64 shrink_size;
@@ -1287,6 +1288,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12871288
GETOPT_VAL_DEVICE_UUID,
12881289
GETOPT_VAL_INODE_FLAGS,
12891290
GETOPT_VAL_COMPRESS,
1291+
GETOPT_VAL_REFLINK,
12901292
};
12911293
static const struct option long_options[] = {
12921294
{ "byte-count", required_argument, NULL, 'b' },
@@ -1316,6 +1318,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
13161318
{ "shrink", no_argument, NULL, GETOPT_VAL_SHRINK },
13171319
{ "compress", required_argument, NULL,
13181320
GETOPT_VAL_COMPRESS },
1321+
{ "reflink", no_argument, NULL, GETOPT_VAL_REFLINK },
13191322
#if EXPERIMENTAL
13201323
{ "param", required_argument, NULL, GETOPT_VAL_PARAM },
13211324
{ "num-global-roots", required_argument, NULL, GETOPT_VAL_GLOBAL_ROOTS },
@@ -1465,6 +1468,9 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
14651468
case GETOPT_VAL_PARAM:
14661469
bconf_save_param(optarg);
14671470
break;
1471+
case GETOPT_VAL_REFLINK:
1472+
do_reflink = true;
1473+
break;
14681474
case GETOPT_VAL_HELP:
14691475
default:
14701476
usage(&mkfs_cmd, c != GETOPT_VAL_HELP);
@@ -1502,6 +1508,11 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
15021508
ret = 1;
15031509
goto error;
15041510
}
1511+
if (do_reflink && source_dir == NULL) {
1512+
error("the option --reflink must be used with --rootdir");
1513+
ret = 1;
1514+
goto error;
1515+
}
15051516
if (!list_empty(&subvols) && source_dir == NULL) {
15061517
error("option --subvol must be used with --rootdir");
15071518
ret = 1;
@@ -2135,7 +2146,8 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
21352146

21362147
ret = btrfs_mkfs_fill_dir(trans, source_dir, root,
21372148
&subvols, &inode_flags_list,
2138-
compression, compression_level);
2149+
compression, compression_level,
2150+
do_reflink);
21392151
if (ret) {
21402152
errno = -ret;
21412153
error("error while filling filesystem: %m");

mkfs/rootdir.c

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "kerncompat.h"
2020
#include <sys/stat.h>
2121
#include <sys/xattr.h>
22+
#include <sys/ioctl.h>
2223
#include <dirent.h>
2324
#include <unistd.h>
2425
#include <fcntl.h>
@@ -158,6 +159,7 @@ static u64 next_subvol_id = BTRFS_FIRST_FREE_OBJECTID;
158159
static u64 default_subvol_id;
159160
static enum btrfs_compression_type g_compression;
160161
static u64 g_compression_level;
162+
static bool g_do_reflink;
161163

162164
static inline struct inode_entry *rootdir_path_last(struct rootdir_path *path)
163165
{
@@ -702,6 +704,83 @@ struct source_descriptor {
702704
char *wrkmem;
703705
};
704706

707+
static int do_reflink_write(struct btrfs_fs_info *info,
708+
const struct source_descriptor *source, u64 addr,
709+
u64 file_pos, u64 bytes, const void *buf)
710+
{
711+
struct btrfs_multi_bio *multi = NULL;
712+
struct btrfs_device *device;
713+
u64 bytes_left;
714+
u64 this_len;
715+
u64 total_write = 0;
716+
u64 dev_bytenr;
717+
int dev_nr;
718+
int ret = 0;
719+
struct file_clone_range fcr;
720+
721+
fcr.src_fd = source->fd;
722+
723+
bytes_left = round_down(bytes, info->sectorsize);
724+
725+
while (bytes_left > 0) {
726+
this_len = bytes_left;
727+
dev_nr = 0;
728+
729+
ret = btrfs_map_block(info, WRITE, addr, &this_len, &multi, 0, NULL);
730+
if (ret) {
731+
error("cannot map the block %llu", addr);
732+
return -EIO;
733+
}
734+
735+
while (dev_nr < multi->num_stripes) {
736+
device = multi->stripes[dev_nr].dev;
737+
if (device->fd <= 0) {
738+
kfree(multi);
739+
return -EIO;
740+
}
741+
742+
dev_bytenr = multi->stripes[dev_nr].physical;
743+
this_len = min(this_len, bytes_left);
744+
dev_nr++;
745+
device->total_ios++;
746+
747+
fcr.src_offset = file_pos + total_write;
748+
fcr.src_length = this_len;
749+
fcr.dest_offset = dev_bytenr;
750+
751+
ret = ioctl(device->fd, FICLONERANGE, &fcr);
752+
if (ret < 0) {
753+
error("cannot clone range: %m");
754+
ret = -errno;
755+
kfree(multi);
756+
return ret;
757+
}
758+
}
759+
760+
BUG_ON(bytes_left < this_len);
761+
762+
bytes_left -= this_len;
763+
addr += this_len;
764+
total_write += this_len;
765+
766+
kfree(multi);
767+
multi = NULL;
768+
}
769+
770+
/*
771+
* FICLONERANGE can only handle whole sectors. If the file is not a
772+
* multiple of the sector size, we need to write the last sector
773+
* manually.
774+
*/
775+
if (bytes % info->sectorsize) {
776+
return write_data_to_disk(info,
777+
(char *)buf + round_down(bytes, info->sectorsize),
778+
addr, info->sectorsize);
779+
}
780+
781+
return 0;
782+
}
783+
705784
static int add_file_item_extent(struct btrfs_trans_handle *trans,
706785
struct btrfs_root *root,
707786
struct btrfs_inode_item *btrfs_inode,
@@ -721,7 +800,7 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
721800
ssize_t comp_ret;
722801
u64 flags = btrfs_stack_inode_flags(btrfs_inode);
723802

724-
if (flags & BTRFS_INODE_NOCOMPRESS)
803+
if (g_do_reflink || flags & BTRFS_INODE_NOCOMPRESS)
725804
do_comp = false;
726805

727806
if ((flags & BTRFS_INODE_NODATACOW) || (flags & BTRFS_INODE_NODATASUM)) {
@@ -852,8 +931,14 @@ static int add_file_item_extent(struct btrfs_trans_handle *trans,
852931

853932
first_block = key.objectid;
854933

855-
ret = write_data_to_disk(root->fs_info, write_buf, first_block,
856-
to_write);
934+
if (g_do_reflink) {
935+
ret = do_reflink_write(root->fs_info, source, first_block,
936+
file_pos, to_read, write_buf);
937+
} else {
938+
ret = write_data_to_disk(root->fs_info, write_buf, first_block,
939+
to_write);
940+
}
941+
857942
if (ret) {
858943
error("failed to write %s", source->path_name);
859944
return ret;
@@ -1835,7 +1920,7 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
18351920
struct btrfs_root *root, struct list_head *subvols,
18361921
struct list_head *inode_flags_list,
18371922
enum btrfs_compression_type compression,
1838-
unsigned int compression_level)
1923+
unsigned int compression_level, bool do_reflink)
18391924
{
18401925
int ret;
18411926
struct stat root_st;
@@ -1883,6 +1968,7 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
18831968
g_inode_flags_list = inode_flags_list;
18841969
g_compression = compression;
18851970
g_compression_level = compression_level;
1971+
g_do_reflink = do_reflink;
18861972
INIT_LIST_HEAD(&current_path.inode_list);
18871973

18881974
ret = nftw(source_dir, ftw_add_inode, 32, FTW_PHYS);

mkfs/rootdir.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
6969
struct btrfs_root *root, struct list_head *subvols,
7070
struct list_head *inode_flags_list,
7171
enum btrfs_compression_type compression,
72-
unsigned int compression_level);
72+
unsigned int compression_level, bool do_reflink);
7373
u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
7474
u64 meta_profile, u64 data_profile);
7575
int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,

0 commit comments

Comments
 (0)