mirror of
https://github.com/kdave/btrfs-progs
synced 2025-02-04 11:51:42 +00:00
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 <dsterba@suse.cz>
This commit is contained in:
parent
a1a3dc7fd4
commit
6420192e87
@ -153,6 +153,17 @@ List the recently modified files in a subvolume, after <last_gen> ID.
|
||||
*show* <path>::
|
||||
Show information of a given subvolume in the <path>.
|
||||
|
||||
*sync* <path> [subvolid...]::
|
||||
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.
|
||||
+
|
||||
`Options`
|
||||
+
|
||||
-s <N>::::
|
||||
sleep N seconds between checks (default: 1)
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
*btrfs subvolume* returns a zero exit status if it succeeds. Non zero is
|
||||
|
232
cmds-subvolume.c
232
cmds-subvolume.c
@ -1024,6 +1024,237 @@ out:
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
static const char * const cmd_subvol_sync_usage[] = {
|
||||
"btrfs subvolume sync <path> [<subvol-id>...]",
|
||||
"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 <N> 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
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user