diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2025-04-28 13:38:04 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2025-07-12 19:49:55 -0400 |
commit | 3321afc8b6e43fe71e744f19c7df6c7dc3146271 (patch) | |
tree | 08b3fed3a3cc6068f4a8cb3ffee6b5cc6a7d9200 | |
parent | c1a25673e937090449d4cd858c7cd89bc570d59d (diff) |
cmd_image_update
New tool: 'bcachefs image update', for updating images produced by
'bcachefs image create': this tool will update images while making the
minimal changes to the on disk image, to make tools like casync more
efficient.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | c_src/bcachefs.c | 1 | ||||
-rw-r--r-- | c_src/cmd_image.c | 181 |
2 files changed, 181 insertions, 1 deletions
diff --git a/c_src/bcachefs.c b/c_src/bcachefs.c index 9e9cf647..58fa2549 100644 --- a/c_src/bcachefs.c +++ b/c_src/bcachefs.c @@ -40,6 +40,7 @@ void bcachefs_usage(void) "\n" "Commands for managing images:\n" " image create Create a new compact disk image\n" + " image update Sync an image with a directory tree\n" "\n" "Mount:\n" " mount Mount a filesystem\n" diff --git a/c_src/cmd_image.c b/c_src/cmd_image.c index 2d1839ad..58268b4b 100644 --- a/c_src/cmd_image.c +++ b/c_src/cmd_image.c @@ -26,6 +26,7 @@ #include "crypto.h" #include "libbcachefs/alloc_background.h" #include "libbcachefs/alloc_foreground.h" +#include "libbcachefs/btree_update.h" #include "libbcachefs/data_update.h" #include "libbcachefs/disk_accounting.h" #include "libbcachefs/errcode.h" @@ -66,6 +67,22 @@ static u64 count_input_size(int dirfd) return bytes; } +static void set_data_allowed_for_image_update(struct bch_fs *c) +{ + mutex_lock(&c->sb_lock); + struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, 0); + SET_BCH_MEMBER_DATA_ALLOWED(m, BIT(BCH_DATA_user)); + + m = bch2_members_v2_get_mut(c->disk_sb.sb, 1); + SET_BCH_MEMBER_DATA_ALLOWED(m, BIT(BCH_DATA_journal)|BIT(BCH_DATA_btree)); + + bch2_write_super(c); + mutex_unlock(&c->sb_lock); + + bch2_dev_allocator_set_rw(c, c->devs[0], true); + bch2_dev_allocator_set_rw(c, c->devs[1], true); +} + struct move_btree_args { bool move_alloc; unsigned target; @@ -78,7 +95,6 @@ static bool move_btree_pred(struct bch_fs *c, void *_arg, { struct move_btree_args *args = _arg; - data_opts->target = dev_to_target(0); data_opts->target = args->target; if (k.k->type != KEY_TYPE_btree_ptr_v2) @@ -608,6 +624,166 @@ static int cmd_image_create(int argc, char *argv[]) return 0; } +static int image_update(const char *src_path, const char *dst_image, + bool keep_alloc, + unsigned verbosity) +{ + int src_fd = xopen(src_path, O_RDONLY); + + if (!S_ISDIR(xfstat(src_fd).st_mode)) + die("%s is not a directory", src_path); + + u64 input_bytes = count_input_size(src_fd); + + if (truncate(dst_image, input_bytes * 2)) + die("truncate error: %m"); + + darray_const_str device_paths = {}; + darray_push(&device_paths, dst_image); + + struct bch_opts opts = bch2_opts_empty(); + opt_set(opts, copygc_enabled, false); + opt_set(opts, rebalance_enabled, false); + opt_set(opts, nostart, true); + + struct bch_fs *c = bch2_fs_open(&device_paths, &opts); + int ret = PTR_ERR_OR_ZERO(c); + if (ret) + die("error opening image: %s", bch2_err_str(ret)); + + c->loglevel = 5 + max_t(int, 0, verbosity - 1); + + struct dev_opts dev_opts = dev_opts_default(); + dev_opts.path = mprintf("%s.metadata", dst_image); + + if (1) { + /* factor this out into a helper */ + + ret = open_for_format(&dev_opts, BLK_OPEN_CREAT, false); + if (ret) { + fprintf(stderr, "error opening %s: %m", dev_opts.path); + goto err; + } + + if (ftruncate(dev_opts.bdev->bd_fd, input_bytes)) { + fprintf(stderr, "ftruncate error: %m"); + goto err; + } + + ret = bch2_format_for_device_add(&dev_opts, + c->opts.block_size, + c->opts.btree_node_size); + bch_err_msg(c, ret, "opening %s", dev_opts.path); + if (ret) + goto err; + + ret = bch2_dev_add(c, dev_opts.path); + bch_err_msg(c, ret, "adding metadata device"); + if (ret) + goto err; + } + + set_data_allowed_for_image_update(c); + + ret = bch2_fs_start(c); + bch_err_msg(c, ret, "starting fs"); + if (ret) + goto err; + + bch_verbose(c, "Moving btree to temp device"); + ret = move_btree(c, true, 1); + bch_err_msg(c, ret, "migrating btree to temp device"); + if (ret) + goto err_stop; + + /* xattrs will be recreated */ + bch_verbose(c, "Deleting xattrs"); + ret = bch2_btree_delete_range(c, BTREE_ID_xattrs, POS_MIN, SPOS_MAX, + BTREE_ITER_all_snapshots, NULL); + bch_err_msg(c, ret, "deleting xattrs"); + if (ret) + goto err_stop; + + bch_verbose(c, "Syncing data"); + struct copy_fs_state s = {}; + + ret = copy_fs(c, &s, src_fd, src_path) ?: + finish_image(c, keep_alloc, verbosity); + if (ret) + goto err; + + bch2_fs_stop(c); + unlink(dev_opts.path); + xclose(src_fd); + return 0; +err_stop: + bch2_fs_stop(c); +err: + unlink(dev_opts.path); + exit(EXIT_FAILURE); +} + +static void image_update_usage(void) +{ + puts("bcachefs image update - update an image file, minimizing changes\n" + "Usage: bcachefs image update [OPTION]... <file>\n" + "\n" + "Options:\n" + " --source=path Source directory to be used as content for the new image\n" + " -a, --keep-alloc Include allocation info in the filesystem\n" + " 6.16+ regenerates alloc info on first rw mount\n" + " -q, --quiet Only print errors\n" + " -v, --verbose Verbose filesystem initialization\n" + " -h, --help Display this help and exit\n" + "\n" + "Report bugs to <linux-bcachefs@vger.kernel.org>"); +} + +static int cmd_image_update(int argc, char *argv[]) +{ + static const struct option longopts[] = { + { "source", required_argument, NULL, 's' }, + { "keep-alloc", no_argument, NULL, 'a' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { NULL } + }; + const char *source = NULL; + bool keep_alloc = false; + unsigned verbosity = 1; + int opt; + + while ((opt = getopt_long(argc, argv, "s:aqvh", + longopts, NULL)) != -1) + switch (opt) { + case 's': + source = optarg; + break; + case 'a': + keep_alloc = true; + break; + case 'q': + verbosity = 0; + break; + case 'v': + verbosity++; + break; + case 'h': + image_update_usage(); + exit(EXIT_SUCCESS); + default: + die("getopt ret %i %c", opt, opt); + } + args_shift(optind); + + if (argc != 1) + die("Please supply a filename"); + + return image_update(source, argv[0], + keep_alloc, verbosity); +} + static int image_usage(void) { puts("bcachefs image - commands for creating and updating image files\n" @@ -615,6 +791,7 @@ static int image_usage(void) "\n" "Commands:\n" " create Create a minimally-sized disk image\n" + " update Update a disk image, minimizing changes\n" "\n" "Report bugs to <linux-bcachefs@vger.kernel.org>"); return 0; @@ -628,6 +805,8 @@ int image_cmds(int argc, char *argv[]) return image_usage(); if (!strcmp(cmd, "create")) return cmd_image_create(argc, argv); + if (!strcmp(cmd, "update")) + return cmd_image_update(argc, argv); image_usage(); return -EINVAL; |