X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-filesystem.c;h=225df42145d6f5cf960216db337d72a11509aefd;hb=905b3d18ae118931839b0a1ece17870073b51484;hp=27f8888749b7bf8b9753881c66281d5edec679c3;hpb=c125b7cf43aac815782ded0a0f36060e81c79726;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 27f8888..225df42 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -22,50 +22,49 @@ #include #include #include +#include +#include +#include +#include +#include + +#include #include "kerncompat.h" #include "ctree.h" -#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" +#include "help.h" + +/* + * for btrfs fi show, we maintain a hash of fsids we've already printed. + * This way we don't print dups if a given FS is mounted more than once. + */ +static struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = {NULL,}; static const char * const filesystem_cmd_group_usage[] = { "btrfs filesystem [] []", NULL }; -static const char * const cmd_df_usage[] = { - "btrfs filesystem df ", +static const char * const cmd_filesystem_df_usage[] = { + "btrfs filesystem df [options] ", "Show space usage information for a mount point", + HELPINFO_UNITS_SHORT_LONG, NULL }; -static int cmd_df(int argc, char **argv) +static int get_df(int fd, struct btrfs_ioctl_space_args **sargs_ret) { - struct btrfs_ioctl_space_args *sargs, *sargs_orig; - u64 count = 0, i; + u64 count = 0; int ret; - int fd; - int e; - char *path; - DIR *dirstream = NULL; - - if (check_argc_exact(argc, 2)) - usage(cmd_df_usage); - - path = argv[1]; + struct btrfs_ioctl_space_args *sargs; - fd = open_file_or_dir(path, &dirstream); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); - return 12; - } - - sargs_orig = sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); + sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); if (!sargs) return -ENOMEM; @@ -73,107 +72,118 @@ static int cmd_df(int argc, char **argv) sargs->total_spaces = 0; ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", - path, strerror(e)); - goto out; + if (ret < 0) { + error("cannot get space info: %m"); + free(sargs); + return -errno; } + /* This really should never happen */ if (!sargs->total_spaces) { - ret = 0; - goto out; + free(sargs); + return -ENOENT; } - count = sargs->total_spaces; + free(sargs); - sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + + sargs = malloc(sizeof(struct btrfs_ioctl_space_args) + (count * sizeof(struct btrfs_ioctl_space_info))); - if (!sargs) { - sargs = sargs_orig; - ret = -ENOMEM; - goto out; - } + if (!sargs) + return -ENOMEM; sargs->space_slots = count; sargs->total_spaces = 0; - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", - path, strerror(e)); - goto out; + if (ret < 0) { + error("cannot get space info with %llu slots: %m", + count); + free(sargs); + return -errno; } + *sargs_ret = sargs; + return 0; +} - for (i = 0; i < sargs->total_spaces; i++) { - char description[80]; - int written = 0; - u64 flags = sargs->spaces[i].flags; +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", + 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)); + } +} - memset(description, 0, 80); +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; + unsigned unit_mode; - if (flags & BTRFS_BLOCK_GROUP_DATA) { - if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 14, "%s", - "Data+Metadata"); - written += 13; - } else { - snprintf(description, 5, "%s", "Data"); - written += 4; - } - } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { - snprintf(description, 7, "%s", "System"); - written += 6; - } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 9, "%s", "Metadata"); - written += 8; - } + unit_mode = get_unit_mode_from_arg(&argc, argv, 1); - if (flags & BTRFS_BLOCK_GROUP_RAID0) { - snprintf(description+written, 8, "%s", ", RAID0"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { - snprintf(description+written, 8, "%s", ", RAID1"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_DUP) { - snprintf(description+written, 6, "%s", ", DUP"); - written += 5; - } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { - snprintf(description+written, 9, "%s", ", RAID10"); - written += 8; - } else if (flags & BTRFS_BLOCK_GROUP_RAID5) { - snprintf(description+written, 9, "%s", ", RAID5"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID6) { - snprintf(description+written, 9, "%s", ", RAID6"); - written += 7; - } + clean_args_no_options(argc, argv, cmd_filesystem_df_usage); - printf("%s: total=%s, used=%s\n", description, - pretty_size(sargs->spaces[i].total_bytes), - pretty_size(sargs->spaces[i].used_bytes)); + if (check_argc_exact(argc - optind, 1)) + usage(cmd_filesystem_df_usage); + + path = argv[optind]; + + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + return 1; + + ret = get_df(fd, &sargs); + + if (ret == 0) { + print_df(sargs, unit_mode); + free(sargs); + } else { + error("get_df failed %s", strerror(-ret)); } -out: + close_file_or_dir(fd, dirstream); - free(sargs); + return !!ret; +} + +static int match_search_item_kernel(u8 *fsid, char *mnt, char *label, + char *search) +{ + char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; + int search_len = strlen(search); + + search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE); + uuid_unparse(fsid, uuidbuf); + if (!strncmp(uuidbuf, search, search_len)) + return 1; + + if (*label && strcmp(label, search) == 0) + return 1; + + if (strcmp(mnt, search) == 0) + return 1; return 0; } -static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +static int uuid_search(struct btrfs_fs_devices *fs_devices, const char *search) { - char uuidbuf[37]; - struct list_head *cur; + char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_device *device; int search_len = strlen(search); - search_len = min(search_len, 37); + search_len = min(search_len, BTRFS_UUID_UNPARSED_SIZE); uuid_unparse(fs_devices->fsid, uuidbuf); if (!strncmp(uuidbuf, search, search_len)) return 1; - list_for_each(cur, &fs_devices->devices) { - device = list_entry(cur, struct btrfs_device, dev_list); + list_for_each_entry(device, &fs_devices->devices, dev_list) { if ((device->label && strcmp(device->label, search) == 0) || strcmp(device->name, search) == 0) return 1; @@ -181,14 +191,95 @@ static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) return 0; } -static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +/* + * Sort devices by devid, ascending + */ +static int cmp_device_id(void *priv, struct list_head *a, + struct list_head *b) +{ + const struct btrfs_device *da = list_entry(a, struct btrfs_device, + dev_list); + const struct btrfs_device *db = list_entry(b, struct btrfs_device, + dev_list); + + return da->devid < db->devid ? -1 : + da->devid > db->devid ? 1 : 0; +} + +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[37]; - struct list_head *cur; + char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_device *device; u64 devs_found = 0; u64 total; + if (add_seen_fsid(fs_devices->fsid, seen_fsid_hash, -1, NULL)) + return; + uuid_unparse(fs_devices->fsid, uuidbuf); device = list_entry(fs_devices->devices.next, struct btrfs_device, dev_list); @@ -197,109 +288,544 @@ 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)); + pretty_size_mode(device->super_bytes_used, unit_mode)); + + print_devices(fs_devices, &devs_found, unit_mode); + + if (devs_found < total) { + printf("\t*** Some devices missing\n"); + } + printf("\n"); +} + +/* adds up all the used spaces as reported by the space info ioctl + */ +static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si) +{ + u64 ret = 0; + int i; + for (i = 0; i < si->total_spaces; i++) + ret += si->spaces[i].used_bytes; + return ret; +} + +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, 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; + + ret = add_seen_fsid(fs_info->fsid, seen_fsid_hash, -1, NULL); + if (ret == -EEXIST) + return 0; + else if (ret) + return ret; + + uuid_unparse(fs_info->fsid, uuidbuf); + if (label && *label) + printf("Label: '%s' ", label); + else + printf("Label: none "); + + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, + fs_info->num_devices, + pretty_size_mode(calc_used_bytes(space_info), + unit_mode)); + + for (i = 0; i < fs_info->num_devices; i++) { + char *canonical_path; - list_for_each(cur, &fs_devices->devices) { - device = list_entry(cur, struct btrfs_device, dev_list); + 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", - (unsigned long long)device->devid, - pretty_size(device->total_bytes), - pretty_size(device->bytes_used), device->name); + tmp_dev_info->devid, + pretty_size_mode(tmp_dev_info->total_bytes, unit_mode), + pretty_size_mode(tmp_dev_info->bytes_used, unit_mode), + canonical_path); - devs_found++; + free(canonical_path); } - if (devs_found < total) { + + if (missing) printf("\t*** Some devices missing\n"); - } printf("\n"); + return 0; +} + +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 = NULL; + char label[BTRFS_LABEL_SIZE]; + + f = setmntent("/proc/self/mounts", "r"); + if (f == NULL) + return 1; + + memset(label, 0, sizeof(label)); + while ((mnt = getmntent(f)) != NULL) { + free(dev_info_arg); + dev_info_arg = NULL; + if (strcmp(mnt->mnt_type, "btrfs")) + continue; + ret = get_fs_info(mnt->mnt_dir, &fs_info_arg, + &dev_info_arg); + if (ret) + goto out; + + /* skip all fs already shown as mounted fs */ + if (is_seen_fsid(fs_info_arg.fsid, seen_fsid_hash)) + continue; + + ret = get_label_mounted(mnt->mnt_dir, label); + /* provide backward kernel compatibility */ + if (ret == -ENOTTY) + ret = get_label_unmounted( + (const char *)dev_info_arg->path, label); + + if (ret) + goto out; + + if (search && !match_search_item_kernel(fs_info_arg.fsid, + mnt->mnt_dir, label, search)) { + continue; + } + + 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, unit_mode); + free(space_info_arg); + memset(label, 0, sizeof(label)); + found = 1; + } + if (fd != -1) + close(fd); + } + +out: + free(dev_info_arg); + endmntent(f); + return !found; +} + +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 const char * const cmd_show_usage[] = { - "btrfs filesystem show [--all-devices] [|