btrfs-progs: fix show super unknown flag output
[btrfs-progs-unstable/devel.git] / cmds-replace.c
blob4a4e865884abf790222194da1c180d9664a22087
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 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
188 if (ret) {
189 fprintf(stderr,
190 "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
191 path, strerror(errno),
192 replace_dev_result2string(status_args.result));
193 goto leave_with_error;
196 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
197 fprintf(stderr,
198 "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
199 path, replace_dev_result2string(status_args.result));
200 goto leave_with_error;
203 if (status_args.status.replace_state ==
204 BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
205 fprintf(stderr,
206 "ERROR: btrfs replace on \"%s\" already started!\n",
207 path);
208 goto leave_with_error;
211 srcdev = argv[optind];
212 dstdev = canonicalize_path(argv[optind + 1]);
213 if (!dstdev) {
214 fprintf(stderr,
215 "ERROR: Could not canonicalize path '%s': %s\n",
216 argv[optind + 1], strerror(errno));
217 goto leave_with_error;
220 if (is_numerical(srcdev)) {
221 struct btrfs_ioctl_fs_info_args fi_args;
222 struct btrfs_ioctl_dev_info_args *di_args = NULL;
224 start_args.start.srcdevid = arg_strtou64(srcdev);
226 ret = get_fs_info(path, &fi_args, &di_args);
227 if (ret) {
228 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
229 "%s\n", strerror(-ret));
230 free(di_args);
231 goto leave_with_error;
233 if (!fi_args.num_devices) {
234 fprintf(stderr, "ERROR: no devices found\n");
235 free(di_args);
236 goto leave_with_error;
239 for (i = 0; i < fi_args.num_devices; i++)
240 if (start_args.start.srcdevid == di_args[i].devid)
241 break;
242 free(di_args);
243 if (i == fi_args.num_devices) {
244 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
245 srcdev, path);
246 goto leave_with_error;
248 } else {
249 fdsrcdev = open(srcdev, O_RDWR);
250 if (fdsrcdev < 0) {
251 fprintf(stderr, "Error: Unable to open device '%s'\n",
252 srcdev);
253 fprintf(stderr, "\tTry using the devid instead of the path\n");
254 goto leave_with_error;
256 ret = fstat(fdsrcdev, &st);
257 if (ret) {
258 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
259 goto leave_with_error;
261 if (!S_ISBLK(st.st_mode)) {
262 fprintf(stderr, "Error: '%s' is not a block device\n",
263 srcdev);
264 goto leave_with_error;
266 strncpy((char *)start_args.start.srcdev_name, srcdev,
267 BTRFS_DEVICE_PATH_NAME_MAX);
268 close(fdsrcdev);
269 fdsrcdev = -1;
270 start_args.start.srcdevid = 0;
273 ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
274 if (ret) {
275 fprintf(stderr, "%s", estr);
276 goto leave_with_error;
278 fddstdev = open(dstdev, O_RDWR);
279 if (fddstdev < 0) {
280 fprintf(stderr, "Unable to open %s\n", dstdev);
281 goto leave_with_error;
283 strncpy((char *)start_args.start.tgtdev_name, dstdev,
284 BTRFS_DEVICE_PATH_NAME_MAX);
285 ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
286 &mixed, 0);
287 if (ret)
288 goto leave_with_error;
290 close(fddstdev);
291 fddstdev = -1;
292 free(dstdev);
293 dstdev = NULL;
295 dev_replace_handle_sigint(fdmnt);
296 if (!do_not_background) {
297 if (daemon(0, 0) < 0) {
298 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
299 strerror(errno));
300 goto leave_with_error;
304 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
305 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
306 if (do_not_background) {
307 if (ret) {
308 fprintf(stderr,
309 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
310 path, strerror(errno),
311 replace_dev_result2string(start_args.result));
313 if (errno == EOPNOTSUPP)
314 fprintf(stderr,
315 "WARNING: dev_replace does not yet handle RAID5/6\n");
317 goto leave_with_error;
320 if (start_args.result !=
321 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
322 fprintf(stderr,
323 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
324 path,
325 replace_dev_result2string(start_args.result));
326 goto leave_with_error;
329 close_file_or_dir(fdmnt, dirstream);
330 return 0;
332 leave_with_error:
333 if (dstdev)
334 free(dstdev);
335 if (fdmnt != -1)
336 close(fdmnt);
337 if (fdsrcdev != -1)
338 close(fdsrcdev);
339 if (fddstdev != -1)
340 close(fddstdev);
341 return 1;
344 static const char *const cmd_status_replace_usage[] = {
345 "btrfs replace status [-1] <mount_point>",
346 "Print status and progress information of a running device replace",
347 "operation",
349 "-1 print once instead of print continuously until the replace",
350 " operation finishes (or is canceled)",
351 NULL
354 static int cmd_status_replace(int argc, char **argv)
356 int fd;
357 int e;
358 int c;
359 char *path;
360 int once = 0;
361 int ret;
362 DIR *dirstream = NULL;
364 while ((c = getopt(argc, argv, "1")) != -1) {
365 switch (c) {
366 case '1':
367 once = 1;
368 break;
369 case '?':
370 default:
371 usage(cmd_status_replace_usage);
375 if (check_argc_exact(argc - optind, 1))
376 usage(cmd_status_replace_usage);
378 path = argv[optind];
379 fd = open_file_or_dir(path, &dirstream);
380 e = errno;
381 if (fd < 0) {
382 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
383 path, strerror(e));
384 return 1;
387 ret = print_replace_status(fd, path, once);
388 close_file_or_dir(fd, dirstream);
389 return !!ret;
392 static int print_replace_status(int fd, const char *path, int once)
394 struct btrfs_ioctl_dev_replace_args args = {0};
395 struct btrfs_ioctl_dev_replace_status_params *status;
396 int ret;
397 int prevent_loop = 0;
398 int skip_stats;
399 int num_chars;
400 char string1[80];
401 char string2[80];
402 char string3[80];
404 for (;;) {
405 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
406 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
407 if (ret) {
408 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
409 path, strerror(errno),
410 replace_dev_result2string(args.result));
411 return ret;
414 status = &args.status;
415 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
416 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
417 path,
418 replace_dev_result2string(args.result));
419 return -1;
422 skip_stats = 0;
423 num_chars = 0;
424 switch (status->replace_state) {
425 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
426 num_chars =
427 printf("%s done",
428 progress2string(string3,
429 sizeof(string3),
430 status->progress_1000));
431 break;
432 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
433 prevent_loop = 1;
434 printf("Started on %s, finished on %s",
435 time2string(string1, sizeof(string1),
436 status->time_started),
437 time2string(string2, sizeof(string2),
438 status->time_stopped));
439 break;
440 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
441 prevent_loop = 1;
442 printf("Started on %s, canceled on %s at %s",
443 time2string(string1, sizeof(string1),
444 status->time_started),
445 time2string(string2, sizeof(string2),
446 status->time_stopped),
447 progress2string(string3, sizeof(string3),
448 status->progress_1000));
449 break;
450 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
451 prevent_loop = 1;
452 printf("Started on %s, suspended on %s at %s",
453 time2string(string1, sizeof(string1),
454 status->time_started),
455 time2string(string2, sizeof(string2),
456 status->time_stopped),
457 progress2string(string3, sizeof(string3),
458 status->progress_1000));
459 break;
460 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
461 prevent_loop = 1;
462 skip_stats = 1;
463 printf("Never started");
464 break;
465 default:
466 prevent_loop = 1;
467 fprintf(stderr,
468 "Unknown btrfs dev replace status:%llu",
469 status->replace_state);
470 ret = -EINVAL;
471 break;
474 if (!skip_stats)
475 num_chars += printf(
476 ", %llu write errs, %llu uncorr. read errs",
477 (unsigned long long)status->num_write_errors,
478 (unsigned long long)
479 status->num_uncorrectable_read_errors);
480 if (once || prevent_loop || ret) {
481 printf("\n");
482 return ret;
485 fflush(stdout);
486 sleep(1);
487 while (num_chars > 0) {
488 putchar('\b');
489 num_chars--;
493 return 0;
496 static char *
497 time2string(char *buf, size_t s, __u64 t)
499 struct tm t_tm;
500 time_t t_time_t;
502 t_time_t = (time_t)t;
503 assert((__u64)t_time_t == t);
504 localtime_r(&t_time_t, &t_tm);
505 strftime(buf, s, "%e.%b %T", &t_tm);
506 return buf;
509 static char *
510 progress2string(char *buf, size_t s, int progress_1000)
512 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
513 assert(s > 0);
514 buf[s - 1] = '\0';
515 return buf;
518 static const char *const cmd_cancel_replace_usage[] = {
519 "btrfs replace cancel <mount_point>",
520 "Cancel a running device replace operation.",
521 NULL
524 static int cmd_cancel_replace(int argc, char **argv)
526 struct btrfs_ioctl_dev_replace_args args = {0};
527 int ret;
528 int c;
529 int fd;
530 int e;
531 char *path;
532 DIR *dirstream = NULL;
534 while ((c = getopt(argc, argv, "")) != -1) {
535 switch (c) {
536 case '?':
537 default:
538 usage(cmd_cancel_replace_usage);
542 if (check_argc_exact(argc - optind, 1))
543 usage(cmd_cancel_replace_usage);
545 path = argv[optind];
546 fd = open_file_or_dir(path, &dirstream);
547 if (fd < 0) {
548 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
549 path, strerror(errno));
550 return 1;
553 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
554 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
555 e = errno;
556 close_file_or_dir(fd, dirstream);
557 if (ret) {
558 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
559 path, strerror(e),
560 replace_dev_result2string(args.result));
561 return 1;
563 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
564 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
565 path, replace_dev_result2string(args.result));
566 return 2;
568 return 0;
571 const struct cmd_group replace_cmd_group = {
572 replace_cmd_group_usage, NULL, {
573 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
574 0 },
575 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
576 0 },
577 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
578 0 },
579 NULL_CMD_STRUCT
583 int cmd_replace(int argc, char **argv)
585 return handle_command_group(&replace_cmd_group, argc, argv);