2 * Copyright (C) 2012 STRATO. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
24 #include <sys/ioctl.h>
32 #include "kerncompat.h"
42 static int print_replace_status(int fd, const char *path, int once);
43 static char *time2string(char *buf, size_t s, __u64 t);
44 static char *progress2string(char *buf, size_t s, int progress_1000);
47 static const char *replace_dev_result2string(__u64 result)
50 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
52 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
54 case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
55 return "already started";
57 return "<illegal result value>";
61 static const char * const replace_cmd_group_usage[] = {
62 "btrfs replace <command> [<args>]",
66 static int is_numerical(const char *str)
68 if (!(*str >= '0' && *str <= '9'))
70 while (*str >= '0' && *str <= '9')
77 static int dev_replace_cancel_fd = -1;
78 static void dev_replace_sigint_handler(int signal)
81 struct btrfs_ioctl_dev_replace_args args = {0};
83 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
84 ret = ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
86 perror("Device replace cancel failed");
89 static int dev_replace_handle_sigint(int fd)
91 struct sigaction sa = {
92 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
95 dev_replace_cancel_fd = fd;
96 return sigaction(SIGINT, &sa, NULL);
99 static const char *const cmd_start_replace_usage[] = {
100 "btrfs replace start [-Bfr] <srcdev>|<devid> <targetdev> <mount_point>",
101 "Replace device of a btrfs filesystem.",
102 "On a live filesystem, duplicate the data to the target device which",
103 "is currently stored on the source device. If the source device is not",
104 "available anymore, or if the -r option is set, the data is built",
105 "only using the RAID redundancy mechanisms. After completion of the",
106 "operation, the source device is removed from the filesystem.",
107 "If the <srcdev> is a numerical value, it is assumed to be the device id",
108 "of the filesystem which is mounted at <mount_point>, otherwise it is",
109 "the path to the source device. If the source device is disconnected,",
110 "from the system, you have to use the <devid> parameter format.",
111 "The <targetdev> needs to be same size or larger than the <srcdev>.",
113 "-r only read from <srcdev> if no other zero-defect mirror exists",
114 " (enable this if your drive has lots of read errors, the access",
115 " would be very slow)",
116 "-f force using and overwriting <targetdev> even if it looks like",
117 " containing a valid btrfs filesystem. A valid filesystem is",
118 " assumed if a btrfs superblock is found which contains a",
119 " correct checksum. Devices which are currently mounted are",
120 " never allowed to be used as the <targetdev>",
121 "-B do not background",
125 static int cmd_start_replace(int argc, char **argv)
127 struct btrfs_ioctl_dev_replace_args start_args = {0};
128 struct btrfs_ioctl_dev_replace_args status_args = {0};
138 int avoid_reading_from_srcdev = 0;
139 int force_using_targetdev = 0;
141 u64 dstdev_block_count;
142 int do_not_background = 0;
144 DIR *dirstream = NULL;
145 char estr[100]; /* check test_dev_for_mkfs() for error string size*/
147 while ((c = getopt(argc, argv, "Brf")) != -1) {
150 do_not_background = 1;
153 avoid_reading_from_srcdev = 1;
156 force_using_targetdev = 1;
160 usage(cmd_start_replace_usage);
164 start_args.start.cont_reading_from_srcdev_mode =
165 avoid_reading_from_srcdev ?
166 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
167 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
168 if (check_argc_exact(argc - optind, 3))
169 usage(cmd_start_replace_usage);
170 path = argv[optind + 2];
172 fdmnt = open_path_or_dev_mnt(path, &dirstream);
175 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
176 path, strerror(errno));
177 goto leave_with_error;
180 /* check for possible errors before backgrounding */
181 status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
182 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
185 "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
186 path, strerror(errno),
187 replace_dev_result2string(status_args.result));
188 goto leave_with_error;
191 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
193 "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
194 path, replace_dev_result2string(status_args.result));
195 goto leave_with_error;
198 if (status_args.status.replace_state ==
199 BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
201 "ERROR: btrfs replace on \"%s\" already started!\n",
203 goto leave_with_error;
206 srcdev = argv[optind];
207 dstdev = argv[optind + 1];
209 if (is_numerical(srcdev)) {
210 struct btrfs_ioctl_fs_info_args fi_args;
211 struct btrfs_ioctl_dev_info_args *di_args = NULL;
213 start_args.start.srcdevid = arg_strtou64(srcdev);
215 ret = get_fs_info(path, &fi_args, &di_args);
217 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
218 "%s\n", strerror(-ret));
220 goto leave_with_error;
222 if (!fi_args.num_devices) {
223 fprintf(stderr, "ERROR: no devices found\n");
225 goto leave_with_error;
228 for (i = 0; i < fi_args.num_devices; i++)
229 if (start_args.start.srcdevid == di_args[i].devid)
232 if (i == fi_args.num_devices) {
233 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
235 goto leave_with_error;
238 fdsrcdev = open(srcdev, O_RDWR);
240 fprintf(stderr, "Error: Unable to open device '%s'\n",
242 fprintf(stderr, "\tTry using the devid instead of the path\n");
243 goto leave_with_error;
245 ret = fstat(fdsrcdev, &st);
247 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
248 goto leave_with_error;
250 if (!S_ISBLK(st.st_mode)) {
251 fprintf(stderr, "Error: '%s' is not a block device\n",
253 goto leave_with_error;
255 strncpy((char *)start_args.start.srcdev_name, srcdev,
256 BTRFS_DEVICE_PATH_NAME_MAX);
259 start_args.start.srcdevid = 0;
262 ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
264 fprintf(stderr, "%s", estr);
265 goto leave_with_error;
267 fddstdev = open(dstdev, O_RDWR);
269 fprintf(stderr, "Unable to open %s\n", dstdev);
270 goto leave_with_error;
272 strncpy((char *)start_args.start.tgtdev_name, dstdev,
273 BTRFS_DEVICE_PATH_NAME_MAX);
274 ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
277 goto leave_with_error;
282 dev_replace_handle_sigint(fdmnt);
283 if (!do_not_background) {
284 if (daemon(0, 0) < 0) {
285 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
287 goto leave_with_error;
291 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
292 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
293 if (do_not_background) {
296 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
297 path, strerror(errno),
298 replace_dev_result2string(start_args.result));
299 goto leave_with_error;
302 if (start_args.result !=
303 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
305 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
307 replace_dev_result2string(start_args.result));
308 goto leave_with_error;
311 close_file_or_dir(fdmnt, dirstream);
324 static const char *const cmd_status_replace_usage[] = {
325 "btrfs replace status [-1] <mount_point>",
326 "Print status and progress information of a running device replace",
329 "-1 print once instead of print continuously until the replace",
330 " operation finishes (or is canceled)",
334 static int cmd_status_replace(int argc, char **argv)
342 DIR *dirstream = NULL;
344 while ((c = getopt(argc, argv, "1")) != -1) {
351 usage(cmd_status_replace_usage);
355 if (check_argc_exact(argc - optind, 1))
356 usage(cmd_status_replace_usage);
359 fd = open_file_or_dir(path, &dirstream);
362 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
367 ret = print_replace_status(fd, path, once);
368 close_file_or_dir(fd, dirstream);
372 static int print_replace_status(int fd, const char *path, int once)
374 struct btrfs_ioctl_dev_replace_args args = {0};
375 struct btrfs_ioctl_dev_replace_status_params *status;
377 int prevent_loop = 0;
385 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
386 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
388 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
389 path, strerror(errno),
390 replace_dev_result2string(args.result));
394 status = &args.status;
395 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
396 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
398 replace_dev_result2string(args.result));
404 switch (status->replace_state) {
405 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
408 progress2string(string3,
410 status->progress_1000));
412 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
414 printf("Started on %s, finished on %s",
415 time2string(string1, sizeof(string1),
416 status->time_started),
417 time2string(string2, sizeof(string2),
418 status->time_stopped));
420 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
422 printf("Started on %s, canceled on %s at %s",
423 time2string(string1, sizeof(string1),
424 status->time_started),
425 time2string(string2, sizeof(string2),
426 status->time_stopped),
427 progress2string(string3, sizeof(string3),
428 status->progress_1000));
430 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
432 printf("Started on %s, suspended on %s at %s",
433 time2string(string1, sizeof(string1),
434 status->time_started),
435 time2string(string2, sizeof(string2),
436 status->time_stopped),
437 progress2string(string3, sizeof(string3),
438 status->progress_1000));
440 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
443 printf("Never started");
453 ", %llu write errs, %llu uncorr. read errs",
454 (unsigned long long)status->num_write_errors,
456 status->num_uncorrectable_read_errors);
457 if (once || prevent_loop) {
464 while (num_chars > 0) {
474 time2string(char *buf, size_t s, __u64 t)
479 t_time_t = (time_t)t;
480 assert((__u64)t_time_t == t);
481 localtime_r(&t_time_t, &t_tm);
482 strftime(buf, s, "%e.%b %T", &t_tm);
487 progress2string(char *buf, size_t s, int progress_1000)
489 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
495 static const char *const cmd_cancel_replace_usage[] = {
496 "btrfs replace cancel <mount_point>",
497 "Cancel a running device replace operation.",
501 static int cmd_cancel_replace(int argc, char **argv)
503 struct btrfs_ioctl_dev_replace_args args = {0};
509 DIR *dirstream = NULL;
511 while ((c = getopt(argc, argv, "")) != -1) {
515 usage(cmd_cancel_replace_usage);
519 if (check_argc_exact(argc - optind, 1))
520 usage(cmd_cancel_replace_usage);
523 fd = open_file_or_dir(path, &dirstream);
525 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
526 path, strerror(errno));
530 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
531 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
533 close_file_or_dir(fd, dirstream);
535 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
537 replace_dev_result2string(args.result));
540 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
541 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
542 path, replace_dev_result2string(args.result));
548 const struct cmd_group replace_cmd_group = {
549 replace_cmd_group_usage, NULL, {
550 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
552 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
554 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
560 int cmd_replace(int argc, char **argv)
562 return handle_command_group(&replace_cmd_group, argc, argv);