summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2025-04-28 13:38:04 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2025-07-12 19:49:55 -0400
commit3321afc8b6e43fe71e744f19c7df6c7dc3146271 (patch)
tree08b3fed3a3cc6068f4a8cb3ffee6b5cc6a7d9200
parentc1a25673e937090449d4cd858c7cd89bc570d59d (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.c1
-rw-r--r--c_src/cmd_image.c181
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;