X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-subvolume.c;h=45363a5ab3f80cb2a04fa6a1af153498094884da;hb=335b7011352bfe27d59094d622abe111177021d4;hp=e3d2cbce0b60f56b7057bd1387ea60be83c2a551;hpb=a6bbbe60874cd7f92bcf55f75fa26f33957e9b25;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-subvolume.c b/cmds-subvolume.c index e3d2cbc..45363a5 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -14,6 +14,7 @@ * Boston, MA 021110-1307, USA. */ +#include #include #include #include @@ -28,6 +29,8 @@ #include #include +#include + #include "kerncompat.h" #include "ioctl.h" #include "qgroup.h" @@ -37,39 +40,13 @@ #include "utils.h" #include "btrfs-list.h" #include "utils.h" +#include "help.h" -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; -} - -static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids, - int sleep_interval) +static int wait_for_subvolume_cleaning(int fd, size_t count, uint64_t *ids, + int sleep_interval) { - int ret; - int i; + size_t i; + enum btrfs_util_error err; while (1) { int clean = 1; @@ -77,16 +54,14 @@ static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids, for (i = 0; i < count; i++) { if (!ids[i]) continue; - ret = is_subvolume_cleaned(fd, ids[i]); - if (ret < 0) { - error( - "cannot read status of dead subvolume %llu: %s", - (unsigned long long)ids[i], strerror(-ret)); - return ret; - } - if (ret) { - printf("Subvolume id %llu is gone\n", ids[i]); + err = btrfs_util_subvolume_info_fd(fd, ids[i], NULL); + if (err == BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) { + printf("Subvolume id %" PRIu64 " is gone\n", + ids[i]); ids[i] = 0; + } else if (err) { + error_btrfs_util(err); + return -errno; } else { clean = 0; } @@ -127,9 +102,8 @@ static int cmd_subvol_create(int argc, char **argv) struct btrfs_qgroup_inherit *inherit = NULL; DIR *dirstream = NULL; - optind = 1; while (1) { - int c = getopt(argc, argv, "c:i:v"); + int c = getopt(argc, argv, "c:i:"); if (c < 0) break; @@ -210,7 +184,7 @@ static int cmd_subvol_create(int argc, char **argv) } if (res < 0) { - error("cannot create subvolume: %s", strerror(errno)); + error("cannot create subvolume: %m"); goto out; } @@ -226,12 +200,18 @@ out: static int wait_for_commit(int fd) { - int ret; + enum btrfs_util_error err; + uint64_t transid; - ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL); - if (ret < 0) - return ret; - return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL); + err = btrfs_util_start_sync_fd(fd, &transid); + if (err) + return -1; + + err = btrfs_util_wait_sync_fd(fd, transid); + if (err) + return -1; + + return 0; } static const char * const cmd_subvol_delete_usage[] = { @@ -246,6 +226,7 @@ static const char * const cmd_subvol_delete_usage[] = { "", "-c|--commit-after wait for transaction commit at the end of the operation", "-C|--commit-each wait for transaction commit after deleting each subvolume", + "-v|--verbose verbose output of operations", NULL }; @@ -254,7 +235,6 @@ static int cmd_subvol_delete(int argc, char **argv) int res, ret = 0; int cnt; int fd = -1; - struct btrfs_ioctl_vol_args args; char *dname, *vname, *cpath; char *dupdname = NULL; char *dupvname = NULL; @@ -262,26 +242,31 @@ static int cmd_subvol_delete(int argc, char **argv) DIR *dirstream = NULL; int verbose = 0; int commit_mode = 0; + u8 fsid[BTRFS_FSID_SIZE]; + char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; + struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, }; + enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 }; + enum btrfs_util_error err; - optind = 1; while (1) { int c; static const struct option long_options[] = { - {"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */ - {"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */ + {"commit-after", no_argument, NULL, 'c'}, + {"commit-each", no_argument, NULL, 'C'}, + {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; - c = getopt_long(argc, argv, "cC", long_options, NULL); + c = getopt_long(argc, argv, "cCv", long_options, NULL); if (c < 0) break; switch(c) { case 'c': - commit_mode = 1; + commit_mode = COMMIT_AFTER; break; case 'C': - commit_mode = 2; + commit_mode = COMMIT_EACH; break; case 'v': verbose++; @@ -297,7 +282,7 @@ static int cmd_subvol_delete(int argc, char **argv) if (verbose > 0) { printf("Transaction commit: %s\n", !commit_mode ? "none (default)" : - commit_mode == 1 ? "at the end" : "after each"); + commit_mode == COMMIT_AFTER ? "at the end" : "after each"); } cnt = optind; @@ -305,14 +290,9 @@ static int cmd_subvol_delete(int argc, char **argv) again: path = argv[cnt]; - res = test_issubvolume(path); - if (res < 0) { - error("cannot access subvolume %s: %s", path, strerror(-res)); - ret = 1; - goto out; - } - if (!res) { - error("not a subvolume: %s", path); + err = btrfs_util_is_subvolume(path); + if (err) { + error_btrfs_util(err); ret = 1; goto out; } @@ -320,8 +300,7 @@ again: cpath = realpath(path, NULL); if (!cpath) { ret = errno; - error("cannot find real path for '%s': %s", - path, strerror(errno)); + error("cannot find real path for '%s': %m", path); goto out; } dupdname = strdup(cpath); @@ -337,50 +316,90 @@ again: } printf("Delete subvolume (%s): '%s/%s'\n", - commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc) + commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc) ? "commit" : "no-commit", dname, vname); - memset(&args, 0, sizeof(args)); - strncpy_null(args.name, vname); - res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); - if(res < 0 ){ - error("cannot delete '%s/%s': %s", dname, vname, - strerror(errno)); + + err = btrfs_util_delete_subvolume_fd(fd, vname, 0); + if (err) { + error_btrfs_util(err); ret = 1; goto out; } - if (commit_mode == 1) { + if (commit_mode == COMMIT_EACH) { res = wait_for_commit(fd); if (res < 0) { - error("unable to wait for commit after '%s': %s", - path, strerror(errno)); + error("unable to wait for commit after '%s': %m", path); ret = 1; } + } else if (commit_mode == COMMIT_AFTER) { + res = get_fsid(dname, fsid, 0); + if (res < 0) { + error("unable to get fsid for '%s': %s", + path, strerror(-res)); + error( + "delete succeeded but commit may not be done in the end"); + ret = 1; + goto out; + } + + if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) { + if (verbose > 0) { + uuid_unparse(fsid, uuidbuf); + printf(" new fs is found for '%s', fsid: %s\n", + path, uuidbuf); + } + /* + * This is the first time a subvolume on this + * filesystem is deleted, keep fd in order to issue + * SYNC ioctl in the end + */ + goto keep_fd; + } } out: + close_file_or_dir(fd, dirstream); +keep_fd: + fd = -1; + dirstream = NULL; free(dupdname); free(dupvname); dupdname = NULL; dupvname = NULL; cnt++; - if (cnt < argc) { - close_file_or_dir(fd, dirstream); - /* avoid double free */ - fd = -1; - dirstream = NULL; + if (cnt < argc) goto again; - } - if (commit_mode == 2 && fd != -1) { - res = wait_for_commit(fd); - if (res < 0) { - error("unable to do final sync after deletion: %s", - strerror(errno)); - ret = 1; + if (commit_mode == COMMIT_AFTER) { + int slot; + + /* + * Traverse seen_fsid_hash and issue SYNC ioctl on each + * filesystem + */ + for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) { + struct seen_fsid *seen = seen_fsid_hash[slot]; + + while (seen) { + res = wait_for_commit(seen->fd); + if (res < 0) { + uuid_unparse(seen->fsid, uuidbuf); + error( + "unable to do final sync after deletion: %m, fsid: %s", + uuidbuf); + ret = 1; + } else if (verbose > 0) { + uuid_unparse(seen->fsid, uuidbuf); + printf("final sync is done for fsid: %s\n", + uuidbuf); + } + seen = seen->next; + } } + /* fd will also be closed in free_seen_fsid */ + free_seen_fsid(seen_fsid_hash); } - close_file_or_dir(fd, dirstream); return ret; } @@ -391,24 +410,32 @@ out: * - lowercase for enabling specific items in the output */ static const char * const cmd_subvol_list_usage[] = { - "btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] " - "[--sort=gen,ogen,rootid,path] ", - "List subvolumes (and snapshots)", + "btrfs subvolume list [options] ", + "List subvolumes and snapshots in the filesystem.", "", - "-p print parent ID", + "Path filtering:", + "-o print only subvolumes below specified path", "-a print all the subvolumes in the filesystem and", " distinguish absolute and relative path with respect", " to the given ", + "", + "Field selection:", + "-p print parent ID", "-c print the ogeneration of the subvolume", "-g print the generation of the subvolume", - "-o print only subvolumes below specified path", "-u print the uuid of subvolumes (and snapshots)", "-q print the parent uuid of the snapshots", "-R print the uuid of the received snapshots", - "-t print the result as a table", - "-s list snapshots only in the filesystem", + "", + "Type filtering:", + "-s list only snapshots", "-r list readonly subvolumes (including snapshots)", "-d list deleted subvolumes that are not yet cleaned", + "", + "Other:", + "-t print the result as a table", + "", + "Sorting:", "-G [+|-]value", " filter the subvolumes by generation", " (+value: >= value; -value: <= value; value: = value)", @@ -431,15 +458,14 @@ static int cmd_subvol_list(int argc, char **argv) u64 top_id; int ret = -1, uerr = 0; char *subvol; - int is_tab_result = 0; int is_list_all = 0; int is_only_in_path = 0; DIR *dirstream = NULL; + enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT; filter_set = btrfs_list_alloc_filter_set(); comparer_set = btrfs_list_alloc_comparer_set(); - optind = 1; while(1) { int c; static const struct option long_options[] = { @@ -474,7 +500,7 @@ static int cmd_subvol_list(int argc, char **argv) is_only_in_path = 1; break; case 't': - is_tab_result = 1; + layout = BTRFS_LIST_LAYOUT_TABLE; break; case 's': btrfs_list_setup_filter(&filter_set, @@ -531,10 +557,6 @@ static int cmd_subvol_list(int argc, char **argv) } } - if (flags) - btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS, - flags); - if (check_argc_exact(argc - optind, 1)) { uerr = 1; goto out; @@ -548,11 +570,13 @@ static int cmd_subvol_list(int argc, char **argv) goto out; } + if (flags) + btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS, + flags); + ret = btrfs_list_get_path_rootid(fd, &top_id); - if (ret) { - error("can't get rootid for '%s'", subvol); + if (ret) goto out; - } if (is_list_all) btrfs_list_setup_filter(&filter_set, @@ -569,21 +593,15 @@ static int cmd_subvol_list(int argc, char **argv) btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL); btrfs_list_setup_print_column(BTRFS_LIST_PATH); - if (is_tab_result) - ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, - BTRFS_LIST_LAYOUT_TABLE, - !is_list_all && !is_only_in_path, NULL); - else - ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, - BTRFS_LIST_LAYOUT_DEFAULT, - !is_list_all && !is_only_in_path, NULL); + ret = btrfs_list_subvols_print(fd, filter_set, comparer_set, + layout, !is_list_all && !is_only_in_path, NULL); out: close_file_or_dir(fd, dirstream); if (filter_set) - btrfs_list_free_filter_set(filter_set); + free(filter_set); if (comparer_set) - btrfs_list_free_comparer_set(comparer_set); + free(comparer_set); if (uerr) usage(cmd_subvol_list_usage); return !!ret; @@ -612,11 +630,11 @@ static int cmd_subvol_snapshot(int argc, char **argv) char *dupdir = NULL; char *newname; char *dstdir; + enum btrfs_util_error err; struct btrfs_ioctl_vol_args_v2 args; struct btrfs_qgroup_inherit *inherit = NULL; DIR *dirstream1 = NULL, *dirstream2 = NULL; - optind = 1; memset(&args, 0, sizeof(args)); while (1) { int c = getopt(argc, argv, "c:i:r"); @@ -660,13 +678,9 @@ static int cmd_subvol_snapshot(int argc, char **argv) dst = argv[optind + 1]; retval = 1; /* failure */ - res = test_issubvolume(subvol); - if (res < 0) { - error("cannot access subvolume %s: %s", subvol, strerror(-res)); - goto out; - } - if (!res) { - error("not a subvolume: %s", subvol); + err = btrfs_util_is_subvolume(subvol); + if (err) { + error_btrfs_util(err); goto out; } @@ -730,7 +744,7 @@ static int cmd_subvol_snapshot(int argc, char **argv) res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); if (res < 0) { - error("cannot snapshot '%s': %s", subvol, strerror(errno)); + error("cannot snapshot '%s': %m", subvol); goto out; } @@ -755,32 +769,25 @@ static const char * const cmd_subvol_get_default_usage[] = { static int cmd_subvol_get_default(int argc, char **argv) { int fd = -1; - int ret; - char *subvol; - struct btrfs_list_filter_set *filter_set; - u64 default_id; + int ret = 1; + uint64_t default_id; DIR *dirstream = NULL; + enum btrfs_util_error err; + struct btrfs_util_subvolume_info subvol; + char *path; clean_args_no_options(argc, argv, cmd_subvol_get_default_usage); if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_get_default_usage); - subvol = argv[1]; - fd = btrfs_open_dir(subvol, &dirstream, 1); + fd = btrfs_open_dir(argv[1], &dirstream, 1); if (fd < 0) return 1; - ret = btrfs_list_get_default_subvolume(fd, &default_id); - if (ret) { - error("failed to look up default subvolume: %s", - strerror(errno)); - goto out; - } - - ret = 1; - if (default_id == 0) { - error("'default' dir item not found"); + err = btrfs_util_get_default_subvolume_fd(fd, &default_id); + if (err) { + error_btrfs_util(err); goto out; } @@ -791,60 +798,63 @@ static int cmd_subvol_get_default(int argc, char **argv) goto out; } - filter_set = btrfs_list_alloc_filter_set(); - btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID, - default_id); + err = btrfs_util_subvolume_info_fd(fd, default_id, &subvol); + if (err) { + error_btrfs_util(err); + goto out; + } - /* by default we shall print the following columns*/ - btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID); - btrfs_list_setup_print_column(BTRFS_LIST_GENERATION); - btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL); - btrfs_list_setup_print_column(BTRFS_LIST_PATH); + err = btrfs_util_subvolume_path_fd(fd, default_id, &path); + if (err) { + error_btrfs_util(err); + goto out; + } - ret = btrfs_list_subvols_print(fd, filter_set, NULL, - BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL); + printf("ID %" PRIu64 " gen %" PRIu64 " top level %" PRIu64 " path %s\n", + subvol.id, subvol.generation, subvol.parent_id, path); - if (filter_set) - btrfs_list_free_filter_set(filter_set); + free(path); + + ret = 0; out: close_file_or_dir(fd, dirstream); - return !!ret; + return ret; } static const char * const cmd_subvol_set_default_usage[] = { + "btrfs subvolume set-default \n" "btrfs subvolume set-default ", - "Set the default subvolume of a filesystem", + "Set the default subvolume of the filesystem mounted as default.", + "The subvolume can be specified by its path,", + "or the pair of subvolume id and path to the filesystem.", NULL }; static int cmd_subvol_set_default(int argc, char **argv) { - int ret=0, fd, e; - u64 objectid; - char *path; - char *subvolid; - DIR *dirstream = NULL; + u64 objectid; + char *path; + enum btrfs_util_error err; clean_args_no_options(argc, argv, cmd_subvol_set_default_usage); - if (check_argc_exact(argc - optind, 2)) + if (check_argc_min(argc - optind, 1) || + check_argc_max(argc - optind, 2)) usage(cmd_subvol_set_default_usage); - subvolid = argv[optind]; - path = argv[optind + 1]; - - objectid = arg_strtou64(subvolid); - - fd = btrfs_open_dir(path, &dirstream, 1); - if (fd < 0) - return 1; + if (argc - optind == 1) { + /* path to the subvolume is specified */ + objectid = 0; + path = argv[optind]; + } else { + /* subvol id and path to the filesystem are specified */ + objectid = arg_strtou64(argv[optind]); + path = argv[optind + 1]; + } - ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); - e = errno; - close_file_or_dir(fd, dirstream); - if (ret < 0) { - error("unable to set a new default subvolume: %s", - strerror(e)); + err = btrfs_util_set_default_subvolume(path, objectid); + if (err) { + error_btrfs_util(err); return 1; } return 0; @@ -863,6 +873,7 @@ static int cmd_subvol_find_new(int argc, char **argv) char *subvol; u64 last_gen; DIR *dirstream = NULL; + enum btrfs_util_error err; clean_args_no_options(argc, argv, cmd_subvol_find_new_usage); @@ -872,13 +883,9 @@ static int cmd_subvol_find_new(int argc, char **argv) subvol = argv[optind]; last_gen = arg_strtou64(argv[optind + 1]); - ret = test_issubvolume(subvol); - if (ret < 0) { - error("cannot access subvolume %s: %s", subvol, strerror(-ret)); - return 1; - } - if (!ret) { - error("not a subvolume: %s", subvol); + err = btrfs_util_is_subvolume(subvol); + if (err) { + error_btrfs_util(err); return 1; } @@ -886,10 +893,9 @@ static int cmd_subvol_find_new(int argc, char **argv) if (fd < 0) return 1; - ret = ioctl(fd, BTRFS_IOC_SYNC); - if (ret < 0) { - error("sync ioctl failed on '%s': %s", - subvol, strerror(errno)); + err = btrfs_util_sync_fd(fd); + if (err) { + error_btrfs_util(err); close_file_or_dir(fd, dirstream); return 1; } @@ -900,119 +906,204 @@ static int cmd_subvol_find_new(int argc, char **argv) } static const char * const cmd_subvol_show_usage[] = { - "btrfs subvolume show ", - "Show more information of the subvolume", + "btrfs subvolume show [options] |", + "Show more information about the subvolume", + "-r|--rootid rootid of the subvolume", + "-u|--uuid uuid of the subvolume", + "", + "If no option is specified, will be shown, otherwise", + "the rootid or uuid are resolved relative to the path.", NULL }; static int cmd_subvol_show(int argc, char **argv) { - struct root_info get_ri; - struct btrfs_list_filter_set *filter_set = NULL; char tstr[256]; char uuidparse[BTRFS_UUID_UNPARSED_SIZE]; char *fullpath = NULL; - char raw_prefix[] = "\t\t\t\t"; int fd = -1; int ret = 1; DIR *dirstream1 = NULL; + int by_rootid = 0; + int by_uuid = 0; + u64 rootid_arg = 0; + u8 uuid_arg[BTRFS_UUID_SIZE]; + struct btrfs_util_subvolume_iterator *iter; + struct btrfs_util_subvolume_info subvol; + char *subvol_path = NULL; + enum btrfs_util_error err; + + while (1) { + int c; + static const struct option long_options[] = { + { "rootid", required_argument, NULL, 'r'}, + { "uuid", required_argument, NULL, 'u'}, + { NULL, 0, NULL, 0 } + }; - clean_args_no_options(argc, argv, cmd_subvol_show_usage); + c = getopt_long(argc, argv, "r:u:", long_options, NULL); + if (c < 0) + break; + + switch (c) { + case 'r': + rootid_arg = arg_strtou64(optarg); + by_rootid = 1; + break; + case 'u': + uuid_parse(optarg, uuid_arg); + by_uuid = 1; + break; + default: + usage(cmd_subvol_show_usage); + } + } if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_show_usage); - memset(&get_ri, 0, sizeof(get_ri)); + if (by_rootid && by_uuid) { + error( + "options --rootid and --uuid cannot be used at the same time"); + usage(cmd_subvol_show_usage); + } + fullpath = realpath(argv[optind], NULL); if (!fullpath) { - error("cannot find real path for '%s': %s", - argv[optind], strerror(errno)); + error("cannot find real path for '%s': %m", argv[optind]); goto out; } - ret = get_subvol_info(fullpath, &get_ri); - if (ret == 2) { - /* - * Since the top level btrfs was given don't - * take that as error - */ - printf("%s is toplevel subvolume\n", fullpath); - ret = 0; + fd = open_file_or_dir(fullpath, &dirstream1); + if (fd < 0) { + error("can't access '%s'", fullpath); goto out; } - if (ret) { - ret < 0 ? - error("Failed to get subvol info %s: %s\n", - fullpath, strerror(-ret)): - error("Failed to get subvol info %s: %d\n", - fullpath, ret); - return ret; + + if (by_uuid) { + err = btrfs_util_create_subvolume_iterator_fd(fd, + BTRFS_FS_TREE_OBJECTID, + 0, &iter); + if (err) { + error_btrfs_util(err); + goto out; + } + + for (;;) { + err = btrfs_util_subvolume_iterator_next_info(iter, + &subvol_path, + &subvol); + if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) { + uuid_unparse(uuid_arg, uuidparse); + error("can't find uuid '%s' on '%s'", uuidparse, + fullpath); + btrfs_util_destroy_subvolume_iterator(iter); + goto out; + } else if (err) { + error_btrfs_util(err); + btrfs_util_destroy_subvolume_iterator(iter); + goto out; + } + + if (uuid_compare(subvol.uuid, uuid_arg) == 0) + break; + + free(subvol_path); + } + btrfs_util_destroy_subvolume_iterator(iter); + } else { + /* + * If !by_rootid, rootid_arg = 0, which means find the + * subvolume ID of the fd and use that. + */ + err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol); + if (err) { + error_btrfs_util(err); + goto out; + } + + err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path); + if (err) { + error_btrfs_util(err); + goto out; + } + } /* print the info */ - printf("%s\n", fullpath); - printf("\tName: \t\t\t%s\n", get_ri.name); + printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path); + printf("\tName: \t\t\t%s\n", + (subvol.id == BTRFS_FS_TREE_OBJECTID ? "" : + basename(subvol_path))); - if (uuid_is_null(get_ri.uuid)) + if (uuid_is_null(subvol.uuid)) strcpy(uuidparse, "-"); else - uuid_unparse(get_ri.uuid, uuidparse); + uuid_unparse(subvol.uuid, uuidparse); printf("\tUUID: \t\t\t%s\n", uuidparse); - if (uuid_is_null(get_ri.puuid)) + if (uuid_is_null(subvol.parent_uuid)) strcpy(uuidparse, "-"); else - uuid_unparse(get_ri.puuid, uuidparse); + uuid_unparse(subvol.parent_uuid, uuidparse); printf("\tParent UUID: \t\t%s\n", uuidparse); - if (uuid_is_null(get_ri.ruuid)) + if (uuid_is_null(subvol.received_uuid)) strcpy(uuidparse, "-"); else - uuid_unparse(get_ri.ruuid, uuidparse); + uuid_unparse(subvol.received_uuid, uuidparse); printf("\tReceived UUID: \t\t%s\n", uuidparse); - if (get_ri.otime) { + if (subvol.otime.tv_sec) { struct tm tm; - localtime_r(&get_ri.otime, &tm); + localtime_r(&subvol.otime.tv_sec, &tm); strftime(tstr, 256, "%Y-%m-%d %X %z", &tm); } else strcpy(tstr, "-"); printf("\tCreation time: \t\t%s\n", tstr); - printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id); - printf("\tGeneration: \t\t%llu\n", get_ri.gen); - printf("\tGen at creation: \t%llu\n", get_ri.ogen); - printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree); - printf("\tTop level ID: \t\t%llu\n", get_ri.top_id); + printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id); + printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation); + printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid); + printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id); + printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id); - if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY) + if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY) printf("\tFlags: \t\t\treadonly\n"); else printf("\tFlags: \t\t\t-\n"); /* print the snapshots of the given subvol if any*/ printf("\tSnapshot(s):\n"); - filter_set = btrfs_list_alloc_filter_set(); - btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT, - (u64)(unsigned long)get_ri.uuid); - btrfs_list_setup_print_column(BTRFS_LIST_PATH); - fd = open_file_or_dir(fullpath, &dirstream1); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", fullpath); - goto out; + err = btrfs_util_create_subvolume_iterator_fd(fd, + BTRFS_FS_TREE_OBJECTID, 0, + &iter); + + for (;;) { + struct btrfs_util_subvolume_info subvol2; + char *path; + + err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2); + if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) { + break; + } else if (err) { + error_btrfs_util(err); + btrfs_util_destroy_subvolume_iterator(iter); + goto out; + } + + if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0) + printf("\t\t\t\t%s\n", path); + + free(path); } - btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW, - 1, raw_prefix); + btrfs_util_destroy_subvolume_iterator(iter); + ret = 0; out: - /* clean up */ - free(get_ri.path); - free(get_ri.name); - free(get_ri.full_path); - btrfs_list_free_filter_set(filter_set); - + free(subvol_path); close_file_or_dir(fd, dirstream1); free(fullpath); return !!ret; @@ -1031,162 +1122,16 @@ static const char * const cmd_subvol_sync_usage[] = { NULL }; -#if 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; -} -#endif - -#define SUBVOL_ID_BATCH 1024 - -/* - * Enumerate all dead subvolumes that exist in the filesystem. - * Fill @ids and reallocate to bigger size if needed. - */ -static int enumerate_dead_subvols(int fd, u64 **ids) -{ - int ret; - struct btrfs_ioctl_search_args args; - struct btrfs_ioctl_search_key *sk = &args.key; - int idx = 0; - int count = 0; - - memset(&args, 0, sizeof(args)); - - 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 = 0; - sk->max_offset = (u64)-1; - sk->min_transid = 0; - sk->max_transid = (u64)-1; - sk->nr_items = 4096; - - *ids = NULL; - while (1) { - struct btrfs_ioctl_search_header *sh; - unsigned long off; - int i; - - ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); - if (ret < 0) - return -errno; - - if (!sk->nr_items) - return idx; - - off = 0; - for (i = 0; i < sk->nr_items; i++) { - sh = (struct btrfs_ioctl_search_header*)(args.buf + off); - off += sizeof(*sh); - - if (btrfs_search_header_type(sh) - == BTRFS_ORPHAN_ITEM_KEY) { - if (idx >= count) { - u64 *newids; - - count += SUBVOL_ID_BATCH; - newids = (u64*)realloc(*ids, - count * sizeof(u64)); - if (!newids) - return -ENOMEM; - *ids = newids; - } - (*ids)[idx] = btrfs_search_header_offset(sh); - idx++; - } - off += btrfs_search_header_len(sh); - - sk->min_objectid = btrfs_search_header_objectid(sh); - sk->min_type = btrfs_search_header_type(sh); - sk->min_offset = btrfs_search_header_offset(sh); - } - if (sk->min_offset < (u64)-1) - sk->min_offset++; - else - break; - if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY) - break; - if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID) - break; - } - - return idx; -} - 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; + uint64_t *ids = NULL; + size_t id_count, i; int sleep_interval = 1; + enum btrfs_util_error err; - optind = 1; while (1) { int c = getopt(argc, argv, "s:"); @@ -1219,10 +1164,9 @@ static int cmd_subvol_sync(int argc, char **argv) id_count = argc - optind; if (!id_count) { - id_count = enumerate_dead_subvols(fd, &ids); - if (id_count < 0) { - error("can't enumerate dead subvolumes: %s", - strerror(-id_count)); + err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count); + if (err) { + error_btrfs_util(err); ret = 1; goto out; } @@ -1231,7 +1175,7 @@ static int cmd_subvol_sync(int argc, char **argv) goto out; } } else { - ids = (u64*)malloc(id_count * sizeof(u64)); + ids = malloc(id_count * sizeof(uint64_t)); if (!ids) { error("not enough memory"); ret = 1; @@ -1245,14 +1189,14 @@ static int cmd_subvol_sync(int argc, char **argv) arg = argv[optind + i]; errno = 0; id = strtoull(arg, NULL, 10); - if (errno < 0) { + if (errno) { error("unrecognized subvolume id %s", arg); ret = 1; goto out; } - if (id < BTRFS_FIRST_FREE_OBJECTID - || id > BTRFS_LAST_FREE_OBJECTID) { - error("subvolume id %s out of range\n", arg); + if (id < BTRFS_FIRST_FREE_OBJECTID || + id > BTRFS_LAST_FREE_OBJECTID) { + error("subvolume id %s out of range", arg); ret = 1; goto out; }