X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-inspect.c;h=afd7fe48df5cc6aba8642401ea2b6b8c835d3240;hb=47c694b0f4afa4fabf1a2013b9f9514a832898f7;hp=f943ed9de210e7b4026adafb3d5b0bf8b7fd5a3f;hpb=c5800fb016a98625309769559af564e6c768ca6d;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-inspect.c b/cmds-inspect.c index f943ed9..afd7fe4 100644 --- a/cmds-inspect.c +++ b/cmds-inspect.c @@ -17,14 +17,21 @@ #include #include #include +#include #include #include +#include +#include #include "kerncompat.h" #include "ioctl.h" - +#include "utils.h" +#include "ctree.h" +#include "send-utils.h" +#include "disk-io.h" #include "commands.h" #include "btrfs-list.h" +#include "help.h" static const char * const inspect_cmd_group_usage[] = { "btrfs inspect-internal ", @@ -36,19 +43,16 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend) int ret; int i; struct btrfs_ioctl_ino_path_args ipa; - struct btrfs_data_container *fspath; - - fspath = malloc(4096); - if (!fspath) - return 1; + struct btrfs_data_container fspath[PATH_MAX]; + memset(fspath, 0, sizeof(*fspath)); ipa.inum = inum; - ipa.size = 4096; - ipa.fspath = (u64)fspath; + ipa.size = PATH_MAX; + ipa.fspath = ptr_to_u64(fspath); ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa); - if (ret) { - printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); + if (ret < 0) { + error("ino paths ioctl: %m"); goto out; } @@ -60,31 +64,36 @@ static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend) fspath->elem_cnt, fspath->elem_missed); for (i = 0; i < fspath->elem_cnt; ++i) { - char **str = (char **)fspath->val; - str[i] += (unsigned long)fspath->val; + u64 ptr; + char *str; + ptr = (u64)(unsigned long)fspath->val; + ptr += fspath->val[i]; + str = (char *)(unsigned long)ptr; if (prepend) - printf("%s/%s\n", prepend, str[i]); + printf("%s/%s\n", prepend, str); else - printf("%s\n", str[i]); + printf("%s\n", str); } out: - free(fspath); - return ret; + return !!ret; } -static const char * const cmd_inode_resolve_usage[] = { +static const char * const cmd_inspect_inode_resolve_usage[] = { "btrfs inspect-internal inode-resolve [-v] ", "Get file system paths for the given inode", + "", + "-v verbose mode", NULL }; -static int cmd_inode_resolve(int argc, char **argv) +static int cmd_inspect_inode_resolve(int argc, char **argv) { int fd; int verbose = 0; + int ret; + DIR *dirstream = NULL; - optind = 1; while (1) { int c = getopt(argc, argv, "v"); if (c < 0) @@ -95,30 +104,36 @@ static int cmd_inode_resolve(int argc, char **argv) verbose = 1; break; default: - usage(cmd_inode_resolve_usage); + usage(cmd_inspect_inode_resolve_usage); } } if (check_argc_exact(argc - optind, 2)) - usage(cmd_inode_resolve_usage); + usage(cmd_inspect_inode_resolve_usage); - fd = open_file_or_dir(argv[optind+1]); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); - return 12; - } + fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1); + if (fd < 0) + return 1; + + ret = __ino_to_path_fd(arg_strtou64(argv[optind]), fd, verbose, + argv[optind+1]); + close_file_or_dir(fd, dirstream); + return !!ret; - return __ino_to_path_fd(atoll(argv[optind]), fd, verbose, - argv[optind+1]); } -static const char * const cmd_logical_resolve_usage[] = { - "btrfs inspect-internal logical-resolve [-Pv] ", +static const char * const cmd_inspect_logical_resolve_usage[] = { + "btrfs inspect-internal logical-resolve [-Pv] [-s bufsize] ", "Get file system paths for the given logical address", + "-P skip the path resolving and print the inodes instead", + "-v verbose mode", + "-s bufsize set inode container's size. This is used to increase inode", + " container's size in case it is not enough to read all the ", + " resolved results. The max value one can set is 64k", NULL }; -static int cmd_logical_resolve(int argc, char **argv) +static int cmd_inspect_logical_resolve(int argc, char **argv) { int ret; int fd; @@ -128,12 +143,13 @@ static int cmd_logical_resolve(int argc, char **argv) int bytes_left; struct btrfs_ioctl_logical_ino_args loi; struct btrfs_data_container *inodes; - char full_path[4096]; + u64 size = 4096; + char full_path[PATH_MAX]; char *path_ptr; + DIR *dirstream = NULL; - optind = 1; while (1) { - int c = getopt(argc, argv, "Pv"); + int c = getopt(argc, argv, "Pvs:"); if (c < 0) break; @@ -144,38 +160,43 @@ static int cmd_logical_resolve(int argc, char **argv) case 'v': verbose = 1; break; + case 's': + size = arg_strtou64(optarg); + break; default: - usage(cmd_logical_resolve_usage); + usage(cmd_inspect_logical_resolve_usage); } } if (check_argc_exact(argc - optind, 2)) - usage(cmd_logical_resolve_usage); + usage(cmd_inspect_logical_resolve_usage); - inodes = malloc(4096); + size = min(size, (u64)SZ_64K); + inodes = malloc(size); if (!inodes) return 1; - loi.logical = atoll(argv[optind]); - loi.size = 4096; - loi.inodes = (u64)inodes; + memset(inodes, 0, sizeof(*inodes)); + loi.logical = arg_strtou64(argv[optind]); + loi.size = size; + loi.inodes = ptr_to_u64(inodes); - fd = open_file_or_dir(argv[optind+1]); + fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1); if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); ret = 12; goto out; } ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi); - if (ret) { - printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); + if (ret < 0) { + error("logical ino ioctl: %m"); goto out; } if (verbose) - printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, " - "cnt=%d, missed=%d\n", ret, + printf("ioctl ret=%d, total_size=%llu, bytes_left=%lu, " + "bytes_missing=%lu, cnt=%d, missed=%d\n", + ret, size, (unsigned long)inodes->bytes_left, (unsigned long)inodes->bytes_missing, inodes->elem_cnt, inodes->elem_missed); @@ -184,7 +205,10 @@ static int cmd_logical_resolve(int argc, char **argv) ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]); path_ptr = full_path + ret; bytes_left -= ret + 1; - BUG_ON(bytes_left < 0); + if (bytes_left < 0) { + error("path buffer too small: %d bytes", bytes_left); + goto out; + } for (i = 0; i < inodes->elem_cnt; i += 3) { u64 inum = inodes->val[i]; @@ -192,11 +216,14 @@ static int cmd_logical_resolve(int argc, char **argv) u64 root = inodes->val[i+2]; int path_fd; char *name; + DIR *dirs = NULL; if (getpath) { - name = path_for_root(fd, root); - if (IS_ERR(name)) - return PTR_ERR(name); + name = btrfs_list_path_for_root(fd, root); + if (IS_ERR(name)) { + ret = PTR_ERR(name); + goto out; + } if (!name) { path_ptr[-1] = '\0'; path_fd = fd; @@ -204,16 +231,21 @@ static int cmd_logical_resolve(int argc, char **argv) path_ptr[-1] = '/'; ret = snprintf(path_ptr, bytes_left, "%s", name); - BUG_ON(ret >= bytes_left); free(name); - path_fd = open_file_or_dir(full_path); + if (ret >= bytes_left) { + error("path buffer too small: %d bytes", + bytes_left - ret); + goto out; + } + path_fd = btrfs_open_dir(full_path, &dirs, 1); if (path_fd < 0) { - fprintf(stderr, "ERROR: can't access " - "'%s'\n", full_path); + ret = -ENOENT; goto out; } } __ino_to_path_fd(inum, path_fd, verbose, full_path); + if (path_fd != fd) + close_file_or_dir(path_fd, dirs); } else { printf("inode %llu offset %llu root %llu\n", inum, offset, root); @@ -221,17 +253,401 @@ static int cmd_logical_resolve(int argc, char **argv) } out: + close_file_or_dir(fd, dirstream); free(inodes); + return !!ret; +} + +static const char * const cmd_inspect_subvolid_resolve_usage[] = { + "btrfs inspect-internal subvolid-resolve ", + "Get file system paths for the given subvolume ID.", + NULL +}; + +static int cmd_inspect_subvolid_resolve(int argc, char **argv) +{ + int ret; + int fd = -1; + u64 subvol_id; + char path[PATH_MAX]; + DIR *dirstream = NULL; + + clean_args_no_options(argc, argv, cmd_inspect_subvolid_resolve_usage); + + if (check_argc_exact(argc - optind, 2)) + usage(cmd_inspect_subvolid_resolve_usage); + + fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1); + if (fd < 0) { + ret = -ENOENT; + goto out; + } + + subvol_id = arg_strtou64(argv[optind]); + ret = btrfs_subvolid_resolve(fd, path, sizeof(path), subvol_id); + + if (ret) { + error("resolving subvolid %llu error %d", + (unsigned long long)subvol_id, ret); + goto out; + } + + path[PATH_MAX - 1] = '\0'; + printf("%s\n", path); + +out: + close_file_or_dir(fd, dirstream); + return !!ret; +} + +static const char* const cmd_inspect_rootid_usage[] = { + "btrfs inspect-internal rootid ", + "Get tree ID of the containing subvolume of path.", + NULL +}; + +static int cmd_inspect_rootid(int argc, char **argv) +{ + int ret; + int fd = -1; + u64 rootid; + DIR *dirstream = NULL; + + clean_args_no_options(argc, argv, cmd_inspect_rootid_usage); + + if (check_argc_exact(argc - optind, 1)) + usage(cmd_inspect_rootid_usage); + + fd = btrfs_open_file_or_dir(argv[optind], &dirstream, 1); + if (fd < 0) { + ret = -ENOENT; + goto out; + } + + ret = lookup_path_rootid(fd, &rootid); + if (ret) { + error("failed to lookup root id: %s", strerror(-ret)); + goto out; + } + + printf("%llu\n", (unsigned long long)rootid); +out: + close_file_or_dir(fd, dirstream); + + return !!ret; +} + +static const char* const cmd_inspect_min_dev_size_usage[] = { + "btrfs inspect-internal min-dev-size [options] ", + "Get the minimum size the device can be shrunk to. The", + "device id 1 is used by default.", + "--id DEVID specify the device id to query", + NULL +}; + +struct dev_extent_elem { + u64 start; + /* inclusive end */ + u64 end; + struct list_head list; +}; + +static int add_dev_extent(struct list_head *list, + const u64 start, const u64 end, + const int append) +{ + struct dev_extent_elem *e; + + e = malloc(sizeof(*e)); + if (!e) + return -ENOMEM; + + e->start = start; + e->end = end; + + if (append) + list_add_tail(&e->list, list); + else + list_add(&e->list, list); + + return 0; +} + +static void free_dev_extent_list(struct list_head *list) +{ + while (!list_empty(list)) { + struct dev_extent_elem *e; + + e = list_first_entry(list, struct dev_extent_elem, list); + list_del(&e->list); + free(e); + } +} + +static int hole_includes_sb_mirror(const u64 start, const u64 end) +{ + int i; + int ret = 0; + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + u64 bytenr = btrfs_sb_offset(i); + + if (bytenr >= start && bytenr <= end) { + ret = 1; + break; + } + } + return ret; } +static void adjust_dev_min_size(struct list_head *extents, + struct list_head *holes, + u64 *min_size) +{ + /* + * If relocation of the block group of a device extent must happen (see + * below) scratch space is used for the relocation. So track here the + * size of the largest device extent that has to be relocated. We track + * only the largest and not the sum of the sizes of all relocated block + * groups because after each block group is relocated the running + * transaction is committed so that pinned space is released. + */ + u64 scratch_space = 0; + + /* + * List of device extents is sorted by descending order of the extent's + * end offset. If some extent goes beyond the computed minimum size, + * which initially matches the sum of the lengths of all extents, + * we need to check if the extent can be relocated to an hole in the + * device between [0, *min_size[ (which is what the resize ioctl does). + */ + while (!list_empty(extents)) { + struct dev_extent_elem *e; + struct dev_extent_elem *h; + int found = 0; + u64 extent_len; + u64 hole_len = 0; + + e = list_first_entry(extents, struct dev_extent_elem, list); + if (e->end <= *min_size) + break; + + /* + * Our extent goes beyond the computed *min_size. See if we can + * find a hole large enough to relocate it to. If not we must stop + * and set *min_size to the end of the extent. + */ + extent_len = e->end - e->start + 1; + list_for_each_entry(h, holes, list) { + hole_len = h->end - h->start + 1; + if (hole_len >= extent_len) { + found = 1; + break; + } + } + + if (!found) { + *min_size = e->end + 1; + break; + } + + /* + * If the hole found contains the location for a superblock + * mirror, we are pessimistic and require allocating one + * more extent of the same size. This is because the block + * group could be in the worst case used by a single extent + * with a size >= (block_group.length - superblock.size). + */ + if (hole_includes_sb_mirror(h->start, + h->start + extent_len - 1)) + *min_size += extent_len; + + if (hole_len > extent_len) { + h->start += extent_len; + } else { + list_del(&h->list); + free(h); + } + + list_del(&e->list); + free(e); + + if (extent_len > scratch_space) + scratch_space = extent_len; + } + + if (scratch_space) { + *min_size += scratch_space; + /* + * Chunk allocation requires inserting/updating items in the + * chunk tree, so often this can lead to the need of allocating + * a new system chunk too, which has a maximum size of 32Mb. + */ + *min_size += SZ_32M; + } +} + +static int print_min_dev_size(int fd, u64 devid) +{ + int ret = 1; + /* + * Device allocations starts at 1Mb or at the value passed through the + * mount option alloc_start if it's bigger than 1Mb. The alloc_start + * option is used for debugging and testing only, and recently the + * possibility of deprecating/removing it has been discussed, so we + * ignore it here. + */ + u64 min_size = SZ_1M; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + u64 last_pos = (u64)-1; + LIST_HEAD(extents); + LIST_HEAD(holes); + + memset(&args, 0, sizeof(args)); + sk->tree_id = BTRFS_DEV_TREE_OBJECTID; + sk->min_objectid = devid; + sk->max_objectid = devid; + sk->max_type = BTRFS_DEV_EXTENT_KEY; + sk->min_type = BTRFS_DEV_EXTENT_KEY; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 4096; + + while (1) { + int i; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (ret < 0) { + error("tree search ioctl: %m"); + ret = 1; + goto out; + } + + if (sk->nr_items == 0) + break; + + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_dev_extent *extent; + u64 len; + + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + off += sizeof(*sh); + extent = (struct btrfs_dev_extent *)(args.buf + off); + 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) + 1; + + if (btrfs_search_header_objectid(sh) != devid || + btrfs_search_header_type(sh) != BTRFS_DEV_EXTENT_KEY) + continue; + + len = btrfs_stack_dev_extent_length(extent); + min_size += len; + ret = add_dev_extent(&extents, + btrfs_search_header_offset(sh), + btrfs_search_header_offset(sh) + len - 1, 0); + + if (!ret && last_pos != (u64)-1 && + last_pos != btrfs_search_header_offset(sh)) + ret = add_dev_extent(&holes, last_pos, + btrfs_search_header_offset(sh) - 1, 1); + if (ret) { + error("add device extent: %s", strerror(-ret)); + ret = 1; + goto out; + } + + last_pos = btrfs_search_header_offset(sh) + len; + } + + if (sk->min_type != BTRFS_DEV_EXTENT_KEY || + sk->min_objectid != devid) + break; + } + + adjust_dev_min_size(&extents, &holes, &min_size); + printf("%llu bytes (%s)\n", min_size, pretty_size(min_size)); + ret = 0; +out: + free_dev_extent_list(&extents); + free_dev_extent_list(&holes); + + return ret; +} + +static int cmd_inspect_min_dev_size(int argc, char **argv) +{ + int ret; + int fd = -1; + DIR *dirstream = NULL; + u64 devid = 1; + + while (1) { + int c; + enum { GETOPT_VAL_DEVID = 256 }; + static const struct option long_options[] = { + { "id", required_argument, NULL, GETOPT_VAL_DEVID }, + {NULL, 0, NULL, 0} + }; + + c = getopt_long(argc, argv, "", long_options, NULL); + if (c < 0) + break; + + switch (c) { + case GETOPT_VAL_DEVID: + devid = arg_strtou64(optarg); + break; + default: + usage(cmd_inspect_min_dev_size_usage); + } + } + if (check_argc_exact(argc - optind, 1)) + usage(cmd_inspect_min_dev_size_usage); + + fd = btrfs_open_dir(argv[optind], &dirstream, 1); + if (fd < 0) { + ret = -ENOENT; + goto out; + } + + ret = print_min_dev_size(fd, devid); + close_file_or_dir(fd, dirstream); +out: + return !!ret; +} + +static const char inspect_cmd_group_info[] = +"query various internal information"; + const struct cmd_group inspect_cmd_group = { - inspect_cmd_group_usage, NULL, { - { "inode-resolve", cmd_inode_resolve, cmd_inode_resolve_usage, - NULL, 0 }, - { "logical-resolve", cmd_logical_resolve, - cmd_logical_resolve_usage, NULL, 0 }, - { 0, 0, 0, 0, 0 } + inspect_cmd_group_usage, inspect_cmd_group_info, { + { "inode-resolve", cmd_inspect_inode_resolve, + cmd_inspect_inode_resolve_usage, NULL, 0 }, + { "logical-resolve", cmd_inspect_logical_resolve, + cmd_inspect_logical_resolve_usage, NULL, 0 }, + { "subvolid-resolve", cmd_inspect_subvolid_resolve, + cmd_inspect_subvolid_resolve_usage, NULL, 0 }, + { "rootid", cmd_inspect_rootid, cmd_inspect_rootid_usage, NULL, + 0 }, + { "min-dev-size", cmd_inspect_min_dev_size, + cmd_inspect_min_dev_size_usage, NULL, 0 }, + { "dump-tree", cmd_inspect_dump_tree, + cmd_inspect_dump_tree_usage, NULL, 0 }, + { "dump-super", cmd_inspect_dump_super, + cmd_inspect_dump_super_usage, NULL, 0 }, + { "tree-stats", cmd_inspect_tree_stats, + cmd_inspect_tree_stats_usage, NULL, 0 }, + NULL_CMD_STRUCT } };