From 6420192e870cedb3f43dac2095ce9fd7729a63e3 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 23 Jul 2014 22:56:13 +0200 Subject: btrfs-progs: wait until all subvolumes are cleaned Enhance the 'subvolume' subcommand to wait until a given list of subvolumes or all currently scheduled for deletion are cleaned completely from the filesystem. Signed-off-by: David Sterba --- cmds-subvolume.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) (limited to 'cmds-subvolume.c') diff --git a/cmds-subvolume.c b/cmds-subvolume.c index 45712ac0..fa58a244 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -1024,6 +1024,237 @@ out: return !!ret; } +static const char * const cmd_subvol_sync_usage[] = { + "btrfs subvolume sync [...]", + "Wait until given subvolume(s) are completely removed from the filesystem.", + "Wait until given subvolume(s) are completely removed from the filesystem", + "after deletion.", + "If no subvolume id is given, wait until all ongoing deletion requests", + "are complete. This may take long if new deleted subvolumes appear during", + "the sleep interval.", + "", + "-s sleep N seconds between checks (default: 1)", + NULL +}; + +static int is_subvolume_cleaned(int fd, u64 subvolid) +{ + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = subvolid; + sk->max_objectid = subvolid; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_type = BTRFS_ROOT_ITEM_KEY; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) + return -errno; + + if (sk->nr_items == 0) + return 1; + + return 0; +} + +/* + * If we're looking for any dead subvolume, take a shortcut and look + * for any ORPHAN_ITEMs in the tree root + */ +static int fs_has_dead_subvolumes(int fd) +{ + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header sh; + u64 min_subvolid = 0; + +again: + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = BTRFS_ORPHAN_OBJECTID; + sk->max_objectid = BTRFS_ORPHAN_OBJECTID; + sk->min_type = BTRFS_ORPHAN_ITEM_KEY; + sk->max_type = BTRFS_ORPHAN_ITEM_KEY; + sk->min_offset = min_subvolid; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) + return -errno; + + if (!sk->nr_items) + return 0; + + memcpy(&sh, args.buf, sizeof(sh)); + min_subvolid = sh.offset; + + /* + * Verify that the root item is really there and we haven't hit + * a stale orphan + */ + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = min_subvolid; + sk->max_objectid = min_subvolid; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_type = BTRFS_ROOT_ITEM_KEY; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) + return -errno; + + /* + * Stale orphan, try the next one + */ + if (!sk->nr_items) { + min_subvolid++; + goto again; + } + + return 1; +} + +static int cmd_subvol_sync(int argc, char **argv) +{ + int fd = -1; + int i; + int ret = 1; + DIR *dirstream = NULL; + u64 *ids = NULL; + int id_count; + int remaining; + int sleep_interval = 1; + + optind = 1; + while (1) { + int c = getopt(argc, argv, "s:"); + + if (c < 0) + break; + + switch (c) { + case 's': + sleep_interval = atoi(argv[optind]); + if (sleep_interval < 1) { + fprintf(stderr, + "ERROR: invalid sleep interval %s\n", + argv[optind]); + ret = 1; + goto out; + } + break; + default: + usage(cmd_subvol_sync_usage); + } + } + + if (check_argc_min(argc - optind, 1)) + usage(cmd_subvol_sync_usage); + + fd = open_file_or_dir(argv[optind], &dirstream); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]); + ret = 1; + goto out; + } + optind++; + + id_count = argc - optind; + + /* + * Wait for all + */ + if (!id_count) { + while (1) { + ret = fs_has_dead_subvolumes(fd); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search - %s\n", + strerror(-ret)); + ret = 1; + goto out; + } + if (!ret) + goto out; + sleep(sleep_interval); + } + } + + /* + * Wait only for the requested ones + */ + ids = (u64*)malloc(sizeof(u64) * id_count); + + if (!ids) { + fprintf(stderr, "ERROR: not enough memory\n"); + ret = 1; + goto out; + } + + for (i = 0; i < id_count; i++) { + u64 id; + const char *arg; + + arg = argv[optind + i]; + errno = 0; + id = strtoull(arg, NULL, 10); + if (errno < 0) { + fprintf(stderr, "ERROR: unrecognized subvolume id %s\n", + arg); + ret = 1; + goto out; + } + if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) { + fprintf(stderr, "ERROR: subvolume id %s out of range\n", + arg); + ret = 1; + goto out; + } + ids[i] = id; + } + + remaining = id_count; + while (1) { + for (i = 0; i < id_count; i++) { + if (!ids[i]) + continue; + ret = is_subvolume_cleaned(fd, ids[i]); + if (ret < 0) { + fprintf(stderr, "ERROR: can't perform the search - %s\n", + strerror(-ret)); + goto out; + } + if (ret) { + printf("Subvolume id %llu is gone\n", ids[i]); + ids[i] = 0; + remaining--; + } + } + if (!remaining) + break; + sleep(sleep_interval); + } + +out: + free(ids); + close_file_or_dir(fd, dirstream); + + return !!ret; +} + const struct cmd_group subvolume_cmd_group = { subvolume_cmd_group_usage, NULL, { { "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 }, @@ -1036,6 +1267,7 @@ const struct cmd_group subvolume_cmd_group = { cmd_subvol_set_default_usage, NULL, 0 }, { "find-new", cmd_find_new, cmd_find_new_usage, NULL, 0 }, { "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 }, + { "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 }, NULL_CMD_STRUCT } }; -- cgit v1.2.3