btrfs-progs: import ulist
[btrfs-progs-unstable/devel.git] / cmds-replace.c
blob9eb981ba175a8aeb83b45464777359bb8ddf8421
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 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 = argv[optind + 1];
214 if (is_numerical(srcdev)) {
215 struct btrfs_ioctl_fs_info_args fi_args;
216 struct btrfs_ioctl_dev_info_args *di_args = NULL;
218 start_args.start.srcdevid = arg_strtou64(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 ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
280 &mixed, 0);
281 if (ret)
282 goto leave_with_error;
284 close(fddstdev);
285 fddstdev = -1;
287 dev_replace_handle_sigint(fdmnt);
288 if (!do_not_background) {
289 if (daemon(0, 0) < 0) {
290 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
291 strerror(errno));
292 goto leave_with_error;
296 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
297 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
298 if (do_not_background) {
299 if (ret) {
300 fprintf(stderr,
301 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
302 path, strerror(errno),
303 replace_dev_result2string(start_args.result));
304 goto leave_with_error;
307 if (start_args.result !=
308 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
309 fprintf(stderr,
310 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
311 path,
312 replace_dev_result2string(start_args.result));
313 goto leave_with_error;
316 close_file_or_dir(fdmnt, dirstream);
317 return 0;
319 leave_with_error:
320 if (fdmnt != -1)
321 close(fdmnt);
322 if (fdsrcdev != -1)
323 close(fdsrcdev);
324 if (fddstdev != -1)
325 close(fddstdev);
326 return 1;
329 static const char *const cmd_status_replace_usage[] = {
330 "btrfs replace status [-1] <mount_point>",
331 "Print status and progress information of a running device replace",
332 "operation",
334 "-1 print once instead of print continuously until the replace",
335 " operation finishes (or is canceled)",
336 NULL
339 static int cmd_status_replace(int argc, char **argv)
341 int fd;
342 int e;
343 int c;
344 char *path;
345 int once = 0;
346 int ret;
347 DIR *dirstream = NULL;
349 while ((c = getopt(argc, argv, "1")) != -1) {
350 switch (c) {
351 case '1':
352 once = 1;
353 break;
354 case '?':
355 default:
356 usage(cmd_status_replace_usage);
360 if (check_argc_exact(argc - optind, 1))
361 usage(cmd_status_replace_usage);
363 path = argv[optind];
364 fd = open_file_or_dir(path, &dirstream);
365 e = errno;
366 if (fd < 0) {
367 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
368 path, strerror(e));
369 return 1;
372 ret = print_replace_status(fd, path, once);
373 close_file_or_dir(fd, dirstream);
374 return !!ret;
377 static int print_replace_status(int fd, const char *path, int once)
379 struct btrfs_ioctl_dev_replace_args args = {0};
380 struct btrfs_ioctl_dev_replace_status_params *status;
381 int ret;
382 int prevent_loop = 0;
383 int skip_stats;
384 int num_chars;
385 char string1[80];
386 char string2[80];
387 char string3[80];
389 for (;;) {
390 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
391 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
392 if (ret) {
393 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
394 path, strerror(errno),
395 replace_dev_result2string(args.result));
396 return ret;
399 status = &args.status;
400 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
401 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
402 path,
403 replace_dev_result2string(args.result));
404 return -1;
407 skip_stats = 0;
408 num_chars = 0;
409 switch (status->replace_state) {
410 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
411 num_chars =
412 printf("%s done",
413 progress2string(string3,
414 sizeof(string3),
415 status->progress_1000));
416 break;
417 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
418 prevent_loop = 1;
419 printf("Started on %s, finished on %s",
420 time2string(string1, sizeof(string1),
421 status->time_started),
422 time2string(string2, sizeof(string2),
423 status->time_stopped));
424 break;
425 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
426 prevent_loop = 1;
427 printf("Started on %s, canceled on %s at %s",
428 time2string(string1, sizeof(string1),
429 status->time_started),
430 time2string(string2, sizeof(string2),
431 status->time_stopped),
432 progress2string(string3, sizeof(string3),
433 status->progress_1000));
434 break;
435 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
436 prevent_loop = 1;
437 printf("Started on %s, suspended on %s at %s",
438 time2string(string1, sizeof(string1),
439 status->time_started),
440 time2string(string2, sizeof(string2),
441 status->time_stopped),
442 progress2string(string3, sizeof(string3),
443 status->progress_1000));
444 break;
445 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
446 prevent_loop = 1;
447 skip_stats = 1;
448 printf("Never started");
449 break;
450 default:
451 prevent_loop = 1;
452 fprintf(stderr,
453 "Unknown btrfs dev replace status:%llu",
454 status->replace_state);
455 ret = -EINVAL;
456 break;
459 if (!skip_stats)
460 num_chars += printf(
461 ", %llu write errs, %llu uncorr. read errs",
462 (unsigned long long)status->num_write_errors,
463 (unsigned long long)
464 status->num_uncorrectable_read_errors);
465 if (once || prevent_loop || ret) {
466 printf("\n");
467 return ret;
470 fflush(stdout);
471 sleep(1);
472 while (num_chars > 0) {
473 putchar('\b');
474 num_chars--;
478 return 0;
481 static char *
482 time2string(char *buf, size_t s, __u64 t)
484 struct tm t_tm;
485 time_t t_time_t;
487 t_time_t = (time_t)t;
488 assert((__u64)t_time_t == t);
489 localtime_r(&t_time_t, &t_tm);
490 strftime(buf, s, "%e.%b %T", &t_tm);
491 return buf;
494 static char *
495 progress2string(char *buf, size_t s, int progress_1000)
497 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
498 assert(s > 0);
499 buf[s - 1] = '\0';
500 return buf;
503 static const char *const cmd_cancel_replace_usage[] = {
504 "btrfs replace cancel <mount_point>",
505 "Cancel a running device replace operation.",
506 NULL
509 static int cmd_cancel_replace(int argc, char **argv)
511 struct btrfs_ioctl_dev_replace_args args = {0};
512 int ret;
513 int c;
514 int fd;
515 int e;
516 char *path;
517 DIR *dirstream = NULL;
519 while ((c = getopt(argc, argv, "")) != -1) {
520 switch (c) {
521 case '?':
522 default:
523 usage(cmd_cancel_replace_usage);
527 if (check_argc_exact(argc - optind, 1))
528 usage(cmd_cancel_replace_usage);
530 path = argv[optind];
531 fd = open_file_or_dir(path, &dirstream);
532 if (fd < 0) {
533 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
534 path, strerror(errno));
535 return 1;
538 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
539 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
540 e = errno;
541 close_file_or_dir(fd, dirstream);
542 if (ret) {
543 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
544 path, strerror(e),
545 replace_dev_result2string(args.result));
546 return 1;
548 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
549 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
550 path, replace_dev_result2string(args.result));
551 return 2;
553 return 0;
556 const struct cmd_group replace_cmd_group = {
557 replace_cmd_group_usage, NULL, {
558 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
559 0 },
560 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
561 0 },
562 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
563 0 },
564 NULL_CMD_STRUCT
568 int cmd_replace(int argc, char **argv)
570 return handle_command_group(&replace_cmd_group, argc, argv);