diff options
-rw-r--r-- | c_src/bcachefs.c | 1 | ||||
-rw-r--r-- | c_src/cmd_image.c | 169 |
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; |