X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-filesystem.c;h=800aa4dc430fbe5df881f07a5759150904211227;hb=f802f572b1cb1d33bab9747e87e6506b284546cf;hp=0f29afc5b289507d39c98f69000e807a5a26cafe;hpb=d9d970451024709bdff71bf20b99843a132c195d;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 0f29afc..800aa4d 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -14,7 +14,6 @@ * Boston, MA 021110-1307, USA. */ -#define _XOPEN_SOURCE 500 #include #include #include @@ -34,9 +33,10 @@ #include "ioctl.h" #include "utils.h" #include "volumes.h" -#include "version.h" #include "commands.h" +#include "cmds-fi-usage.h" #include "list_sort.h" +#include "disk-io.h" /* @@ -52,6 +52,15 @@ struct seen_fsid { static struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = {NULL,}; +static int is_seen_fsid(u8 *fsid) +{ + u8 hash = fsid[0]; + int slot = hash % SEEN_FSID_HASH_SIZE; + struct seen_fsid *seen = seen_fsid_hash[slot]; + + return seen ? 1 : 0; +} + static int add_seen_fsid(u8 *fsid) { u8 hash = fsid[0]; @@ -111,50 +120,22 @@ static const char * const filesystem_cmd_group_usage[] = { NULL }; -static const char * const cmd_df_usage[] = { - "btrfs filesystem df ", - "Show space usage information for a mount point", - NULL +static const char * const cmd_filesystem_df_usage[] = { + "btrfs filesystem df [options] ", + "Show space usage information for a mount point", + "-b|--raw raw numbers in bytes", + "-h|--human-readable", + " human friendly numbers, base 1024 (default)", + "-H human friendly numbers, base 1000", + "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", + "--si use 1000 as a base (kB, MB, GB, TB)", + "-k|--kbytes show sizes in KiB, or kB with --si", + "-m|--mbytes show sizes in MiB, or MB with --si", + "-g|--gbytes show sizes in GiB, or GB with --si", + "-t|--tbytes show sizes in TiB, or TB with --si", + NULL }; -static char *group_type_str(u64 flag) -{ - switch (flag & BTRFS_BLOCK_GROUP_TYPE_MASK) { - case BTRFS_BLOCK_GROUP_DATA: - return "Data"; - case BTRFS_BLOCK_GROUP_SYSTEM: - return "System"; - case BTRFS_BLOCK_GROUP_METADATA: - return "Metadata"; - case BTRFS_BLOCK_GROUP_DATA|BTRFS_BLOCK_GROUP_METADATA: - return "Data+Metadata"; - default: - return "unknown"; - } -} - -static char *group_profile_str(u64 flag) -{ - switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { - case 0: - return "single"; - case BTRFS_BLOCK_GROUP_RAID0: - return "RAID0"; - case BTRFS_BLOCK_GROUP_RAID1: - return "RAID1"; - case BTRFS_BLOCK_GROUP_RAID5: - return "RAID5"; - case BTRFS_BLOCK_GROUP_RAID6: - return "RAID6"; - case BTRFS_BLOCK_GROUP_DUP: - return "DUP"; - case BTRFS_BLOCK_GROUP_RAID10: - return "RAID10"; - default: - return "unknown"; - } -} - static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret) { u64 count = 0; @@ -187,7 +168,7 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret) sargs = malloc(sizeof(struct btrfs_ioctl_space_args) + (count * sizeof(struct btrfs_ioctl_space_info))); if (!sargs) - ret = -ENOMEM; + return -ENOMEM; sargs->space_slots = count; sargs->total_spaces = 0; @@ -203,42 +184,95 @@ static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret) return 0; } -static void print_df(struct btrfs_ioctl_space_args *sargs) +static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode) { u64 i; struct btrfs_ioctl_space_info *sp = sargs->spaces; for (i = 0; i < sargs->total_spaces; i++, sp++) { printf("%s, %s: total=%s, used=%s\n", - group_type_str(sp->flags), - group_profile_str(sp->flags), - pretty_size(sp->total_bytes), - pretty_size(sp->used_bytes)); + btrfs_group_type_str(sp->flags), + btrfs_group_profile_str(sp->flags), + pretty_size_mode(sp->total_bytes, unit_mode), + pretty_size_mode(sp->used_bytes, unit_mode)); } } -static int cmd_df(int argc, char **argv) +static int cmd_filesystem_df(int argc, char **argv) { struct btrfs_ioctl_space_args *sargs = NULL; int ret; int fd; char *path; - DIR *dirstream = NULL; + DIR *dirstream = NULL; + unsigned unit_mode = UNITS_DEFAULT; - if (check_argc_exact(argc, 2)) - usage(cmd_df_usage); + while (1) { + int c; + static const struct option long_options[] = { + { "raw", no_argument, NULL, 'b'}, + { "kbytes", no_argument, NULL, 'k'}, + { "mbytes", no_argument, NULL, 'm'}, + { "gbytes", no_argument, NULL, 'g'}, + { "tbytes", no_argument, NULL, 't'}, + { "si", no_argument, NULL, GETOPT_VAL_SI}, + { "iec", no_argument, NULL, GETOPT_VAL_IEC}, + { "human-readable", no_argument, NULL, + GETOPT_VAL_HUMAN_READABLE}, + { NULL, 0, NULL, 0 } + }; - path = argv[1]; + c = getopt_long(argc, argv, "bhHkmgt", long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'b': + unit_mode = UNITS_RAW; + break; + case 'k': + units_set_base(&unit_mode, UNITS_KBYTES); + break; + case 'm': + units_set_base(&unit_mode, UNITS_MBYTES); + break; + case 'g': + units_set_base(&unit_mode, UNITS_GBYTES); + break; + case 't': + units_set_base(&unit_mode, UNITS_TBYTES); + break; + case GETOPT_VAL_HUMAN_READABLE: + case 'h': + unit_mode = UNITS_HUMAN_BINARY; + break; + case 'H': + unit_mode = UNITS_HUMAN_DECIMAL; + break; + case GETOPT_VAL_SI: + units_set_mode(&unit_mode, UNITS_DECIMAL); + break; + case GETOPT_VAL_IEC: + units_set_mode(&unit_mode, UNITS_BINARY); + break; + default: + usage(cmd_filesystem_df_usage); + } + } + + if (check_argc_exact(argc, optind + 1)) + usage(cmd_filesystem_df_usage); + + path = argv[optind]; fd = open_file_or_dir(path, &dirstream); if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); + fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } ret = get_df(fd, &sargs); - if (!ret && sargs) { - print_df(sargs); + if (ret == 0) { + print_df(sargs, unit_mode); free(sargs); } else { fprintf(stderr, "ERROR: get_df failed %s\n", strerror(-ret)); @@ -304,10 +338,73 @@ static int cmp_device_id(void *priv, struct list_head *a, da->devid > db->devid ? 1 : 0; } -static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +static void splice_device_list(struct list_head *seed_devices, + struct list_head *all_devices) +{ + struct btrfs_device *in_all, *next_all; + struct btrfs_device *in_seed, *next_seed; + + list_for_each_entry_safe(in_all, next_all, all_devices, dev_list) { + list_for_each_entry_safe(in_seed, next_seed, seed_devices, + dev_list) { + if (in_all->devid == in_seed->devid) { + /* + * When do dev replace in a sprout fs + * to a dev in its seed fs, the replacing + * dev will reside in the sprout fs and + * the replaced dev will still exist + * in the seed fs. + * So pick the latest one when showing + * the sprout fs. + */ + if (in_all->generation + < in_seed->generation) { + list_del(&in_all->dev_list); + free(in_all); + } else if (in_all->generation + > in_seed->generation) { + list_del(&in_seed->dev_list); + free(in_seed); + } + break; + } + } + } + + list_splice(seed_devices, all_devices); +} + +static void print_devices(struct btrfs_fs_devices *fs_devices, + u64 *devs_found, unsigned unit_mode) +{ + struct btrfs_device *device; + struct btrfs_fs_devices *cur_fs; + struct list_head *all_devices; + + all_devices = &fs_devices->devices; + cur_fs = fs_devices->seed; + /* add all devices of seed fs to the fs to be printed */ + while (cur_fs) { + splice_device_list(&cur_fs->devices, all_devices); + cur_fs = cur_fs->seed; + } + + list_sort(NULL, all_devices, cmp_device_id); + list_for_each_entry(device, all_devices, dev_list) { + printf("\tdevid %4llu size %s used %s path %s\n", + (unsigned long long)device->devid, + pretty_size_mode(device->total_bytes, unit_mode), + pretty_size_mode(device->bytes_used, unit_mode), + device->name); + + (*devs_found)++; + } +} + +static void print_one_uuid(struct btrfs_fs_devices *fs_devices, + unsigned unit_mode) { char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; - struct list_head *cur; struct btrfs_device *device; u64 devs_found = 0; u64 total; @@ -323,23 +420,13 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices) else printf("Label: none "); - total = device->total_devs; printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, (unsigned long long)total, - pretty_size(device->super_bytes_used)); - - list_sort(NULL, &fs_devices->devices, cmp_device_id); - list_for_each(cur, &fs_devices->devices) { - device = list_entry(cur, struct btrfs_device, dev_list); + pretty_size_mode(device->super_bytes_used, unit_mode)); - printf("\tdevid %4llu size %s used %s path %s\n", - (unsigned long long)device->devid, - pretty_size(device->total_bytes), - pretty_size(device->bytes_used), device->name); + print_devices(fs_devices, &devs_found, unit_mode); - devs_found++; - } if (devs_found < total) { printf("\t*** Some devices missing\n"); } @@ -360,9 +447,11 @@ static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si) static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info, struct btrfs_ioctl_dev_info_args *dev_info, struct btrfs_ioctl_space_args *space_info, - char *label, char *path) + char *label, char *path, unsigned unit_mode) { int i; + int fd; + int missing = 0; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_ioctl_dev_info_args *tmp_dev_info; int ret; @@ -381,61 +470,46 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info, printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, fs_info->num_devices, - pretty_size(calc_used_bytes(space_info))); + pretty_size_mode(calc_used_bytes(space_info), + unit_mode)); for (i = 0; i < fs_info->num_devices; i++) { + char *canonical_path; + tmp_dev_info = (struct btrfs_ioctl_dev_info_args *)&dev_info[i]; + + /* Add check for missing devices even mounted */ + fd = open((char *)tmp_dev_info->path, O_RDONLY); + if (fd < 0) { + missing = 1; + continue; + } + close(fd); + canonical_path = canonicalize_path((char *)tmp_dev_info->path); printf("\tdevid %4llu size %s used %s path %s\n", tmp_dev_info->devid, - pretty_size(tmp_dev_info->total_bytes), - pretty_size(tmp_dev_info->bytes_used), - tmp_dev_info->path); + pretty_size_mode(tmp_dev_info->total_bytes, unit_mode), + pretty_size_mode(tmp_dev_info->bytes_used, unit_mode), + canonical_path); + + free(canonical_path); } + if (missing) + printf("\t*** Some devices missing\n"); printf("\n"); return 0; } -/* This function checks if the given input parameter is - * an uuid or a path - * return -1: some error in the given input - * return 0: unknow input - * return 1: given input is uuid - * return 2: given input is path - */ -static int check_arg_type(char *input) -{ - uuid_t out; - char path[PATH_MAX]; - - if (!input) - return -EINVAL; - - if (realpath(input, path)) { - if (is_block_device(input) == 1) - return BTRFS_ARG_BLKDEV; - - if (is_mount_point(input) == 1) - return BTRFS_ARG_MNTPOINT; - - return BTRFS_ARG_UNKNOWN; - } - - if (strlen(input) == (BTRFS_UUID_UNPARSED_SIZE - 1) && - !uuid_parse(input, out)) - return BTRFS_ARG_UUID; - - return BTRFS_ARG_UNKNOWN; -} - -static int btrfs_scan_kernel(void *search) +static int btrfs_scan_kernel(void *search, unsigned unit_mode) { int ret = 0, fd; + int found = 0; FILE *f; struct mntent *mnt; struct btrfs_ioctl_fs_info_args fs_info_arg; struct btrfs_ioctl_dev_info_args *dev_info_arg = NULL; - struct btrfs_ioctl_space_args *space_info_arg; + struct btrfs_ioctl_space_args *space_info_arg = NULL; char label[BTRFS_LABEL_SIZE]; f = setmntent("/proc/self/mounts", "r"); @@ -448,12 +522,13 @@ static int btrfs_scan_kernel(void *search) continue; ret = get_fs_info(mnt->mnt_dir, &fs_info_arg, &dev_info_arg); - if (ret) + if (ret) { + kfree(dev_info_arg); goto out; + } if (get_label_mounted(mnt->mnt_dir, label)) { kfree(dev_info_arg); - ret = 1; goto out; } if (search && !match_search_item_kernel(fs_info_arg.fsid, @@ -465,22 +540,279 @@ static int btrfs_scan_kernel(void *search) fd = open(mnt->mnt_dir, O_RDONLY); if ((fd != -1) && !get_df(fd, &space_info_arg)) { print_one_fs(&fs_info_arg, dev_info_arg, - space_info_arg, label, mnt->mnt_dir); + space_info_arg, label, mnt->mnt_dir, + unit_mode); kfree(space_info_arg); memset(label, 0, sizeof(label)); + found = 1; } if (fd != -1) close(fd); kfree(dev_info_arg); - if (search) - ret = 0; } - if (search) - ret = 1; out: endmntent(f); + return !found; +} + +static int dev_to_fsid(char *dev, __u8 *fsid) +{ + struct btrfs_super_block *disk_super; + char *buf; + int ret; + int fd; + + buf = malloc(4096); + if (!buf) + return -ENOMEM; + + fd = open(dev, O_RDONLY); + if (fd < 0) { + ret = -errno; + free(buf); + return ret; + } + + disk_super = (struct btrfs_super_block *)buf; + ret = btrfs_read_dev_super(fd, disk_super, + BTRFS_SUPER_INFO_OFFSET, 0); + if (ret) + goto out; + + memcpy(fsid, disk_super->fsid, BTRFS_FSID_SIZE); + ret = 0; + +out: + close(fd); + free(buf); + return ret; +} + +static void free_fs_devices(struct btrfs_fs_devices *fs_devices) +{ + struct btrfs_fs_devices *cur_seed, *next_seed; + struct btrfs_device *device; + + while (!list_empty(&fs_devices->devices)) { + device = list_entry(fs_devices->devices.next, + struct btrfs_device, dev_list); + list_del(&device->dev_list); + + free(device->name); + free(device->label); + free(device); + } + + /* free seed fs chain */ + cur_seed = fs_devices->seed; + fs_devices->seed = NULL; + while (cur_seed) { + next_seed = cur_seed->seed; + free(cur_seed); + + cur_seed = next_seed; + } + + list_del(&fs_devices->list); + free(fs_devices); +} + +static int copy_device(struct btrfs_device *dst, + struct btrfs_device *src) +{ + dst->devid = src->devid; + memcpy(dst->uuid, src->uuid, BTRFS_UUID_SIZE); + if (src->name == NULL) + dst->name = NULL; + else { + dst->name = strdup(src->name); + if (!dst->name) + return -ENOMEM; + } + if (src->label == NULL) + dst->label = NULL; + else { + dst->label = strdup(src->label); + if (!dst->label) { + free(dst->name); + return -ENOMEM; + } + } + dst->total_devs = src->total_devs; + dst->super_bytes_used = src->super_bytes_used; + dst->total_bytes = src->total_bytes; + dst->bytes_used = src->bytes_used; + dst->generation = src->generation; + + return 0; +} + +static int copy_fs_devices(struct btrfs_fs_devices *dst, + struct btrfs_fs_devices *src) +{ + struct btrfs_device *cur_dev, *dev_copy; + int ret = 0; + + memcpy(dst->fsid, src->fsid, BTRFS_FSID_SIZE); + INIT_LIST_HEAD(&dst->devices); + dst->seed = NULL; + + list_for_each_entry(cur_dev, &src->devices, dev_list) { + dev_copy = malloc(sizeof(*dev_copy)); + if (!dev_copy) { + ret = -ENOMEM; + break; + } + + ret = copy_device(dev_copy, cur_dev); + if (ret) { + free(dev_copy); + break; + } + + list_add(&dev_copy->dev_list, &dst->devices); + dev_copy->fs_devices = dst; + } + + return ret; +} + +static int find_and_copy_seed(struct btrfs_fs_devices *seed, + struct btrfs_fs_devices *copy, + struct list_head *fs_uuids) { + struct btrfs_fs_devices *cur_fs; + + list_for_each_entry(cur_fs, fs_uuids, list) + if (!memcmp(seed->fsid, cur_fs->fsid, BTRFS_FSID_SIZE)) + return copy_fs_devices(copy, cur_fs); + + return 1; +} + +static int has_seed_devices(struct btrfs_fs_devices *fs_devices) +{ + struct btrfs_device *device; + int dev_cnt_total, dev_cnt = 0; + + device = list_first_entry(&fs_devices->devices, struct btrfs_device, + dev_list); + + dev_cnt_total = device->total_devs; + + list_for_each_entry(device, &fs_devices->devices, dev_list) + dev_cnt++; + + return dev_cnt_total != dev_cnt; +} + +static int search_umounted_fs_uuids(struct list_head *all_uuids, + char *search, int *found) +{ + struct btrfs_fs_devices *cur_fs, *fs_copy; + struct list_head *fs_uuids; + int ret = 0; + + fs_uuids = btrfs_scanned_uuids(); + + /* + * The fs_uuids list is global, and open_ctree_* will + * modify it, make a private copy here + */ + list_for_each_entry(cur_fs, fs_uuids, list) { + /* don't bother handle all fs, if search target specified */ + if (search) { + if (uuid_search(cur_fs, search) == 0) + continue; + if (found) + *found = 1; + } + + /* skip all fs already shown as mounted fs */ + if (is_seen_fsid(cur_fs->fsid)) + continue; + + fs_copy = malloc(sizeof(*fs_copy)); + if (!fs_copy) { + ret = -ENOMEM; + goto out; + } + + ret = copy_fs_devices(fs_copy, cur_fs); + if (ret) { + free(fs_copy); + goto out; + } + + list_add(&fs_copy->list, all_uuids); + } + +out: + return ret; +} + +static int map_seed_devices(struct list_head *all_uuids) +{ + struct btrfs_fs_devices *cur_fs, *cur_seed; + struct btrfs_fs_devices *seed_copy; + struct btrfs_fs_devices *opened_fs; + struct btrfs_device *device; + struct btrfs_fs_info *fs_info; + struct list_head *fs_uuids; + int ret = 0; + + fs_uuids = btrfs_scanned_uuids(); + + list_for_each_entry(cur_fs, all_uuids, list) { + device = list_first_entry(&cur_fs->devices, + struct btrfs_device, dev_list); + if (!device) + continue; + + /* skip fs without seeds */ + if (!has_seed_devices(cur_fs)) + continue; + + /* + * open_ctree_* detects seed/sprout mapping + */ + fs_info = open_ctree_fs_info(device->name, 0, 0, + OPEN_CTREE_PARTIAL); + if (!fs_info) + continue; + + /* + * copy the seed chain under the opened fs + */ + opened_fs = fs_info->fs_devices; + cur_seed = cur_fs; + while (opened_fs->seed) { + seed_copy = malloc(sizeof(*seed_copy)); + if (!seed_copy) { + ret = -ENOMEM; + goto fail_out; + } + ret = find_and_copy_seed(opened_fs->seed, seed_copy, + fs_uuids); + if (ret) { + free(seed_copy); + goto fail_out; + } + + cur_seed->seed = seed_copy; + + opened_fs = opened_fs->seed; + cur_seed = cur_seed->seed; + } + + close_ctree(fs_info->chunk_root); + } + +out: return ret; +fail_out: + close_ctree(fs_info->chunk_root); + goto out; } static const char * const cmd_show_usage[] = { @@ -488,40 +820,85 @@ static const char * const cmd_show_usage[] = { "Show the structure of a filesystem", "-d|--all-devices show only disks under /dev containing btrfs filesystem", "-m|--mounted show only mounted btrfs", + "--raw raw numbers in bytes", + "--human-readable human friendly numbers, base 1024 (default)", + "--iec use 1024 as a base (KiB, MiB, GiB, TiB)", + "--si use 1000 as a base (kB, MB, GB, TB)", + "--kbytes show sizes in KiB, or kB with --si", + "--mbytes show sizes in MiB, or MB with --si", + "--gbytes show sizes in GiB, or GB with --si", + "--tbytes show sizes in TiB, or TB with --si", "If no argument is given, structure of all present filesystems is shown.", NULL }; static int cmd_show(int argc, char **argv) { - struct list_head *all_uuids; + LIST_HEAD(all_uuids); struct btrfs_fs_devices *fs_devices; - struct list_head *cur_uuid; char *search = NULL; int ret; - int where = BTRFS_SCAN_LBLKID; + /* default, search both kernel and udev */ + int where = -1; int type = 0; - char mp[BTRFS_PATH_NAME_MAX + 1]; + char mp[PATH_MAX]; char path[PATH_MAX]; + __u8 fsid[BTRFS_FSID_SIZE]; + char uuid_buf[BTRFS_UUID_UNPARSED_SIZE]; + unsigned unit_mode = UNITS_DEFAULT; + int found = 0; while (1) { - int long_index; - static struct option long_options[] = { + int c; + static const struct option long_options[] = { { "all-devices", no_argument, NULL, 'd'}, { "mounted", no_argument, NULL, 'm'}, - { NULL, no_argument, NULL, 0 }, + { "raw", no_argument, NULL, GETOPT_VAL_RAW}, + { "kbytes", no_argument, NULL, GETOPT_VAL_KBYTES}, + { "mbytes", no_argument, NULL, GETOPT_VAL_MBYTES}, + { "gbytes", no_argument, NULL, GETOPT_VAL_GBYTES}, + { "tbytes", no_argument, NULL, GETOPT_VAL_TBYTES}, + { "si", no_argument, NULL, GETOPT_VAL_SI}, + { "iec", no_argument, NULL, GETOPT_VAL_IEC}, + { "human-readable", no_argument, NULL, + GETOPT_VAL_HUMAN_READABLE}, + { NULL, 0, NULL, 0 } }; - int c = getopt_long(argc, argv, "dm", long_options, - &long_index); + + c = getopt_long(argc, argv, "dm", long_options, NULL); if (c < 0) break; switch (c) { case 'd': - where = BTRFS_SCAN_DEV; + where = BTRFS_SCAN_LBLKID; break; case 'm': where = BTRFS_SCAN_MOUNTED; break; + case GETOPT_VAL_RAW: + units_set_mode(&unit_mode, UNITS_RAW); + break; + case GETOPT_VAL_KBYTES: + units_set_base(&unit_mode, UNITS_KBYTES); + break; + case GETOPT_VAL_MBYTES: + units_set_base(&unit_mode, UNITS_MBYTES); + break; + case GETOPT_VAL_GBYTES: + units_set_base(&unit_mode, UNITS_GBYTES); + break; + case GETOPT_VAL_TBYTES: + units_set_base(&unit_mode, UNITS_TBYTES); + break; + case GETOPT_VAL_SI: + units_set_mode(&unit_mode, UNITS_DECIMAL); + break; + case GETOPT_VAL_IEC: + units_set_mode(&unit_mode, UNITS_BINARY); + break; + case GETOPT_VAL_HUMAN_READABLE: + units_set_mode(&unit_mode, UNITS_HUMAN_BINARY); + break; default: usage(cmd_show_usage); } @@ -535,60 +912,99 @@ static int cmd_show(int argc, char **argv) if (strlen(search) == 0) usage(cmd_show_usage); type = check_arg_type(search); - if (type == BTRFS_ARG_BLKDEV) { - if (where == BTRFS_SCAN_DEV) { - /* we need to do this because - * legacy BTRFS_SCAN_DEV - * provides /dev/dm-x paths - */ - if (realpath(search, path)) - search = path; + + /* + * For search is a device: + * realpath do /dev/mapper/XX => /dev/dm-X + * which is required by BTRFS_SCAN_DEV + * For search is a mountpoint: + * realpath do /mnt/btrfs/ => /mnt/btrfs + * which shall be recognized by btrfs_scan_kernel() + */ + if (realpath(search, path)) + search = path; + + /* + * Needs special handling if input arg is block dev And if + * input arg is mount-point just print it right away + */ + if (type == BTRFS_ARG_BLKDEV && where != BTRFS_SCAN_LBLKID) { + ret = get_btrfs_mount(search, mp, sizeof(mp)); + if (!ret) { + /* given block dev is mounted */ + search = mp; + type = BTRFS_ARG_MNTPOINT; } else { - ret = get_btrfs_mount(search, - mp, sizeof(mp)); - if (!ret) - /* given block dev is mounted*/ - search = mp; - else - goto devs_only; + ret = dev_to_fsid(search, fsid); + if (ret) { + fprintf(stderr, + "ERROR: No btrfs on %s\n", + search); + return 1; + } + uuid_unparse(fsid, uuid_buf); + search = uuid_buf; + type = BTRFS_ARG_UUID; + goto devs_only; } } } - if (where == BTRFS_SCAN_DEV) + if (where == BTRFS_SCAN_LBLKID) goto devs_only; /* show mounted btrfs */ - ret = btrfs_scan_kernel(search); - if (search && !ret) - return 0; + ret = btrfs_scan_kernel(search, unit_mode); + if (search && !ret) { + /* since search is found we are done */ + goto out; + } /* shows mounted only */ if (where == BTRFS_SCAN_MOUNTED) goto out; devs_only: - ret = scan_for_btrfs(where, !BTRFS_UPDATE_KERNEL); + ret = btrfs_scan_lblkid(); if (ret) { fprintf(stderr, "ERROR: %d while scanning\n", ret); return 1; } - - all_uuids = btrfs_scanned_uuids(); - list_for_each(cur_uuid, all_uuids) { - fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, - list); - if (search && uuid_search(fs_devices, search) == 0) - continue; - print_one_uuid(fs_devices); + ret = search_umounted_fs_uuids(&all_uuids, search, &found); + if (ret < 0) { + fprintf(stderr, + "ERROR: %d while searching target device\n", ret); + return 1; } + /* + * The seed/sprout mapping are not detected yet, + * do mapping build for all umounted fs + */ + ret = map_seed_devices(&all_uuids); + if (ret) { + fprintf(stderr, + "ERROR: %d while mapping seed devices\n", ret); + return 1; + } + + list_for_each_entry(fs_devices, &all_uuids, list) + print_one_uuid(fs_devices, unit_mode); + + if (search && !found) + ret = 1; + + while (!list_empty(&all_uuids)) { + fs_devices = list_entry(all_uuids.next, + struct btrfs_fs_devices, list); + free_fs_devices(fs_devices); + } out: - printf("%s\n", BTRFS_BUILD_VERSION); + printf("%s\n", PACKAGE_STRING); free_seen_fsid(); - return 0; + return ret; } static const char * const cmd_sync_usage[] = { @@ -610,7 +1026,7 @@ static int cmd_sync(int argc, char **argv) fd = open_file_or_dir(path, &dirstream); if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); + fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } @@ -649,7 +1065,7 @@ static const char * const cmd_defrag_usage[] = { "-f flush data to disk immediately after defragmenting", "-s start defragment only from byte onward", "-l len defragment only up to len bytes", - "-t size minimal size of file to be considered for defragmenting", + "-t size target extent size hint", NULL }; @@ -677,7 +1093,7 @@ static int defrag_callback(const char *fpath, const struct stat *sb, int e = 0; int fd = 0; - if (typeflag == FTW_F) { + if ((typeflag == FTW_F) && S_ISREG(sb->st_mode)) { if (defrag_global_verbose) printf("%s\n", fpath); fd = open(fpath, O_RDWR); @@ -711,7 +1127,7 @@ static int cmd_defrag(int argc, char **argv) int flush = 0; u64 start = 0; u64 len = (u64)-1; - u32 thresh = 0; + u64 thresh = 0; int i; int recursive = 0; int ret = 0; @@ -754,6 +1170,11 @@ static int cmd_defrag(int argc, char **argv) break; case 't': thresh = parse_size(optarg); + if (thresh > (u32)-1) { + fprintf(stderr, + "WARNING: target extent size %llu too big, trimmed to %u", + thresh, (u32)-1); + } defrag_global_fancy_ioctl = 1; break; case 'r': @@ -770,7 +1191,7 @@ static int cmd_defrag(int argc, char **argv) memset(&defrag_global_range, 0, sizeof(range)); defrag_global_range.start = start; defrag_global_range.len = len; - defrag_global_range.extent_thresh = thresh; + defrag_global_range.extent_thresh = (u32)thresh; if (compress_type) { defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; defrag_global_range.compress_type = compress_type; @@ -779,6 +1200,8 @@ static int cmd_defrag(int argc, char **argv) defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO; for (i = optind; i < argc; i++) { + struct stat st; + dirstream = NULL; fd = open_file_or_dir(argv[i], &dirstream); if (fd < 0) { @@ -788,16 +1211,22 @@ static int cmd_defrag(int argc, char **argv) close_file_or_dir(fd, dirstream); continue; } + if (fstat(fd, &st)) { + fprintf(stderr, "ERROR: failed to stat %s - %s\n", + argv[i], strerror(errno)); + defrag_global_errors++; + close_file_or_dir(fd, dirstream); + continue; + } + if (!(S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) { + fprintf(stderr, + "ERROR: %s is not a directory or a regular file\n", + argv[i]); + defrag_global_errors++; + close_file_or_dir(fd, dirstream); + continue; + } if (recursive) { - struct stat st; - - if (fstat(fd, &st)) { - fprintf(stderr, "ERROR: failed to stat %s - %s\n", - argv[i], strerror(errno)); - defrag_global_errors++; - close_file_or_dir(fd, dirstream); - continue; - } if (S_ISDIR(st.st_mode)) { ret = nftw(argv[i], defrag_callback, 10, FTW_MOUNT | FTW_PHYS); @@ -834,7 +1263,7 @@ static int cmd_defrag(int argc, char **argv) } } if (defrag_global_verbose) - printf("%s\n", BTRFS_BUILD_VERSION); + printf("%s\n", PACKAGE_STRING); if (defrag_global_errors) fprintf(stderr, "total %d failures\n", defrag_global_errors); @@ -842,10 +1271,11 @@ static int cmd_defrag(int argc, char **argv) } static const char * const cmd_resize_usage[] = { - "btrfs filesystem resize [devid:][+/-][gkm]|[devid:]max ", + "btrfs filesystem resize [devid:][+/-][kKmMgGtTpPeE]|[devid:]max ", "Resize a filesystem", "If 'max' is passed, the filesystem will occupy all available space", "on the device 'devid'.", + "[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.", NULL }; @@ -855,6 +1285,7 @@ static int cmd_resize(int argc, char **argv) int fd, res, len, e; char *amount, *path; DIR *dirstream = NULL; + struct stat st; if (check_argc_exact(argc, 3)) usage(cmd_resize_usage); @@ -869,13 +1300,28 @@ static int cmd_resize(int argc, char **argv) return 1; } + res = stat(path, &st); + if (res < 0) { + fprintf(stderr, "ERROR: resize: cannot stat %s: %s\n", + path, strerror(errno)); + return 1; + } + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, + "ERROR: resize works on mounted filesystems and accepts only\n" + "directories as argument. Passing file containing a btrfs image\n" + "would resize the underlying filesystem instead of the image.\n"); + return 1; + } + fd = open_file_or_dir(path, &dirstream); if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); + fprintf(stderr, "ERROR: can't access '%s'\n", path); return 1; } printf("Resize '%s' of '%s'\n", path, amount); + memset(&args, 0, sizeof(args)); strncpy_null(args.name, amount); res = ioctl(fd, BTRFS_IOC_RESIZE, &args); e = errno; @@ -884,6 +1330,18 @@ static int cmd_resize(int argc, char **argv) fprintf(stderr, "ERROR: unable to resize '%s' - %s\n", path, strerror(e)); return 1; + } else if (res > 0) { + const char *err_str = btrfs_err_str(res); + + if (err_str) { + fprintf(stderr, "ERROR: btrfs error resizing '%s' - %s\n", + path, err_str); + } else { + fprintf(stderr, + "ERROR: btrfs error resizing '%s' - unknown btrfs_err_code %d\n", + path, res); + } + return 1; } return 0; } @@ -915,15 +1373,21 @@ static int cmd_label(int argc, char **argv) } } +static const char filesystem_cmd_group_info[] = +"overall filesystem tasks and information"; + const struct cmd_group filesystem_cmd_group = { - filesystem_cmd_group_usage, NULL, { - { "df", cmd_df, cmd_df_usage, NULL, 0 }, + filesystem_cmd_group_usage, filesystem_cmd_group_info, { + { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 }, { "show", cmd_show, cmd_show_usage, NULL, 0 }, { "sync", cmd_sync, cmd_sync_usage, NULL, 0 }, { "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 }, - { "balance", cmd_balance, NULL, &balance_cmd_group, 1 }, + { "balance", cmd_balance, NULL, &balance_cmd_group, CMD_HIDDEN }, { "resize", cmd_resize, cmd_resize_usage, NULL, 0 }, { "label", cmd_label, cmd_label_usage, NULL, 0 }, + { "usage", cmd_filesystem_usage, + cmd_filesystem_usage_usage, NULL, 0 }, + NULL_CMD_STRUCT } };