X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-device.c;h=86459d1b956430585c5649986dbd346a16cb6409;hb=f44a0550123be92245943d832df64134c5fd6241;hp=5be83300e62dc35d43676aeced7f49a8c4ef0fbd;hpb=d89437893266332cce295c06c9d00b79aa5b4dd6;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-device.c b/cmds-device.c index 5be8330..86459d1 100644 --- a/cmds-device.c +++ b/cmds-device.c @@ -28,15 +28,19 @@ #include "ctree.h" #include "ioctl.h" #include "utils.h" +#include "volumes.h" +#include "cmds-fi-usage.h" #include "commands.h" +#include "help.h" +#include "mkfs/common.h" static const char * const device_cmd_group_usage[] = { "btrfs device []", NULL }; -static const char * const cmd_add_dev_usage[] = { +static const char * const cmd_device_add_usage[] = { "btrfs device add [options] [...] ", "Add a device to a filesystem", "-K|--nodiscard do not perform whole device TRIM", @@ -44,24 +48,24 @@ static const char * const cmd_add_dev_usage[] = { NULL }; -static int cmd_add_dev(int argc, char **argv) +static int cmd_device_add(int argc, char **argv) { char *mntpnt; - int i, fdmnt, ret=0, e; + int i, fdmnt, ret = 0; DIR *dirstream = NULL; int discard = 1; int force = 0; - char estr[100]; + int last_dev; while (1) { - int long_index; - static struct option long_options[] = { + int c; + static const struct option long_options[] = { { "nodiscard", optional_argument, NULL, 'K'}, { "force", no_argument, NULL, 'f'}, - { 0, 0, 0, 0 } + { NULL, 0, NULL, 0} }; - int c = getopt_long(argc, argv, "Kf", long_options, - &long_index); + + c = getopt_long(argc, argv, "Kf", long_options, NULL); if (c < 0) break; switch (c) { @@ -72,60 +76,64 @@ static int cmd_add_dev(int argc, char **argv) force = 1; break; default: - usage(cmd_add_dev_usage); + usage(cmd_device_add_usage); } } - argc = argc - optind; - - if (check_argc_min(argc, 2)) - usage(cmd_add_dev_usage); + if (check_argc_min(argc - optind, 2)) + usage(cmd_device_add_usage); - mntpnt = argv[optind + argc - 1]; + last_dev = argc - 1; + mntpnt = argv[last_dev]; - fdmnt = open_file_or_dir(mntpnt, &dirstream); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1); + if (fdmnt < 0) return 1; - } - for (i = optind; i < optind + argc - 1; i++){ + for (i = optind; i < last_dev; i++){ struct btrfs_ioctl_vol_args ioctl_args; int devfd, res; u64 dev_block_count = 0; - int mixed = 0; + char *path; - res = test_dev_for_mkfs(argv[i], force, estr); + res = test_dev_for_mkfs(argv[i], force); if (res) { - fprintf(stderr, "%s", estr); ret++; continue; } devfd = open(argv[i], O_RDWR); if (devfd < 0) { - fprintf(stderr, "ERROR: Unable to open device '%s'\n", argv[i]); + error("unable to open device '%s'", argv[i]); ret++; continue; } - res = btrfs_prepare_device(devfd, argv[i], 1, &dev_block_count, - 0, &mixed, discard); + res = btrfs_prepare_device(devfd, argv[i], &dev_block_count, 0, + PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE | + (discard ? PREP_DEVICE_DISCARD : 0)); close(devfd); if (res) { ret++; goto error_out; } - strncpy_null(ioctl_args.name, argv[i]); - res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); - e = errno; - if(res<0){ - fprintf(stderr, "ERROR: error adding the device '%s' - %s\n", - argv[i], strerror(e)); + path = canonicalize_path(argv[i]); + if (!path) { + error("could not canonicalize pathname '%s': %m", + argv[i]); ret++; + goto error_out; } + memset(&ioctl_args, 0, sizeof(ioctl_args)); + strncpy_null(ioctl_args.name, path); + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); + if (res < 0) { + error("error adding device '%s': %m", path); + ret++; + } + free(path); } error_out: @@ -133,51 +141,79 @@ error_out: return !!ret; } -static const char * const cmd_rm_dev_usage[] = { - "btrfs device delete [...] ", - "Remove a device from a filesystem", - NULL -}; - -static int cmd_rm_dev(int argc, char **argv) +static int _cmd_device_remove(int argc, char **argv, + const char * const *usagestr) { char *mntpnt; - int i, fdmnt, ret=0, e; + int i, fdmnt, ret = 0; DIR *dirstream = NULL; - if (check_argc_min(argc, 3)) - usage(cmd_rm_dev_usage); + clean_args_no_options(argc, argv, usagestr); + + if (check_argc_min(argc - optind, 2)) + usage(usagestr); mntpnt = argv[argc - 1]; - fdmnt = open_file_or_dir(mntpnt, &dirstream); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1); + if (fdmnt < 0) return 1; - } - for(i=1 ; i < argc - 1; i++ ){ + for(i = optind; i < argc - 1; i++) { struct btrfs_ioctl_vol_args arg; + struct btrfs_ioctl_vol_args_v2 argv2 = {0}; + int is_devid = 0; int res; - if (!is_block_device(argv[i])) { - fprintf(stderr, - "ERROR: %s is not a block device\n", argv[i]); + if (string_is_numerical(argv[i])) { + argv2.devid = arg_strtou64(argv[i]); + argv2.flags = BTRFS_DEVICE_SPEC_BY_ID; + is_devid = 1; + } else if (is_block_device(argv[i]) == 1 || + strcmp(argv[i], "missing") == 0) { + strncpy_null(argv2.name, argv[i]); + } else { + error("not a block device: %s", argv[i]); ret++; continue; } - strncpy_null(arg.name, argv[i]); - res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); - e = errno; - if (res > 0) { - fprintf(stderr, - "ERROR: error removing the device '%s' - %s\n", - argv[i], btrfs_err_str(res)); - ret++; - } else if (res < 0) { - fprintf(stderr, - "ERROR: error removing the device '%s' - %s\n", - argv[i], strerror(e)); + + /* + * Positive values are from BTRFS_ERROR_DEV_*, + * otherwise it's a generic error, one of errnos + */ + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2); + + /* + * If BTRFS_IOC_RM_DEV_V2 is not supported we get ENOTTY and if + * argv2.flags includes a flag which kernel doesn't understand then + * we shall get EOPNOTSUPP + */ + if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) { + if (is_devid) { + error("device delete by id failed: %m"); + ret++; + continue; + } + memset(&arg, 0, sizeof(arg)); + strncpy_null(arg.name, argv[i]); + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); + } + + if (res) { + const char *msg; + + if (res > 0) + msg = btrfs_err_str(res); + else + msg = strerror(errno); + if (is_devid) { + error("error removing devid %llu: %s", + (unsigned long long)argv2.devid, msg); + } else { + error("error removing device '%s': %s", + argv[i], msg); + } ret++; } } @@ -186,123 +222,175 @@ static int cmd_rm_dev(int argc, char **argv) return !!ret; } -static const char * const cmd_scan_dev_usage[] = { - "btrfs device scan [<--all-devices>| [...]]", - "Scan devices for a btrfs filesystem", +#define COMMON_USAGE_REMOVE_DELETE \ + "If 'missing' is specified for , the first device that is", \ + "described by the filesystem metadata, but not present at the mount", \ + "time will be removed. (only in degraded mode)" + +static const char * const cmd_device_remove_usage[] = { + "btrfs device remove | [|...] ", + "Remove a device from a filesystem", + "", + COMMON_USAGE_REMOVE_DELETE, + NULL +}; + +static int cmd_device_remove(int argc, char **argv) +{ + return _cmd_device_remove(argc, argv, cmd_device_remove_usage); +} + +static const char * const cmd_device_delete_usage[] = { + "btrfs device delete | [|...] ", + "Remove a device from a filesystem (alias of \"btrfs device remove\")", + "", + COMMON_USAGE_REMOVE_DELETE, NULL }; -static int cmd_scan_dev(int argc, char **argv) +static int cmd_device_delete(int argc, char **argv) { - int i, fd, e; - int where = BTRFS_SCAN_LBLKID; - int devstart = 1; + return _cmd_device_remove(argc, argv, cmd_device_delete_usage); +} - if( argc > 1 && !strcmp(argv[1],"--all-devices")){ - if (check_argc_max(argc, 2)) - usage(cmd_scan_dev_usage); +static const char * const cmd_device_scan_usage[] = { + "btrfs device scan [(-d|--all-devices)| [...]]", + "Scan devices for a btrfs filesystem", + " -d|--all-devices (deprecated)", + NULL +}; - where = BTRFS_SCAN_DEV; - devstart += 1; - } +static int cmd_device_scan(int argc, char **argv) +{ + int i; + int devstart; + int all = 0; + int ret = 0; - if(argc<=devstart){ - int ret; - printf("Scanning for Btrfs filesystems\n"); - ret = scan_for_btrfs(where, BTRFS_UPDATE_KERNEL); - if (ret){ - fprintf(stderr, "ERROR: error %d while scanning\n", ret); - return 1; + while (1) { + int c; + static const struct option long_options[] = { + { "all-devices", no_argument, NULL, 'd'}, + { NULL, 0, NULL, 0} + }; + + c = getopt_long(argc, argv, "d", long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'd': + all = 1; + break; + default: + usage(cmd_device_scan_usage); } - return 0; } + devstart = optind; - fd = open("/dev/btrfs-control", O_RDWR); - if (fd < 0) { - perror("failed to open /dev/btrfs-control"); - return 1; + if (all && check_argc_max(argc - optind, 1)) + usage(cmd_device_scan_usage); + + if (all || argc - optind == 0) { + printf("Scanning for Btrfs filesystems\n"); + ret = btrfs_scan_devices(); + error_on(ret, "error %d while scanning", ret); + ret = btrfs_register_all_devices(); + error_on(ret, "there are %d errors while registering devices", ret); + goto out; } for( i = devstart ; i < argc ; i++ ){ - struct btrfs_ioctl_vol_args args; - int ret; - - if (!is_block_device(argv[i])) { - fprintf(stderr, - "ERROR: %s is not a block device\n", argv[i]); - close(fd); - return 1; - } - printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); + char *path; - strncpy_null(args.name, argv[i]); - /* - * FIXME: which are the error code returned by this ioctl ? - * it seems that is impossible to understand if there no is - * a btrfs filesystem from an I/O error !!! - */ - ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); - e = errno; - - if( ret < 0 ){ - close(fd); - fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n", - argv[i], strerror(e)); - return 1; + if (is_block_device(argv[i]) != 1) { + error("not a block device: %s", argv[i]); + ret = 1; + goto out; } + path = canonicalize_path(argv[i]); + if (!path) { + error("could not canonicalize path '%s': %m", argv[i]); + ret = 1; + goto out; + } + printf("Scanning for Btrfs filesystems in '%s'\n", path); + if (btrfs_register_one_device(path) != 0) { + ret = 1; + free(path); + goto out; + } + free(path); } - close(fd); - return 0; +out: + return !!ret; } -static const char * const cmd_ready_dev_usage[] = { +static const char * const cmd_device_ready_usage[] = { "btrfs device ready ", - "Check device to see if it has all of it's devices in cache for mounting", + "Check device to see if it has all of its devices in cache for mounting", NULL }; -static int cmd_ready_dev(int argc, char **argv) +static int cmd_device_ready(int argc, char **argv) { struct btrfs_ioctl_vol_args args; int fd; int ret; + char *path; + + clean_args_no_options(argc, argv, cmd_device_ready_usage); - if (check_argc_min(argc, 2)) - usage(cmd_ready_dev_usage); + if (check_argc_exact(argc - optind, 1)) + usage(cmd_device_ready_usage); fd = open("/dev/btrfs-control", O_RDWR); if (fd < 0) { perror("failed to open /dev/btrfs-control"); return 1; } - if (!is_block_device(argv[1])) { - fprintf(stderr, - "ERROR: %s is not a block device\n", argv[1]); - close(fd); - return 1; + + path = canonicalize_path(argv[optind]); + if (!path) { + error("could not canonicalize pathname '%s': %m", + argv[optind]); + ret = 1; + goto out; } - strncpy(args.name, argv[argc - 1], BTRFS_PATH_NAME_MAX); + if (is_block_device(path) != 1) { + error("not a block device: %s", path); + ret = 1; + goto out; + } + + memset(&args, 0, sizeof(args)); + strncpy_null(args.name, path); ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args); if (ret < 0) { - fprintf(stderr, "ERROR: unable to determine if the device '%s'" - " is ready for mounting - %s\n", argv[argc - 1], - strerror(errno)); + error("unable to determine if device '%s' is ready for mount: %m", + path); ret = 1; } +out: + free(path); close(fd); return ret; } -static const char * const cmd_dev_stats_usage[] = { - "btrfs device stats [-z] |", - "Show current device IO stats. -z to reset stats afterwards.", +static const char * const cmd_device_stats_usage[] = { + "btrfs device stats [options] |", + "Show device IO error statistics", + "Show device IO error statistics for all devices of the given filesystem", + "identified by PATH or DEVICE. The filesystem must be mounted.", + "", + "-c|--check return non-zero if any stat counter is not zero", + "-z|--reset show current stats and reset values to zero", NULL }; -static int cmd_dev_stats(int argc, char **argv) +static int cmd_device_stats(int argc, char **argv) { char *dev_path; struct btrfs_ioctl_fs_info_args fi_args; @@ -310,97 +398,117 @@ static int cmd_dev_stats(int argc, char **argv) int ret; int fdmnt; int i; - int c; int err = 0; + int check = 0; __u64 flags = 0; DIR *dirstream = NULL; - optind = 1; - while ((c = getopt(argc, argv, "z")) != -1) { + while (1) { + int c; + static const struct option long_options[] = { + {"check", no_argument, NULL, 'c'}, + {"reset", no_argument, NULL, 'z'}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long(argc, argv, "cz", long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'c': + check = 1; + break; case 'z': flags = BTRFS_DEV_STATS_RESET; break; case '?': default: - fprintf(stderr, "ERROR: device stat args invalid.\n" - " device stat [-z] |\n" - " -z to reset stats after reading.\n"); - return 1; + usage(cmd_device_stats_usage); } } - if (optind + 1 != argc) { - fprintf(stderr, "ERROR: device stat needs path|device as single" - " argument\n"); - return 1; - } + if (check_argc_exact(argc - optind, 1)) + usage(cmd_device_stats_usage); dev_path = argv[optind]; - fdmnt = open_path_or_dev_mnt(dev_path, &dirstream); - - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", dev_path); + fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1); + if (fdmnt < 0) return 1; - } ret = get_fs_info(dev_path, &fi_args, &di_args); if (ret) { - fprintf(stderr, "ERROR: getting dev info for devstats failed: " - "%s\n", strerror(-ret)); + error("getting device info for %s failed: %s", dev_path, + strerror(-ret)); err = 1; goto out; } if (!fi_args.num_devices) { - fprintf(stderr, "ERROR: no devices found\n"); + error("no devices found"); err = 1; goto out; } for (i = 0; i < fi_args.num_devices; i++) { struct btrfs_ioctl_get_dev_stats args = {0}; - __u8 path[BTRFS_DEVICE_PATH_NAME_MAX + 1]; + char path[BTRFS_DEVICE_PATH_NAME_MAX + 1]; - strncpy((char *)path, (char *)di_args[i].path, + strncpy(path, (char *)di_args[i].path, BTRFS_DEVICE_PATH_NAME_MAX); - path[BTRFS_DEVICE_PATH_NAME_MAX] = '\0'; + path[BTRFS_DEVICE_PATH_NAME_MAX] = 0; args.devid = di_args[i].devid; args.nr_items = BTRFS_DEV_STAT_VALUES_MAX; args.flags = flags; if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) { - fprintf(stderr, - "ERROR: ioctl(BTRFS_IOC_GET_DEV_STATS) on %s failed: %s\n", - path, strerror(errno)); - err = 1; + error("device stats ioctl failed on %s: %m", + path); + err |= 1; } else { - if (args.nr_items >= BTRFS_DEV_STAT_WRITE_ERRS + 1) - printf("[%s].write_io_errs %llu\n", - path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_WRITE_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_READ_ERRS + 1) - printf("[%s].read_io_errs %llu\n", - path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_READ_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_FLUSH_ERRS + 1) - printf("[%s].flush_io_errs %llu\n", - path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_FLUSH_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_CORRUPTION_ERRS + 1) - printf("[%s].corruption_errs %llu\n", - path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_CORRUPTION_ERRS]); - if (args.nr_items >= BTRFS_DEV_STAT_GENERATION_ERRS + 1) - printf("[%s].generation_errs %llu\n", - path, - (unsigned long long) args.values[ - BTRFS_DEV_STAT_GENERATION_ERRS]); + char *canonical_path; + int j; + static const struct { + const char name[32]; + u64 num; + } dev_stats[] = { + { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS }, + { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS }, + { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS }, + { "corruption_errs", + BTRFS_DEV_STAT_CORRUPTION_ERRS }, + { "generation_errs", + BTRFS_DEV_STAT_GENERATION_ERRS }, + }; + + canonical_path = canonicalize_path(path); + + /* No path when device is missing. */ + if (!canonical_path) { + canonical_path = malloc(32); + if (!canonical_path) { + error("not enough memory for path buffer"); + goto out; + } + snprintf(canonical_path, 32, + "devid:%llu", args.devid); + } + + for (j = 0; j < ARRAY_SIZE(dev_stats); j++) { + /* We got fewer items than we know */ + if (args.nr_items < dev_stats[j].num + 1) + continue; + printf("[%s].%-16s %llu\n", canonical_path, + dev_stats[j].name, + (unsigned long long) + args.values[dev_stats[j].num]); + if ((check == 1) + && (args.values[dev_stats[j].num] > 0)) + err |= 64; + } + + free(canonical_path); } } @@ -411,13 +519,92 @@ out: return err; } +static const char * const cmd_device_usage_usage[] = { + "btrfs device usage [options] [..]", + "Show detailed information about internal allocations in devices.", + HELPINFO_UNITS_SHORT_LONG, + NULL +}; + +static int _cmd_device_usage(int fd, char *path, unsigned unit_mode) +{ + int i; + int ret = 0; + struct chunk_info *chunkinfo = NULL; + struct device_info *devinfo = NULL; + int chunkcount = 0; + int devcount = 0; + + ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount, &devinfo, + &devcount); + if (ret) + goto out; + + for (i = 0; i < devcount; i++) { + printf("%s, ID: %llu\n", devinfo[i].path, devinfo[i].devid); + print_device_sizes(&devinfo[i], unit_mode); + print_device_chunks(&devinfo[i], chunkinfo, chunkcount, + unit_mode); + printf("\n"); + } + +out: + free(devinfo); + free(chunkinfo); + + return ret; +} + +static int cmd_device_usage(int argc, char **argv) +{ + unsigned unit_mode; + int ret = 0; + int i; + + unit_mode = get_unit_mode_from_arg(&argc, argv, 1); + + clean_args_no_options(argc, argv, cmd_device_usage_usage); + + if (check_argc_min(argc - optind, 1)) + usage(cmd_device_usage_usage); + + for (i = optind; i < argc; i++) { + int fd; + DIR *dirstream = NULL; + + if (i > 1) + printf("\n"); + + fd = btrfs_open_dir(argv[i], &dirstream, 1); + if (fd < 0) { + ret = 1; + break; + } + + ret = _cmd_device_usage(fd, argv[i], unit_mode); + close_file_or_dir(fd, dirstream); + + if (ret) + break; + } + + return !!ret; +} + +static const char device_cmd_group_info[] = +"manage and query devices in the filesystem"; + const struct cmd_group device_cmd_group = { - device_cmd_group_usage, NULL, { - { "add", cmd_add_dev, cmd_add_dev_usage, NULL, 0 }, - { "delete", cmd_rm_dev, cmd_rm_dev_usage, NULL, 0 }, - { "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 }, - { "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 }, - { "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 }, + device_cmd_group_usage, device_cmd_group_info, { + { "add", cmd_device_add, cmd_device_add_usage, NULL, 0 }, + { "delete", cmd_device_delete, cmd_device_delete_usage, NULL, + CMD_ALIAS }, + { "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 }, + { "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 }, + { "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 }, + { "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 }, + { "usage", cmd_device_usage, + cmd_device_usage_usage, NULL, 0 }, NULL_CMD_STRUCT } };