summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--c_src/bcachefs.c1
-rw-r--r--c_src/cmd_image.c169
2 files changed, 170 insertions, 0 deletions
diff --git a/c_src/bcachefs.c b/c_src/bcachefs.c
index f4712597..e47039ce 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"
"Mount:\n"
" mount Mount a filesystem\n"
"\n"
diff --git a/c_src/cmd_image.c b/c_src/cmd_image.c
index 12478b1f..3e77b8b9 100644
--- a/c_src/cmd_image.c
+++ b/c_src/cmd_image.c
@@ -64,6 +64,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;
@@ -286,6 +302,8 @@ static void image_create(struct bch_opt_strs fs_opt_strs,
if (0)
check_gaps(c);
+ /* XXX: print out disk usage */
+
u64 nbuckets;
ret = get_nbuckets_used(c, &nbuckets);
if (ret)
@@ -469,6 +487,154 @@ static int cmd_image_create(int argc, char *argv[])
return 0;
}
+static int image_update(const char *src_path, const char *dst_image)
+{
+ 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));
+
+ 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);
+ if (ret) {
+ fprintf(stderr, "Error opening %s: %s\n", dev_opts.path, strerror(-ret));
+ goto err;
+ }
+
+ ret = bch2_dev_add(c, dev_opts.path);
+ if (ret) {
+ fprintf(stderr, "Error adding metadata device: %s\n", strerror(-ret));
+ goto err;
+ }
+ }
+
+ set_data_allowed_for_image_update(c);
+
+ ret = bch2_fs_start(c);
+ if (ret) {
+ fprintf(stderr, "error starting fs: %s\n", bch2_err_str(ret));
+ goto err;
+ }
+
+ ret = move_btree(c, true, 1);
+ if (ret) {
+ fprintf(stderr, "error migrating btree to temporary device: %s\n",
+ bch2_err_str(ret));
+ goto err_stop;
+ }
+
+ /* delete data being deleted */
+
+ /* sync data */
+
+ /* factor out another helper for dropping metadata device? */
+
+ unlink(dev_opts.path);
+ bch2_fs_stop(c);
+ 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]);
+}
+
static int image_usage(void)
{
puts("bcachefs image - commands for creating and updating image files\n"
@@ -476,6 +642,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;
@@ -489,6 +656,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;