X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-subvolume.c;h=dc626a6495e59969ed36b796c7a63c44c8dc9660;hb=c7bc72264aed05e00edcb4f0575abf841d621dc8;hp=02e1dec18ed2d2afbc26d080c69590433f08b2a9;hpb=babe94e4817aca45aef409b7a21bc47e51cda6ff;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-subvolume.c b/cmds-subvolume.c index 02e1dec..dc626a6 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -37,6 +37,7 @@ #include "utils.h" #include "btrfs-list.h" #include "utils.h" +#include "help.h" static int is_subvolume_cleaned(int fd, u64 subvolid) { @@ -127,9 +128,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; @@ -224,33 +224,6 @@ out: return retval; } -/* - * Test if path is a subvolume - * Returns: - * 0 - path exists but it is not a subvolume - * 1 - path exists and it is a subvolume - * < 0 - error - */ -int test_issubvolume(const char *path) -{ - struct stat st; - struct statfs stfs; - int res; - - res = stat(path, &st); - if (res < 0) - return -errno; - - if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) - return 0; - - res = statfs(path, &stfs); - if (res < 0) - return -errno; - - return (int)stfs.f_type == BTRFS_SUPER_MAGIC; -} - static int wait_for_commit(int fd) { int ret; @@ -273,6 +246,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 }; @@ -289,26 +263,30 @@ 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 }; - 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++; @@ -324,7 +302,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; @@ -364,7 +342,7 @@ 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); @@ -376,38 +354,81 @@ again: 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)); 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 suceeded 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: %s, fsid: %s", + strerror(errno), 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; } @@ -418,24 +439,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)", @@ -458,15 +487,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[] = { @@ -501,7 +529,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, @@ -558,10 +586,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; @@ -575,11 +599,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, @@ -596,21 +622,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; @@ -643,7 +663,6 @@ static int cmd_subvol_snapshot(int argc, char **argv) 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"); @@ -790,7 +809,7 @@ static int cmd_subvol_get_default(int argc, char **argv) clean_args_no_options(argc, argv, cmd_subvol_get_default_usage); - if (check_argc_exact(argc - optind, 2)) + if (check_argc_exact(argc - optind, 1)) usage(cmd_subvol_get_default_usage); subvol = argv[1]; @@ -832,15 +851,18 @@ static int cmd_subvol_get_default(int argc, char **argv) BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL); if (filter_set) - btrfs_list_free_filter_set(filter_set); + free(filter_set); out: close_file_or_dir(fd, dirstream); 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 }; @@ -854,17 +876,43 @@ static int cmd_subvol_set_default(int argc, char **argv) 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]; + if (argc - optind == 1) { + /* path to the subvolume is specified */ + path = argv[optind]; + + ret = test_issubvolume(path); + if (ret < 0) { + error("stat error: %s", strerror(-ret)); + return 1; + } else if (!ret) { + error("'%s' is not a subvolume", path); + return 1; + } - objectid = arg_strtou64(subvolid); + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + return 1; - fd = btrfs_open_dir(path, &dirstream, 1); - if (fd < 0) - return 1; + ret = lookup_path_rootid(fd, &objectid); + if (ret) { + error("unable to get subvol id: %s", strerror(-ret)); + close_file_or_dir(fd, dirstream); + return 1; + } + } else { + /* subvol id and path to the filesystem are specified */ + subvolid = argv[optind]; + path = argv[optind + 1]; + objectid = arg_strtou64(subvolid); + + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + return 1; + } ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); e = errno; @@ -927,29 +975,68 @@ 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; + struct btrfs_list_filter_set *filter_set = NULL; char tstr[256]; char uuidparse[BTRFS_UUID_UNPARSED_SIZE]; - char *fullpath = NULL, *svpath = NULL, *mnt = NULL; + char *fullpath = NULL; char raw_prefix[] = "\t\t\t\t"; - u64 sv_id; - int fd = -1, mntfd = -1; + int fd = -1; int ret = 1; - DIR *dirstream1 = NULL, *dirstream2 = NULL; + DIR *dirstream1 = NULL; + int by_rootid = 0; + int by_uuid = 0; + u64 rootid_arg; + u8 uuid_arg[BTRFS_UUID_SIZE]; + + 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); + if (by_rootid && by_uuid) { + error( + "options --rootid and --uuid cannot be used at the same time"); + usage(cmd_subvol_show_usage); + } + + memset(&get_ri, 0, sizeof(get_ri)); fullpath = realpath(argv[optind], NULL); if (!fullpath) { error("cannot find real path for '%s': %s", @@ -957,61 +1044,27 @@ static int cmd_subvol_show(int argc, char **argv) goto out; } - ret = test_issubvolume(fullpath); - if (ret < 0) { - error("cannot access subvolume %s: %s", fullpath, - strerror(-ret)); - goto out; - } - if (!ret) { - error("not a subvolume: %s", fullpath); - ret = 1; - goto out; - } - - ret = find_mount_root(fullpath, &mnt); - if (ret < 0) { - error("find_mount_root failed on '%s': %s", - fullpath, strerror(-ret)); - goto out; - } - if (ret > 0) { - error("%s doesn't belong to btrfs mount point", fullpath); - goto out; - } - ret = 1; - svpath = get_subvol_name(mnt, fullpath); - - fd = btrfs_open_dir(fullpath, &dirstream1, 1); - if (fd < 0) - goto out; - - ret = btrfs_list_get_path_rootid(fd, &sv_id); - if (ret) { - error("can't get rootid for '%s'", fullpath); - goto out; - } - - mntfd = btrfs_open_dir(mnt, &dirstream2, 1); - if (mntfd < 0) - goto out; - - if (sv_id == BTRFS_FS_TREE_OBJECTID) { - printf("%s is toplevel subvolume\n", fullpath); - goto out; + if (by_rootid) { + ret = get_subvol_info_by_rootid(fullpath, &get_ri, rootid_arg); + } else if (by_uuid) { + ret = get_subvol_info_by_uuid(fullpath, &get_ri, uuid_arg); + } else { + ret = get_subvol_info(fullpath, &get_ri); } - memset(&get_ri, 0, sizeof(get_ri)); - get_ri.root_id = sv_id; - - ret = btrfs_get_subvol(mntfd, &get_ri); if (ret) { - error("can't find '%s'", svpath); - goto out; + if (ret < 0) { + error("Failed to get subvol info %s: %s", + fullpath, strerror(-ret)); + } else { + error("Failed to get subvol info %s: %d", + fullpath, ret); + } + return ret; } /* print the info */ - printf("%s\n", fullpath); + printf("%s\n", get_ri.full_path); printf("\tName: \t\t\t%s\n", get_ri.name); if (uuid_is_null(get_ri.uuid)) @@ -1058,19 +1111,23 @@ static int cmd_subvol_show(int argc, char **argv) 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; + } btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW, 1, raw_prefix); +out: /* clean up */ free(get_ri.path); free(get_ri.name); free(get_ri.full_path); - btrfs_list_free_filter_set(filter_set); + free(filter_set); -out: close_file_or_dir(fd, dirstream1); - close_file_or_dir(mntfd, dirstream2); - free(mnt); free(fullpath); return !!ret; } @@ -1199,24 +1256,26 @@ static int enumerate_dead_subvols(int fd, u64 **ids) sh = (struct btrfs_ioctl_search_header*)(args.buf + off); off += sizeof(*sh); - if (sh->type == BTRFS_ORPHAN_ITEM_KEY) { + if (btrfs_search_header_type(sh) + == BTRFS_ORPHAN_ITEM_KEY) { if (idx >= count) { u64 *newids; count += SUBVOL_ID_BATCH; - newids = (u64*)realloc(*ids, count); + newids = (u64*)realloc(*ids, + count * sizeof(u64)); if (!newids) return -ENOMEM; *ids = newids; } - (*ids)[idx] = sh->offset; + (*ids)[idx] = btrfs_search_header_offset(sh); idx++; } - off += sh->len; + off += btrfs_search_header_len(sh); - sk->min_objectid = sh->objectid; - sk->min_type = sh->type; - sk->min_offset = sh->offset; + 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++; @@ -1241,7 +1300,6 @@ static int cmd_subvol_sync(int argc, char **argv) int id_count; int sleep_interval = 1; - optind = 1; while (1) { int c = getopt(argc, argv, "s:"); @@ -1250,10 +1308,9 @@ static int cmd_subvol_sync(int argc, char **argv) switch (c) { case 's': - sleep_interval = atoi(argv[optind]); + sleep_interval = atoi(optarg); if (sleep_interval < 1) { - error("invalid sleep interval %s", - argv[optind]); + error("invalid sleep interval %s", optarg); ret = 1; goto out; } @@ -1308,7 +1365,7 @@ static int cmd_subvol_sync(int argc, char **argv) } if (id < BTRFS_FIRST_FREE_OBJECTID || id > BTRFS_LAST_FREE_OBJECTID) { - error("subvolume id %s out of range\n", arg); + error("subvolume id %s out of range", arg); ret = 1; goto out; }