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.
24 #include <sys/ioctl.h>
32 #include "kerncompat.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)
50 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
52 case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
54 case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
55 return "already started";
57 return "<illegal result value>";
61 static const char * const replace_cmd_group_usage[] = {
62 "btrfs replace <command> [<args>]",
66 static int is_numerical(const char *str)
68 if (!(*str >= '0' && *str <= '9'))
70 while (*str >= '0' && *str <= '9')
77 static int dev_replace_cancel_fd = -1;
78 static void dev_replace_sigint_handler(int signal)
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);
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",
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};
138 int avoid_reading_from_srcdev = 0;
139 int force_using_targetdev = 0;
141 struct btrfs_fs_devices *fs_devices_mnt = NULL;
143 u64 dstdev_block_count;
144 int do_not_background = 0;
146 DIR *dirstream = NULL;
148 while ((c = getopt(argc, argv, "Brf")) != -1) {
151 do_not_background = 1;
154 avoid_reading_from_srcdev = 1;
157 force_using_targetdev = 1;
161 usage(cmd_start_replace_usage);
165 start_args.start.cont_reading_from_srcdev_mode =
166 avoid_reading_from_srcdev ?
167 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
168 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
169 if (check_argc_exact(argc - optind, 3))
170 usage(cmd_start_replace_usage);
171 path = argv[optind + 2];
173 fdmnt = open_path_or_dev_mnt(path, &dirstream);
176 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
177 path, strerror(errno));
178 goto leave_with_error;
181 /* check for possible errors before backgrounding */
182 status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
183 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
186 "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
187 path, strerror(errno),
188 replace_dev_result2string(status_args.result));
189 goto leave_with_error;
192 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
194 "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
195 path, replace_dev_result2string(status_args.result));
196 goto leave_with_error;
199 if (status_args.status.replace_state ==
200 BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
202 "ERROR: btrfs replace on \"%s\" already started!\n",
204 goto leave_with_error;
207 srcdev = argv[optind];
208 dstdev = argv[optind + 1];
210 if (is_numerical(srcdev)) {
211 struct btrfs_ioctl_fs_info_args fi_args;
212 struct btrfs_ioctl_dev_info_args *di_args = NULL;
214 if (atoi(srcdev) == 0) {
215 fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
217 goto leave_with_error;
219 start_args.start.srcdevid = (__u64)atoi(srcdev);
221 ret = get_fs_info(path, &fi_args, &di_args);
223 fprintf(stderr, "ERROR: getting dev info for devstats failed: "
224 "%s\n", strerror(-ret));
226 goto leave_with_error;
228 if (!fi_args.num_devices) {
229 fprintf(stderr, "ERROR: no devices found\n");
231 goto leave_with_error;
234 for (i = 0; i < fi_args.num_devices; i++)
235 if (start_args.start.srcdevid == di_args[i].devid)
238 if (i == fi_args.num_devices) {
239 fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
241 goto leave_with_error;
244 fdsrcdev = open(srcdev, O_RDWR);
246 fprintf(stderr, "Error: Unable to open device '%s'\n",
248 fprintf(stderr, "\tTry using the devid instead of the path\n");
249 goto leave_with_error;
251 ret = fstat(fdsrcdev, &st);
253 fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
254 goto leave_with_error;
256 if (!S_ISBLK(st.st_mode)) {
257 fprintf(stderr, "Error: '%s' is not a block device\n",
259 goto leave_with_error;
261 strncpy((char *)start_args.start.srcdev_name, srcdev,
262 BTRFS_DEVICE_PATH_NAME_MAX);
265 start_args.start.srcdevid = 0;
268 ret = check_mounted(dstdev);
270 fprintf(stderr, "Error checking %s mount status\n", dstdev);
271 goto leave_with_error;
275 "Error, target device %s is in use and currently mounted!\n",
277 goto leave_with_error;
279 fddstdev = open(dstdev, O_RDWR);
281 fprintf(stderr, "Unable to open %s\n", dstdev);
282 goto leave_with_error;
284 ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
285 &total_devs, BTRFS_SUPER_INFO_OFFSET);
286 if (ret >= 0 && !force_using_targetdev) {
288 "Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
290 goto leave_with_error;
292 ret = fstat(fddstdev, &st);
294 fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
295 goto leave_with_error;
297 if (!S_ISBLK(st.st_mode)) {
298 fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
299 goto leave_with_error;
301 strncpy((char *)start_args.start.tgtdev_name, dstdev,
302 BTRFS_DEVICE_PATH_NAME_MAX);
303 if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
305 fprintf(stderr, "Error: Failed to prepare device '%s'\n",
307 goto leave_with_error;
312 dev_replace_handle_sigint(fdmnt);
313 if (!do_not_background) {
314 if (daemon(0, 0) < 0) {
315 fprintf(stderr, "ERROR, backgrounding failed: %s\n",
317 goto leave_with_error;
321 start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
322 ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
323 if (do_not_background) {
326 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
327 path, strerror(errno),
328 replace_dev_result2string(start_args.result));
329 goto leave_with_error;
332 if (start_args.result !=
333 BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
335 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
337 replace_dev_result2string(start_args.result));
338 goto leave_with_error;
341 close_file_or_dir(fdmnt, dirstream);
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",
359 "-1 print once instead of print continuously until the replace",
360 " operation finishes (or is canceled)",
364 static int cmd_status_replace(int argc, char **argv)
372 DIR *dirstream = NULL;
374 while ((c = getopt(argc, argv, "1")) != -1) {
381 usage(cmd_status_replace_usage);
385 if (check_argc_exact(argc - optind, 1))
386 usage(cmd_status_replace_usage);
389 fd = open_file_or_dir(path, &dirstream);
392 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
397 ret = print_replace_status(fd, path, once);
398 close_file_or_dir(fd, dirstream);
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;
407 int prevent_loop = 0;
415 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
416 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
418 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
419 path, strerror(errno),
420 replace_dev_result2string(args.result));
424 status = &args.status;
425 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
426 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
428 replace_dev_result2string(args.result));
434 switch (status->replace_state) {
435 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
438 progress2string(string3,
440 status->progress_1000));
442 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
444 printf("Started on %s, finished on %s",
445 time2string(string1, sizeof(string1),
446 status->time_started),
447 time2string(string2, sizeof(string2),
448 status->time_stopped));
450 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
452 printf("Started on %s, canceled 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));
460 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
462 printf("Started on %s, suspended on %s at %s",
463 time2string(string1, sizeof(string1),
464 status->time_started),
465 time2string(string2, sizeof(string2),
466 status->time_stopped),
467 progress2string(string3, sizeof(string3),
468 status->progress_1000));
470 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
473 printf("Never started");
483 ", %llu write errs, %llu uncorr. read errs",
484 (unsigned long long)status->num_write_errors,
486 status->num_uncorrectable_read_errors);
487 if (once || prevent_loop) {
494 while (num_chars > 0) {
504 time2string(char *buf, size_t s, __u64 t)
509 t_time_t = (time_t)t;
510 assert((__u64)t_time_t == t);
511 localtime_r(&t_time_t, &t_tm);
512 strftime(buf, s, "%e.%b %T", &t_tm);
517 progress2string(char *buf, size_t s, int progress_1000)
519 snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
525 static const char *const cmd_cancel_replace_usage[] = {
526 "btrfs replace cancel <mount_point>",
527 "Cancel a running device replace operation.",
531 static int cmd_cancel_replace(int argc, char **argv)
533 struct btrfs_ioctl_dev_replace_args args = {0};
539 DIR *dirstream = NULL;
541 while ((c = getopt(argc, argv, "")) != -1) {
545 usage(cmd_cancel_replace_usage);
549 if (check_argc_exact(argc - optind, 1))
550 usage(cmd_cancel_replace_usage);
553 fd = open_file_or_dir(path, &dirstream);
555 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
556 path, strerror(errno));
560 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
561 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
563 close_file_or_dir(fd, dirstream);
565 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
567 replace_dev_result2string(args.result));
574 const struct cmd_group replace_cmd_group = {
575 replace_cmd_group_usage, NULL, {
576 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
578 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
580 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
586 int cmd_replace(int argc, char **argv)
588 return handle_command_group(&replace_cmd_group, argc, argv);