Btrfs-progs: add option to skip whether a scrub has started/resumed in userspace
[btrfs-progs-unstable/devel.git] / cmds-replace.c
blobd9b0940edbbdccae7ef798942c7551087a3b0f1d
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;
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 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);
183 if (ret) {
184 fprintf(stderr,
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) {
192 fprintf(stderr,
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) {
200 fprintf(stderr,
201 "ERROR: btrfs replace on \"%s\" already started!\n",
202 path);
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 if (atoi(srcdev) == 0) {
214 fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
215 srcdev);
216 goto leave_with_error;
218 start_args.start.srcdevid = (__u64)atoi(srcdev);
220 ret = get_fs_info(path, &fi_args, &di_args);
221 if (ret) {
222 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
223 "%s\n", strerror(-ret));
224 free(di_args);
225 goto leave_with_error;
227 if (!fi_args.num_devices) {
228 fprintf(stderr, "ERROR: no devices found\n");
229 free(di_args);
230 goto leave_with_error;
233 for (i = 0; i < fi_args.num_devices; i++)
234 if (start_args.start.srcdevid == di_args[i].devid)
235 break;
236 free(di_args);
237 if (i == fi_args.num_devices) {
238 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
239 srcdev, path);
240 goto leave_with_error;
242 } else {
243 fdsrcdev = open(srcdev, O_RDWR);
244 if (fdsrcdev < 0) {
245 fprintf(stderr, "Error: Unable to open device '%s'\n",
246 srcdev);
247 fprintf(stderr, "\tTry using the devid instead of the path\n");
248 goto leave_with_error;
250 ret = fstat(fdsrcdev, &st);
251 if (ret) {
252 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
253 goto leave_with_error;
255 if (!S_ISBLK(st.st_mode)) {
256 fprintf(stderr, "Error: '%s' is not a block device\n",
257 srcdev);
258 goto leave_with_error;
260 strncpy((char *)start_args.start.srcdev_name, srcdev,
261 BTRFS_DEVICE_PATH_NAME_MAX);
262 close(fdsrcdev);
263 fdsrcdev = -1;
264 start_args.start.srcdevid = 0;
267 ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
268 if (ret) {
269 fprintf(stderr, "%s", estr);
270 goto leave_with_error;
272 fddstdev = open(dstdev, O_RDWR);
273 if (fddstdev < 0) {
274 fprintf(stderr, "Unable to open %s\n", dstdev);
275 goto leave_with_error;
277 strncpy((char *)start_args.start.tgtdev_name, dstdev,
278 BTRFS_DEVICE_PATH_NAME_MAX);
279 if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
280 &mixed, 0)) {
281 fprintf(stderr, "Error: Failed to prepare device '%s'\n",
282 dstdev);
283 goto leave_with_error;
285 close(fddstdev);
286 fddstdev = -1;
288 dev_replace_handle_sigint(fdmnt);
289 if (!do_not_background) {
290 if (daemon(0, 0) < 0) {
291 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
292 strerror(errno));
293 goto leave_with_error;
297 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
298 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
299 if (do_not_background) {
300 if (ret) {
301 fprintf(stderr,
302 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
303 path, strerror(errno),
304 replace_dev_result2string(start_args.result));
305 goto leave_with_error;
308 if (start_args.result !=
309 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
310 fprintf(stderr,
311 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
312 path,
313 replace_dev_result2string(start_args.result));
314 goto leave_with_error;
317 close_file_or_dir(fdmnt, dirstream);
318 return 0;
320 leave_with_error:
321 if (fdmnt != -1)
322 close(fdmnt);
323 if (fdsrcdev != -1)
324 close(fdsrcdev);
325 if (fddstdev != -1)
326 close(fddstdev);
327 return 1;
330 static const char *const cmd_status_replace_usage[] = {
331 "btrfs replace status [-1] <mount_point>",
332 "Print status and progress information of a running device replace",
333 "operation",
335 "-1 print once instead of print continuously until the replace",
336 " operation finishes (or is canceled)",
337 NULL
340 static int cmd_status_replace(int argc, char **argv)
342 int fd;
343 int e;
344 int c;
345 char *path;
346 int once = 0;
347 int ret;
348 DIR *dirstream = NULL;
350 while ((c = getopt(argc, argv, "1")) != -1) {
351 switch (c) {
352 case '1':
353 once = 1;
354 break;
355 case '?':
356 default:
357 usage(cmd_status_replace_usage);
361 if (check_argc_exact(argc - optind, 1))
362 usage(cmd_status_replace_usage);
364 path = argv[optind];
365 fd = open_file_or_dir(path, &dirstream);
366 e = errno;
367 if (fd < 0) {
368 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
369 path, strerror(e));
370 return 1;
373 ret = print_replace_status(fd, path, once);
374 close_file_or_dir(fd, dirstream);
375 return !!ret;
378 static int print_replace_status(int fd, const char *path, int once)
380 struct btrfs_ioctl_dev_replace_args args = {0};
381 struct btrfs_ioctl_dev_replace_status_params *status;
382 int ret;
383 int prevent_loop = 0;
384 int skip_stats;
385 int num_chars;
386 char string1[80];
387 char string2[80];
388 char string3[80];
390 for (;;) {
391 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
392 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
393 if (ret) {
394 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
395 path, strerror(errno),
396 replace_dev_result2string(args.result));
397 return ret;
400 status = &args.status;
401 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
402 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
403 path,
404 replace_dev_result2string(args.result));
405 return -1;
408 skip_stats = 0;
409 num_chars = 0;
410 switch (status->replace_state) {
411 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
412 num_chars =
413 printf("%s done",
414 progress2string(string3,
415 sizeof(string3),
416 status->progress_1000));
417 break;
418 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
419 prevent_loop = 1;
420 printf("Started on %s, finished on %s",
421 time2string(string1, sizeof(string1),
422 status->time_started),
423 time2string(string2, sizeof(string2),
424 status->time_stopped));
425 break;
426 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
427 prevent_loop = 1;
428 printf("Started on %s, canceled on %s at %s",
429 time2string(string1, sizeof(string1),
430 status->time_started),
431 time2string(string2, sizeof(string2),
432 status->time_stopped),
433 progress2string(string3, sizeof(string3),
434 status->progress_1000));
435 break;
436 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
437 prevent_loop = 1;
438 printf("Started on %s, suspended on %s at %s",
439 time2string(string1, sizeof(string1),
440 status->time_started),
441 time2string(string2, sizeof(string2),
442 status->time_stopped),
443 progress2string(string3, sizeof(string3),
444 status->progress_1000));
445 break;
446 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
447 prevent_loop = 1;
448 skip_stats = 1;
449 printf("Never started");
450 break;
451 default:
452 prevent_loop = 1;
453 assert(0);
454 break;
457 if (!skip_stats)
458 num_chars += printf(
459 ", %llu write errs, %llu uncorr. read errs",
460 (unsigned long long)status->num_write_errors,
461 (unsigned long long)
462 status->num_uncorrectable_read_errors);
463 if (once || prevent_loop) {
464 printf("\n");
465 return 0;
468 fflush(stdout);
469 sleep(1);
470 while (num_chars > 0) {
471 putchar('\b');
472 num_chars--;
476 return 0;
479 static char *
480 time2string(char *buf, size_t s, __u64 t)
482 struct tm t_tm;
483 time_t t_time_t;
485 t_time_t = (time_t)t;
486 assert((__u64)t_time_t == t);
487 localtime_r(&t_time_t, &t_tm);
488 strftime(buf, s, "%e.%b %T", &t_tm);
489 return buf;
492 static char *
493 progress2string(char *buf, size_t s, int progress_1000)
495 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
496 assert(s > 0);
497 buf[s - 1] = '\0';
498 return buf;
501 static const char *const cmd_cancel_replace_usage[] = {
502 "btrfs replace cancel <mount_point>",
503 "Cancel a running device replace operation.",
504 NULL
507 static int cmd_cancel_replace(int argc, char **argv)
509 struct btrfs_ioctl_dev_replace_args args = {0};
510 int ret;
511 int c;
512 int fd;
513 int e;
514 char *path;
515 DIR *dirstream = NULL;
517 while ((c = getopt(argc, argv, "")) != -1) {
518 switch (c) {
519 case '?':
520 default:
521 usage(cmd_cancel_replace_usage);
525 if (check_argc_exact(argc - optind, 1))
526 usage(cmd_cancel_replace_usage);
528 path = argv[optind];
529 fd = open_file_or_dir(path, &dirstream);
530 if (fd < 0) {
531 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
532 path, strerror(errno));
533 return 1;
536 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
537 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
538 e = errno;
539 close_file_or_dir(fd, dirstream);
540 if (ret) {
541 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
542 path, strerror(e),
543 replace_dev_result2string(args.result));
544 return 1;
546 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
547 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
548 path, replace_dev_result2string(args.result));
549 return 2;
551 return 0;
554 const struct cmd_group replace_cmd_group = {
555 replace_cmd_group_usage, NULL, {
556 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
557 0 },
558 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
559 0 },
560 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
561 0 },
562 NULL_CMD_STRUCT
566 int cmd_replace(int argc, char **argv)
568 return handle_command_group(&replace_cmd_group, argc, argv);