btrfs-progs: Add stack get/set functions for btrfs_qgroup_status_item
[btrfs-progs-unstable/devel.git] / cmds-replace.c
blob63d34f94df842027adee48b9ef4260b9ac104243
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 case BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS:
57 return "scrub is in progress";
58 default:
59 return "<illegal result value>";
63 static const char * const replace_cmd_group_usage[] = {
64 "btrfs replace <command> [<args>]",
65 NULL
68 static int is_numerical(const char *str)
70 if (!(*str >= '0' && *str <= '9'))
71 return 0;
72 while (*str >= '0' && *str <= '9')
73 str++;
74 if (*str != '\0')
75 return 0;
76 return 1;
79 static int dev_replace_cancel_fd = -1;
80 static void dev_replace_sigint_handler(int signal)
82 int ret;
83 struct btrfs_ioctl_dev_replace_args args = {0};
85 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
86 ret = ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
87 if (ret < 0)
88 perror("Device replace cancel failed");
91 static int dev_replace_handle_sigint(int fd)
93 struct sigaction sa = {
94 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
97 dev_replace_cancel_fd = fd;
98 return sigaction(SIGINT, &sa, NULL);
101 static const char *const cmd_start_replace_usage[] = {
102 "btrfs replace start [-Bfr] <srcdev>|<devid> <targetdev> <mount_point>",
103 "Replace device of a btrfs filesystem.",
104 "On a live filesystem, duplicate the data to the target device which",
105 "is currently stored on the source device. If the source device is not",
106 "available anymore, or if the -r option is set, the data is built",
107 "only using the RAID redundancy mechanisms. After completion of the",
108 "operation, the source device is removed from the filesystem.",
109 "If the <srcdev> is a numerical value, it is assumed to be the device id",
110 "of the filesystem which is mounted at <mount_point>, otherwise it is",
111 "the path to the source device. If the source device is disconnected,",
112 "from the system, you have to use the <devid> parameter format.",
113 "The <targetdev> needs to be same size or larger than the <srcdev>.",
115 "-r only read from <srcdev> if no other zero-defect mirror exists",
116 " (enable this if your drive has lots of read errors, the access",
117 " would be very slow)",
118 "-f force using and overwriting <targetdev> even if it looks like",
119 " containing a valid btrfs filesystem. A valid filesystem is",
120 " assumed if a btrfs superblock is found which contains a",
121 " correct checksum. Devices which are currently mounted are",
122 " never allowed to be used as the <targetdev>",
123 "-B do not background",
124 NULL
127 static int cmd_start_replace(int argc, char **argv)
129 struct btrfs_ioctl_dev_replace_args start_args = {0};
130 struct btrfs_ioctl_dev_replace_args status_args = {0};
131 int ret;
132 int i;
133 int c;
134 int fdmnt = -1;
135 int fdsrcdev = -1;
136 int fddstdev = -1;
137 char *path;
138 char *srcdev;
139 char *dstdev = NULL;
140 int avoid_reading_from_srcdev = 0;
141 int force_using_targetdev = 0;
142 struct stat st;
143 u64 dstdev_block_count;
144 int do_not_background = 0;
145 int mixed = 0;
146 DIR *dirstream = NULL;
147 char estr[100]; /* check test_dev_for_mkfs() for error string size*/
149 while ((c = getopt(argc, argv, "Brf")) != -1) {
150 switch (c) {
151 case 'B':
152 do_not_background = 1;
153 break;
154 case 'r':
155 avoid_reading_from_srcdev = 1;
156 break;
157 case 'f':
158 force_using_targetdev = 1;
159 break;
160 case '?':
161 default:
162 usage(cmd_start_replace_usage);
166 start_args.start.cont_reading_from_srcdev_mode =
167 avoid_reading_from_srcdev ?
168 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
169 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
170 if (check_argc_exact(argc - optind, 3))
171 usage(cmd_start_replace_usage);
172 path = argv[optind + 2];
174 fdmnt = open_path_or_dev_mnt(path, &dirstream);
176 if (fdmnt < 0) {
177 if (errno == EINVAL)
178 fprintf(stderr,
179 "ERROR: '%s' is not a mounted btrfs device\n",
180 path);
181 else
182 fprintf(stderr, "ERROR: can't access '%s': %s\n",
183 path, strerror(errno));
184 goto leave_with_error;
187 /* check for possible errors before backgrounding */
188 status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
189 status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
190 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
191 if (ret) {
192 fprintf(stderr,
193 "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
194 path, strerror(errno));
195 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
196 fprintf(stderr, ", %s\n",
197 replace_dev_result2string(status_args.result));
198 else
199 fprintf(stderr, "\n");
200 goto leave_with_error;
203 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
204 fprintf(stderr,
205 "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
206 path, replace_dev_result2string(status_args.result));
207 goto leave_with_error;
210 if (status_args.status.replace_state ==
211 BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
212 fprintf(stderr,
213 "ERROR: btrfs replace on \"%s\" already started!\n",
214 path);
215 goto leave_with_error;
218 srcdev = argv[optind];
219 dstdev = canonicalize_path(argv[optind + 1]);
220 if (!dstdev) {
221 fprintf(stderr,
222 "ERROR: Could not canonicalize path '%s': %s\n",
223 argv[optind + 1], strerror(errno));
224 goto leave_with_error;
227 if (is_numerical(srcdev)) {
228 struct btrfs_ioctl_fs_info_args fi_args;
229 struct btrfs_ioctl_dev_info_args *di_args = NULL;
231 start_args.start.srcdevid = arg_strtou64(srcdev);
233 ret = get_fs_info(path, &fi_args, &di_args);
234 if (ret) {
235 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
236 "%s\n", strerror(-ret));
237 free(di_args);
238 goto leave_with_error;
240 if (!fi_args.num_devices) {
241 fprintf(stderr, "ERROR: no devices found\n");
242 free(di_args);
243 goto leave_with_error;
246 for (i = 0; i < fi_args.num_devices; i++)
247 if (start_args.start.srcdevid == di_args[i].devid)
248 break;
249 free(di_args);
250 if (i == fi_args.num_devices) {
251 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
252 srcdev, path);
253 goto leave_with_error;
255 } else {
256 fdsrcdev = open(srcdev, O_RDWR);
257 if (fdsrcdev < 0) {
258 fprintf(stderr, "Error: Unable to open device '%s'\n",
259 srcdev);
260 fprintf(stderr, "\tTry using the devid instead of the path\n");
261 goto leave_with_error;
263 ret = fstat(fdsrcdev, &st);
264 if (ret) {
265 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
266 goto leave_with_error;
268 if (!S_ISBLK(st.st_mode)) {
269 fprintf(stderr, "Error: '%s' is not a block device\n",
270 srcdev);
271 goto leave_with_error;
273 strncpy((char *)start_args.start.srcdev_name, srcdev,
274 BTRFS_DEVICE_PATH_NAME_MAX);
275 close(fdsrcdev);
276 fdsrcdev = -1;
277 start_args.start.srcdevid = 0;
280 ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
281 if (ret) {
282 fprintf(stderr, "%s", estr);
283 goto leave_with_error;
285 fddstdev = open(dstdev, O_RDWR);
286 if (fddstdev < 0) {
287 fprintf(stderr, "Unable to open %s\n", dstdev);
288 goto leave_with_error;
290 strncpy((char *)start_args.start.tgtdev_name, dstdev,
291 BTRFS_DEVICE_PATH_NAME_MAX);
292 ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
293 &mixed, 0);
294 if (ret)
295 goto leave_with_error;
297 close(fddstdev);
298 fddstdev = -1;
299 free(dstdev);
300 dstdev = NULL;
302 dev_replace_handle_sigint(fdmnt);
303 if (!do_not_background) {
304 if (daemon(0, 0) < 0) {
305 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
306 strerror(errno));
307 goto leave_with_error;
311 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
312 start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
313 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
314 if (do_not_background) {
315 if (ret) {
316 fprintf(stderr,
317 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
318 path, strerror(errno));
319 if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
320 fprintf(stderr, ", %s\n",
321 replace_dev_result2string(start_args.result));
322 else
323 fprintf(stderr, "\n");
325 if (errno == EOPNOTSUPP)
326 fprintf(stderr,
327 "WARNING: dev_replace does not yet handle RAID5/6\n");
329 goto leave_with_error;
332 if (start_args.result !=
333 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
334 fprintf(stderr,
335 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
336 path,
337 replace_dev_result2string(start_args.result));
338 goto leave_with_error;
341 close_file_or_dir(fdmnt, dirstream);
342 return 0;
344 leave_with_error:
345 if (dstdev)
346 free(dstdev);
347 if (fdmnt != -1)
348 close(fdmnt);
349 if (fdsrcdev != -1)
350 close(fdsrcdev);
351 if (fddstdev != -1)
352 close(fddstdev);
353 return 1;
356 static const char *const cmd_status_replace_usage[] = {
357 "btrfs replace status [-1] <mount_point>",
358 "Print status and progress information of a running device replace",
359 "operation",
361 "-1 print once instead of print continuously until the replace",
362 " operation finishes (or is canceled)",
363 NULL
366 static int cmd_status_replace(int argc, char **argv)
368 int fd;
369 int e;
370 int c;
371 char *path;
372 int once = 0;
373 int ret;
374 DIR *dirstream = NULL;
376 while ((c = getopt(argc, argv, "1")) != -1) {
377 switch (c) {
378 case '1':
379 once = 1;
380 break;
381 case '?':
382 default:
383 usage(cmd_status_replace_usage);
387 if (check_argc_exact(argc - optind, 1))
388 usage(cmd_status_replace_usage);
390 path = argv[optind];
391 fd = open_file_or_dir(path, &dirstream);
392 e = errno;
393 if (fd < 0) {
394 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
395 path, strerror(e));
396 return 1;
399 ret = print_replace_status(fd, path, once);
400 close_file_or_dir(fd, dirstream);
401 return !!ret;
404 static int print_replace_status(int fd, const char *path, int once)
406 struct btrfs_ioctl_dev_replace_args args = {0};
407 struct btrfs_ioctl_dev_replace_status_params *status;
408 int ret;
409 int prevent_loop = 0;
410 int skip_stats;
411 int num_chars;
412 char string1[80];
413 char string2[80];
414 char string3[80];
416 for (;;) {
417 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
418 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
419 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
420 if (ret) {
421 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
422 path, strerror(errno));
423 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
424 fprintf(stderr, ", %s\n",
425 replace_dev_result2string(args.result));
426 else
427 fprintf(stderr, "\n");
428 return ret;
431 status = &args.status;
432 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
433 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
434 path,
435 replace_dev_result2string(args.result));
436 return -1;
439 skip_stats = 0;
440 num_chars = 0;
441 switch (status->replace_state) {
442 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
443 num_chars =
444 printf("%s done",
445 progress2string(string3,
446 sizeof(string3),
447 status->progress_1000));
448 break;
449 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
450 prevent_loop = 1;
451 printf("Started on %s, finished on %s",
452 time2string(string1, sizeof(string1),
453 status->time_started),
454 time2string(string2, sizeof(string2),
455 status->time_stopped));
456 break;
457 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
458 prevent_loop = 1;
459 printf("Started on %s, canceled on %s at %s",
460 time2string(string1, sizeof(string1),
461 status->time_started),
462 time2string(string2, sizeof(string2),
463 status->time_stopped),
464 progress2string(string3, sizeof(string3),
465 status->progress_1000));
466 break;
467 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
468 prevent_loop = 1;
469 printf("Started on %s, suspended on %s at %s",
470 time2string(string1, sizeof(string1),
471 status->time_started),
472 time2string(string2, sizeof(string2),
473 status->time_stopped),
474 progress2string(string3, sizeof(string3),
475 status->progress_1000));
476 break;
477 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
478 prevent_loop = 1;
479 skip_stats = 1;
480 printf("Never started");
481 break;
482 default:
483 prevent_loop = 1;
484 fprintf(stderr,
485 "Unknown btrfs dev replace status:%llu",
486 status->replace_state);
487 ret = -EINVAL;
488 break;
491 if (!skip_stats)
492 num_chars += printf(
493 ", %llu write errs, %llu uncorr. read errs",
494 (unsigned long long)status->num_write_errors,
495 (unsigned long long)
496 status->num_uncorrectable_read_errors);
497 if (once || prevent_loop || ret) {
498 printf("\n");
499 return ret;
502 fflush(stdout);
503 sleep(1);
504 while (num_chars > 0) {
505 putchar('\b');
506 num_chars--;
510 return 0;
513 static char *
514 time2string(char *buf, size_t s, __u64 t)
516 struct tm t_tm;
517 time_t t_time_t;
519 t_time_t = (time_t)t;
520 assert((__u64)t_time_t == t);
521 localtime_r(&t_time_t, &t_tm);
522 strftime(buf, s, "%e.%b %T", &t_tm);
523 return buf;
526 static char *
527 progress2string(char *buf, size_t s, int progress_1000)
529 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
530 assert(s > 0);
531 buf[s - 1] = '\0';
532 return buf;
535 static const char *const cmd_cancel_replace_usage[] = {
536 "btrfs replace cancel <mount_point>",
537 "Cancel a running device replace operation.",
538 NULL
541 static int cmd_cancel_replace(int argc, char **argv)
543 struct btrfs_ioctl_dev_replace_args args = {0};
544 int ret;
545 int c;
546 int fd;
547 int e;
548 char *path;
549 DIR *dirstream = NULL;
551 while ((c = getopt(argc, argv, "")) != -1) {
552 switch (c) {
553 case '?':
554 default:
555 usage(cmd_cancel_replace_usage);
559 if (check_argc_exact(argc - optind, 1))
560 usage(cmd_cancel_replace_usage);
562 path = argv[optind];
563 fd = open_file_or_dir(path, &dirstream);
564 if (fd < 0) {
565 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
566 path, strerror(errno));
567 return 1;
570 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
571 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
572 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
573 e = errno;
574 close_file_or_dir(fd, dirstream);
575 if (ret) {
576 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s",
577 path, strerror(e));
578 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
579 fprintf(stderr, ", %s\n",
580 replace_dev_result2string(args.result));
581 else
582 fprintf(stderr, "\n");
583 return 1;
585 if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
586 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
587 path, replace_dev_result2string(args.result));
588 return 2;
590 return 0;
593 const struct cmd_group replace_cmd_group = {
594 replace_cmd_group_usage, NULL, {
595 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
596 0 },
597 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
598 0 },
599 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
600 0 },
601 NULL_CMD_STRUCT
605 int cmd_replace(int argc, char **argv)
607 return handle_command_group(&replace_cmd_group, argc, argv);