X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cmds-replace.c;h=032a44fcda3c6620ac75235c84f5b659191124b4;hb=31ceca8ddd0039550821227a7990f13622c725a7;hp=10030f64fe3a8ad4e23f0ac7d06a9498147d60c7;hpb=7b81119b1d3904cf00ec29140e8bc5d86a12902f;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/cmds-replace.c b/cmds-replace.c index 10030f6..032a44f 100644 --- a/cmds-replace.c +++ b/cmds-replace.c @@ -37,7 +37,8 @@ #include "disk-io.h" #include "commands.h" - +#include "help.h" +#include "mkfs/common.h" static int print_replace_status(int fd, const char *path, int once); static char *time2string(char *buf, size_t s, __u64 t); @@ -53,6 +54,8 @@ static const char *replace_dev_result2string(__u64 result) return "not started"; case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED: return "already started"; + case BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS: + return "scrub is in progress"; default: return ""; } @@ -63,17 +66,6 @@ static const char * const replace_cmd_group_usage[] = { NULL }; -static int is_numerical(const char *str) -{ - if (!(*str >= '0' && *str <= '9')) - return 0; - while (*str >= '0' && *str <= '9') - str++; - if (*str != '\0') - return 0; - return 1; -} - static int dev_replace_cancel_fd = -1; static void dev_replace_sigint_handler(int signal) { @@ -96,33 +88,33 @@ static int dev_replace_handle_sigint(int fd) return sigaction(SIGINT, &sa, NULL); } -static const char *const cmd_start_replace_usage[] = { - "btrfs replace start srcdev|devid targetdev [-Bfr] mount_point", +static const char *const cmd_replace_start_usage[] = { + "btrfs replace start [-Bfr] | ", "Replace device of a btrfs filesystem.", "On a live filesystem, duplicate the data to the target device which", "is currently stored on the source device. If the source device is not", "available anymore, or if the -r option is set, the data is built", "only using the RAID redundancy mechanisms. After completion of the", "operation, the source device is removed from the filesystem.", - "If the srcdev is a numerical value, it is assumed to be the device id", - "of the filesystem which is mounted at mount_point, otherwise it is", + "If the is a numerical value, it is assumed to be the device id", + "of the filesystem which is mounted at , otherwise it is", "the path to the source device. If the source device is disconnected,", - "from the system, you have to use the devid parameter format.", - "The targetdev needs to be same size or larger than the srcdev.", + "from the system, you have to use the parameter format.", + "The needs to be same size or larger than the .", "", - "-r only read from srcdev if no other zero-defect mirror exists", + "-r only read from if no other zero-defect mirror exists", " (enable this if your drive has lots of read errors, the access", " would be very slow)", - "-f force using and overwriting targetdev even if it looks like", + "-f force using and overwriting even if it looks like", " containing a valid btrfs filesystem. A valid filesystem is", " assumed if a btrfs superblock is found which contains a", " correct checksum. Devices which are currently mounted are", - " never allowed to be used as the targetdev", + " never allowed to be used as the ", "-B do not background", NULL }; -static int cmd_start_replace(int argc, char **argv) +static int cmd_replace_start(int argc, char **argv) { struct btrfs_ioctl_dev_replace_args start_args = {0}; struct btrfs_ioctl_dev_replace_args status_args = {0}; @@ -130,19 +122,17 @@ static int cmd_start_replace(int argc, char **argv) int i; int c; int fdmnt = -1; - int fdsrcdev = -1; int fddstdev = -1; char *path; char *srcdev; - char *dstdev; + char *dstdev = NULL; int avoid_reading_from_srcdev = 0; int force_using_targetdev = 0; - u64 total_devs = 1; - struct btrfs_fs_devices *fs_devices_mnt = NULL; - struct stat st; u64 dstdev_block_count; int do_not_background = 0; - int mixed = 0; + DIR *dirstream = NULL; + u64 srcdev_size; + u64 dstdev_size; while ((c = getopt(argc, argv, "Brf")) != -1) { switch (c) { @@ -157,7 +147,7 @@ static int cmd_start_replace(int argc, char **argv) break; case '?': default: - usage(cmd_start_replace_usage); + usage(cmd_replace_start_usage); } } @@ -166,64 +156,63 @@ static int cmd_start_replace(int argc, char **argv) BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID : BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS; if (check_argc_exact(argc - optind, 3)) - usage(cmd_start_replace_usage); + usage(cmd_replace_start_usage); path = argv[optind + 2]; - fdmnt = open_file_or_dir(path); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access \"%s\": %s\n", - path, strerror(errno)); + + fdmnt = open_path_or_dev_mnt(path, &dirstream, 1); + if (fdmnt < 0) goto leave_with_error; - } /* check for possible errors before backgrounding */ status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS; + status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT; ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args); - if (ret) { + if (ret < 0) { fprintf(stderr, - "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n", - path, strerror(errno), - replace_dev_result2string(status_args.result)); + "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m", + path); + if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT) + fprintf(stderr, ", %s\n", + replace_dev_result2string(status_args.result)); + else + fprintf(stderr, "\n"); goto leave_with_error; } if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) { - fprintf(stderr, - "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n", + error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s", path, replace_dev_result2string(status_args.result)); goto leave_with_error; } if (status_args.status.replace_state == BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) { - fprintf(stderr, - "ERROR: btrfs replace on \"%s\" already started!\n", - path); + error("device replace on '%s' already started", path); goto leave_with_error; } srcdev = argv[optind]; - dstdev = argv[optind + 1]; + dstdev = canonicalize_path(argv[optind + 1]); + if (!dstdev) { + error("cannot canonicalize path '%s': %m", + argv[optind + 1]); + goto leave_with_error; + } - if (is_numerical(srcdev)) { + if (string_is_numerical(srcdev)) { struct btrfs_ioctl_fs_info_args fi_args; struct btrfs_ioctl_dev_info_args *di_args = NULL; - if (atoi(srcdev) == 0) { - fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n", - srcdev); - goto leave_with_error; - } - start_args.start.srcdevid = (__u64)atoi(srcdev); + start_args.start.srcdevid = arg_strtou64(srcdev); - ret = get_fs_info(fdmnt, path, &fi_args, &di_args); + ret = get_fs_info(path, &fi_args, &di_args); if (ret) { - fprintf(stderr, "ERROR: getting dev info for devstats failed: " - "%s\n", strerror(-ret)); + error("failed to get device info: %s", strerror(-ret)); free(di_args); goto leave_with_error; } if (!fi_args.num_devices) { - fprintf(stderr, "ERROR: no devices found\n"); + error("no devices found"); free(di_args); goto leave_with_error; } @@ -231,140 +220,118 @@ static int cmd_start_replace(int argc, char **argv) for (i = 0; i < fi_args.num_devices; i++) if (start_args.start.srcdevid == di_args[i].devid) break; + srcdev_size = di_args[i].total_bytes; free(di_args); if (i == fi_args.num_devices) { - fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n", + error("'%s' is not a valid devid for filesystem '%s'", srcdev, path); goto leave_with_error; } - } else { - fdsrcdev = open(srcdev, O_RDWR); - if (fdsrcdev < 0) { - fprintf(stderr, "Error: Unable to open device '%s'\n", - srcdev); - goto leave_with_error; - } - ret = fstat(fdsrcdev, &st); - if (ret) { - fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev); - goto leave_with_error; - } - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "Error: '%s' is not a block device\n", - srcdev); - goto leave_with_error; - } + } else if (is_block_device(srcdev) > 0) { strncpy((char *)start_args.start.srcdev_name, srcdev, BTRFS_DEVICE_PATH_NAME_MAX); - close(fdsrcdev); - fdsrcdev = -1; start_args.start.srcdevid = 0; + srcdev_size = get_partition_size(srcdev); + } else { + error("source device must be a block device or a devid"); + goto leave_with_error; } - ret = check_mounted(dstdev); - if (ret < 0) { - fprintf(stderr, "Error checking %s mount status\n", dstdev); + ret = test_dev_for_mkfs(dstdev, force_using_targetdev); + if (ret) goto leave_with_error; - } - if (ret == 1) { - fprintf(stderr, - "Error, target device %s is in use and currently mounted!\n", - dstdev); + + dstdev_size = get_partition_size(dstdev); + if (srcdev_size > dstdev_size) { + error("target device smaller than source device (required %llu bytes)", + srcdev_size); goto leave_with_error; } + fddstdev = open(dstdev, O_RDWR); if (fddstdev < 0) { - fprintf(stderr, "Unable to open %s\n", dstdev); - goto leave_with_error; - } - ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt, - &total_devs, BTRFS_SUPER_INFO_OFFSET); - if (ret >= 0 && !force_using_targetdev) { - fprintf(stderr, - "Error, target device %s contains filesystem, use '-f' to force overwriting.\n", - dstdev); - goto leave_with_error; - } - ret = fstat(fddstdev, &st); - if (ret) { - fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev); - goto leave_with_error; - } - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "Error: '%s' is not a block device\n", dstdev); + error("unable to open %s: %m", dstdev); goto leave_with_error; } strncpy((char *)start_args.start.tgtdev_name, dstdev, BTRFS_DEVICE_PATH_NAME_MAX); - if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0, - &mixed, 0)) { - fprintf(stderr, "Error: Failed to prepare device '%s'\n", - dstdev); + ret = btrfs_prepare_device(fddstdev, dstdev, &dstdev_block_count, 0, + PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE); + if (ret) goto leave_with_error; - } + close(fddstdev); fddstdev = -1; + free(dstdev); + dstdev = NULL; dev_replace_handle_sigint(fdmnt); if (!do_not_background) { if (daemon(0, 0) < 0) { - fprintf(stderr, "ERROR, backgrounding failed: %s\n", - strerror(errno)); + error("backgrounding failed: %m"); goto leave_with_error; } } start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START; + start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT; ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args); if (do_not_background) { - if (ret) { + if (ret < 0) { fprintf(stderr, - "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n", - path, strerror(errno), - replace_dev_result2string(start_args.result)); + "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %m", + path); + if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT) + fprintf(stderr, ", %s\n", + replace_dev_result2string(start_args.result)); + else + fprintf(stderr, "\n"); + + if (errno == EOPNOTSUPP) + warning("device replace of RAID5/6 not supported with this kernel"); + goto leave_with_error; } if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) { - fprintf(stderr, - "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n", + error("ioctl(DEV_REPLACE_START) on '%s' returns error: %s", path, replace_dev_result2string(start_args.result)); goto leave_with_error; } } - close(fdmnt); + close_file_or_dir(fdmnt, dirstream); return 0; leave_with_error: + if (dstdev) + free(dstdev); if (fdmnt != -1) close(fdmnt); - if (fdsrcdev != -1) - close(fdsrcdev); if (fddstdev != -1) close(fddstdev); - return -1; + return 1; } -static const char *const cmd_status_replace_usage[] = { - "btrfs replace status mount_point [-1]", +static const char *const cmd_replace_status_usage[] = { + "btrfs replace status [-1] ", "Print status and progress information of a running device replace", "operation", "", - "-1 print once instead of print continously until the replace", + "-1 print once instead of print continuously until the replace", " operation finishes (or is canceled)", NULL }; -static int cmd_status_replace(int argc, char **argv) +static int cmd_replace_status(int argc, char **argv) { int fd; - int e; int c; char *path; int once = 0; int ret; + DIR *dirstream = NULL; while ((c = getopt(argc, argv, "1")) != -1) { switch (c) { @@ -373,25 +340,21 @@ static int cmd_status_replace(int argc, char **argv) break; case '?': default: - usage(cmd_status_replace_usage); + usage(cmd_replace_status_usage); } } if (check_argc_exact(argc - optind, 1)) - usage(cmd_status_replace_usage); + usage(cmd_replace_status_usage); path = argv[optind]; - fd = open_file_or_dir(path); - e = errno; - if (fd < 0) { - fprintf(stderr, "ERROR: can't access \"%s\": %s\n", - path, strerror(e)); - return -1; - } + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + return 1; ret = print_replace_status(fd, path, once); - close(fd); - return ret; + close_file_or_dir(fd, dirstream); + return !!ret; } static int print_replace_status(int fd, const char *path, int once) @@ -408,22 +371,28 @@ static int print_replace_status(int fd, const char *path, int once) for (;;) { args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS; + args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT; ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args); - if (ret) { - fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n", - path, strerror(errno), - replace_dev_result2string(args.result)); + if (ret < 0) { + fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m", + path); + if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT) + fprintf(stderr, ", %s\n", + replace_dev_result2string(args.result)); + else + fprintf(stderr, "\n"); return ret; } - status = &args.status; if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) { - fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n", + error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s", path, replace_dev_result2string(args.result)); return -1; } + status = &args.status; + skip_stats = 0; num_chars = 0; switch (status->replace_state) { @@ -468,9 +437,9 @@ static int print_replace_status(int fd, const char *path, int once) printf("Never started"); break; default: - prevent_loop = 1; - assert(0); - break; + error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu", + path, status->replace_state); + return -EINVAL; } if (!skip_stats) @@ -481,7 +450,7 @@ static int print_replace_status(int fd, const char *path, int once) status->num_uncorrectable_read_errors); if (once || prevent_loop) { printf("\n"); - return 0; + break; } fflush(stdout); @@ -517,63 +486,71 @@ progress2string(char *buf, size_t s, int progress_1000) return buf; } -static const char *const cmd_cancel_replace_usage[] = { - "btrfs replace cancel mount_point", +static const char *const cmd_replace_cancel_usage[] = { + "btrfs replace cancel ", "Cancel a running device replace operation.", NULL }; -static int cmd_cancel_replace(int argc, char **argv) +static int cmd_replace_cancel(int argc, char **argv) { struct btrfs_ioctl_dev_replace_args args = {0}; int ret; int c; int fd; - int e; char *path; + DIR *dirstream = NULL; while ((c = getopt(argc, argv, "")) != -1) { switch (c) { case '?': default: - usage(cmd_cancel_replace_usage); + usage(cmd_replace_cancel_usage); } } if (check_argc_exact(argc - optind, 1)) - usage(cmd_cancel_replace_usage); + usage(cmd_replace_cancel_usage); path = argv[optind]; - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access \"%s\": %s\n", - path, strerror(errno)); - return -1; - } + fd = btrfs_open_dir(path, &dirstream, 1); + if (fd < 0) + return 1; args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL; + args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT; ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args); - e = errno; - close(fd); - if (ret) { - fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n", - path, strerror(e), - replace_dev_result2string(args.result)); - return ret; + close_file_or_dir(fd, dirstream); + if (ret < 0) { + fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %m", + path); + if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT) + fprintf(stderr, ", %s\n", + replace_dev_result2string(args.result)); + else + fprintf(stderr, "\n"); + return 1; + } + if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) { + printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n", + path, replace_dev_result2string(args.result)); + return 2; } - return 0; } +static const char replace_cmd_group_info[] = +"replace a device in the filesystem"; + const struct cmd_group replace_cmd_group = { - replace_cmd_group_usage, NULL, { - { "start", cmd_start_replace, cmd_start_replace_usage, NULL, + replace_cmd_group_usage, replace_cmd_group_info, { + { "start", cmd_replace_start, cmd_replace_start_usage, NULL, 0 }, - { "status", cmd_status_replace, cmd_status_replace_usage, NULL, + { "status", cmd_replace_status, cmd_replace_status_usage, NULL, 0 }, - { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL, + { "cancel", cmd_replace_cancel, cmd_replace_cancel_usage, NULL, 0 }, - { 0, 0, 0, 0, 0 } + NULL_CMD_STRUCT } };