btrfs-progs: fix csum root copy-n-paste error
[btrfs-progs-unstable/devel.git] / cmds-replace.c
blobf7cb0b236aeb6fff9d870c5d9a15210aee8d37a2
1 /*
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.
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27 #include <time.h>
28 #include <assert.h>
29 #include <inttypes.h>
30 #include <sys/wait.h>
32 #include "kerncompat.h"
33 #include "ctree.h"
34 #include "ioctl.h"
35 #include "utils.h"
36 #include "volumes.h"
37 #include "disk-io.h"
39 #include "commands.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)
49 switch (result) {
50 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
51 return "no error";
52 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
53 return "not started";
54 case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
55 return "already started";
56 default:
57 return "<illegal result value>";
61 static const char * const replace_cmd_group_usage[] = {
62 "btrfs replace <command> [<args>]",
63 NULL
66 static int is_numerical(const char *str)
68 if (!(*str >= '0' && *str <= '9'))
69 return 0;
70 while (*str >= '0' && *str <= '9')
71 str++;
72 if (*str != '\0')
73 return 0;
74 return 1;
77 static int dev_replace_cancel_fd = -1;
78 static void dev_replace_sigint_handler(int signal)
80 int ret;
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);
85 if (ret < 0)
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",
122 NULL
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};
129 int ret;
130 int i;
131 int c;
132 int fdmnt = -1;
133 int fdsrcdev = -1;
134 int fddstdev = -1;
135 char *path;
136 char *srcdev;
137 char *dstdev = NULL;
138 int avoid_reading_from_srcdev = 0;
139 int force_using_targetdev = 0;
140 struct stat st;
141 u64 dstdev_block_count;
142 int do_not_background = 0;
143 int mixed = 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) {
148 switch (c) {
149 case 'B':
150 do_not_background = 1;
151 break;
152 case 'r':
153 avoid_reading_from_srcdev = 1;
154 break;
155 case 'f':
156 force_using_targetdev = 1;
157 break;
158 case '?':
159 default:
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);
174 if (fdmnt < 0) {
175 if (errno == EINVAL)
176 fprintf(stderr,
177 "ERROR: '%s' is not a mounted btrfs device\n",
178 path);
179 else
180 fprintf(stderr, "ERROR: can't access '%s': %s\n",
181 path, strerror(errno));
182 goto leave_with_error;
185 /* check for possible errors before backgrounding */
186 status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
187 status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
188 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
189 if (ret) {
190 fprintf(stderr,
191 "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
192 path, strerror(errno));
193 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
194 fprintf(stderr, ", %s\n",
195 replace_dev_result2string(status_args.result));
196 else
197 fprintf(stderr, "\n");
198 goto leave_with_error;
201 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
202 fprintf(stderr,
203 "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
204 path, replace_dev_result2string(status_args.result));
205 goto leave_with_error;
208 if (status_args.status.replace_state ==
209 BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
210 fprintf(stderr,
211 "ERROR: btrfs replace on \"%s\" already started!\n",
212 path);
213 goto leave_with_error;
216 srcdev = argv[optind];
217 dstdev = canonicalize_path(argv[optind + 1]);
218 if (!dstdev) {
219 fprintf(stderr,
220 "ERROR: Could not canonicalize path '%s': %s\n",
221 argv[optind + 1], strerror(errno));
222 goto leave_with_error;
225 if (is_numerical(srcdev)) {
226 struct btrfs_ioctl_fs_info_args fi_args;
227 struct btrfs_ioctl_dev_info_args *di_args = NULL;
229 start_args.start.srcdevid = arg_strtou64(srcdev);
231 ret = get_fs_info(path, &fi_args, &di_args);
232 if (ret) {
233 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
234 "%s\n", strerror(-ret));
235 free(di_args);
236 goto leave_with_error;
238 if (!fi_args.num_devices) {
239 fprintf(stderr, "ERROR: no devices found\n");
240 free(di_args);
241 goto leave_with_error;
244 for (i = 0; i < fi_args.num_devices; i++)
245 if (start_args.start.srcdevid == di_args[i].devid)
246 break;
247 free(di_args);
248 if (i == fi_args.num_devices) {
249 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
250 srcdev, path);
251 goto leave_with_error;
253 } else {
254 fdsrcdev = open(srcdev, O_RDWR);
255 if (fdsrcdev < 0) {
256 fprintf(stderr, "Error: Unable to open device '%s'\n",
257 srcdev);
258 fprintf(stderr, "\tTry using the devid instead of the path\n");
259 goto leave_with_error;
261 ret = fstat(fdsrcdev, &st);
262 if (ret) {
263 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
264 goto leave_with_error;
266 if (!S_ISBLK(st.st_mode)) {
267 fprintf(stderr, "Error: '%s' is not a block device\n",
268 srcdev);
269 goto leave_with_error;
271 strncpy((char *)start_args.start.srcdev_name, srcdev,
272 BTRFS_DEVICE_PATH_NAME_MAX);
273 close(fdsrcdev);
274 fdsrcdev = -1;
275 start_args.start.srcdevid = 0;
278 ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
279 if (ret) {
280 fprintf(stderr, "%s", estr);
281 goto leave_with_error;
283 fddstdev = open(dstdev, O_RDWR);
284 if (fddstdev < 0) {
285 fprintf(stderr, "Unable to open %s\n", dstdev);
286 goto leave_with_error;
288 strncpy((char *)start_args.start.tgtdev_name, dstdev,
289 BTRFS_DEVICE_PATH_NAME_MAX);
290 ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
291 &mixed, 0);
292 if (ret)
293 goto leave_with_error;
295 close(fddstdev);
296 fddstdev = -1;
297 free(dstdev);
298 dstdev = NULL;
300 dev_replace_handle_sigint(fdmnt);
301 if (!do_not_background) {
302 if (daemon(0, 0) < 0) {
303 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
304 strerror(errno));
305 goto leave_with_error;
309 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
310 start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
311 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
312 if (do_not_background) {
313 if (ret) {
314 fprintf(stderr,
315 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
316 path, strerror(errno));
317 if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
318 fprintf(stderr, ", %s\n",
319 replace_dev_result2string(start_args.result));
320 else
321 fprintf(stderr, "\n");
323 if (errno == EOPNOTSUPP)
324 fprintf(stderr,
325 "WARNING: dev_replace does not yet handle RAID5/6\n");
327 goto leave_with_error;
330 if (start_args.result !=
331 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
332 fprintf(stderr,
333 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
334 path,
335 replace_dev_result2string(start_args.result));
336 goto leave_with_error;
339 close_file_or_dir(fdmnt, dirstream);
340 return 0;
342 leave_with_error:
343 if (dstdev)
344 free(dstdev);
345 if (fdmnt != -1)
346 close(fdmnt);
347 if (fdsrcdev != -1)
348 close(fdsrcdev);
349 if (fddstdev != -1)
350 close(fddstdev);
351 return 1;
354 static const char *const cmd_status_replace_usage[] = {
355 "btrfs replace status [-1] <mount_point>",
356 "Print status and progress information of a running device replace",
357 "operation",
359 "-1 print once instead of print continuously until the replace",
360 " operation finishes (or is canceled)",
361 NULL
364 static int cmd_status_replace(int argc, char **argv)
366 int fd;
367 int e;
368 int c;
369 char *path;
370 int once = 0;
371 int ret;
372 DIR *dirstream = NULL;
374 while ((c = getopt(argc, argv, "1")) != -1) {
375 switch (c) {
376 case '1':
377 once = 1;
378 break;
379 case '?':
380 default:
381 usage(cmd_status_replace_usage);
385 if (check_argc_exact(argc - optind, 1))
386 usage(cmd_status_replace_usage);
388 path = argv[optind];
389 fd = open_file_or_dir(path, &dirstream);
390 e = errno;
391 if (fd < 0) {
392 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
393 path, strerror(e));
394 return 1;
397 ret = print_replace_status(fd, path, once);
398 close_file_or_dir(fd, dirstream);
399 return !!ret;
402 static int print_replace_status(int fd, const char *path, int once)
404 struct btrfs_ioctl_dev_replace_args args = {0};
405 struct btrfs_ioctl_dev_replace_status_params *status;
406 int ret;
407 int prevent_loop = 0;
408 int skip_stats;
409 int num_chars;
410 char string1[80];
411 char string2[80];
412 char string3[80];
414 for (;;) {
415 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
416 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
417 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
418 if (ret) {
419 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
420 path, strerror(errno));
421 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
422 fprintf(stderr, ", %s\n",
423 replace_dev_result2string(args.result));
424 else
425 fprintf(stderr, "\n");
426 return ret;
429 status = &args.status;
430 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
431 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
432 path,
433 replace_dev_result2string(args.result));
434 return -1;
437 skip_stats = 0;
438 num_chars = 0;
439 switch (status->replace_state) {
440 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
441 num_chars =
442 printf("%s done",
443 progress2string(string3,
444 sizeof(string3),
445 status->progress_1000));
446 break;
447 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
448 prevent_loop = 1;
449 printf("Started on %s, finished on %s",
450 time2string(string1, sizeof(string1),
451 status->time_started),
452 time2string(string2, sizeof(string2),
453 status->time_stopped));
454 break;
455 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
456 prevent_loop = 1;
457 printf("Started on %s, canceled on %s at %s",
458 time2string(string1, sizeof(string1),
459 status->time_started),
460 time2string(string2, sizeof(string2),
461 status->time_stopped),
462 progress2string(string3, sizeof(string3),
463 status->progress_1000));
464 break;
465 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
466 prevent_loop = 1;
467 printf("Started on %s, suspended on %s at %s",
468 time2string(string1, sizeof(string1),
469 status->time_started),
470 time2string(string2, sizeof(string2),
471 status->time_stopped),
472 progress2string(string3, sizeof(string3),
473 status->progress_1000));
474 break;
475 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
476 prevent_loop = 1;
477 skip_stats = 1;
478 printf("Never started");
479 break;
480 default:
481 prevent_loop = 1;
482 fprintf(stderr,
483 "Unknown btrfs dev replace status:%llu",
484 status->replace_state);
485 ret = -EINVAL;
486 break;
489 if (!skip_stats)
490 num_chars += printf(
491 ", %llu write errs, %llu uncorr. read errs",
492 (unsigned long long)status->num_write_errors,
493 (unsigned long long)
494 status->num_uncorrectable_read_errors);
495 if (once || prevent_loop || ret) {
496 printf("\n");
497 return ret;
500 fflush(stdout);
501 sleep(1);
502 while (num_chars > 0) {
503 putchar('\b');
504 num_chars--;
508 return 0;
511 static char *
512 time2string(char *buf, size_t s, __u64 t)
514 struct tm t_tm;
515 time_t t_time_t;
517 t_time_t = (time_t)t;
518 assert((__u64)t_time_t == t);
519 localtime_r(&t_time_t, &t_tm);
520 strftime(buf, s, "%e.%b %T", &t_tm);
521 return buf;
524 static char *
525 progress2string(char *buf, size_t s, int progress_1000)
527 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
528 assert(s > 0);
529 buf[s - 1] = '\0';
530 return buf;
533 static const char *const cmd_cancel_replace_usage[] = {
534 "btrfs replace cancel <mount_point>",
535 "Cancel a running device replace operation.",
536 NULL
539 static int cmd_cancel_replace(int argc, char **argv)
541 struct btrfs_ioctl_dev_replace_args args = {0};
542 int ret;
543 int c;
544 int fd;
545 int e;
546 char *path;
547 DIR *dirstream = NULL;
549 while ((c = getopt(argc, argv, "")) != -1) {
550 switch (c) {
551 case '?':
552 default:
553 usage(cmd_cancel_replace_usage);
557 if (check_argc_exact(argc - optind, 1))
558 usage(cmd_cancel_replace_usage);
560 path = argv[optind];
561 fd = open_file_or_dir(path, &dirstream);
562 if (fd < 0) {
563 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
564 path, strerror(errno));
565 return 1;
568 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
569 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
570 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
571 e = errno;
572 close_file_or_dir(fd, dirstream);
573 if (ret) {
574 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s",
575 path, strerror(e));
576 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
577 fprintf(stderr, ", %s\n",
578 replace_dev_result2string(args.result));
579 else
580 fprintf(stderr, "\n");
581 return 1;
583 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
584 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
585 path, replace_dev_result2string(args.result));
586 return 2;
588 return 0;
591 const struct cmd_group replace_cmd_group = {
592 replace_cmd_group_usage, NULL, {
593 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
594 0 },
595 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
596 0 },
597 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
598 0 },
599 NULL_CMD_STRUCT
603 int cmd_replace(int argc, char **argv)
605 return handle_command_group(&replace_cmd_group, argc, argv);