btrfs-progs: tests: convert/014 use shell builtin for generating content
[platform/upstream/btrfs-progs.git] / cmds-replace.c
1 /*
2  * Copyright (C) 2012 STRATO.  All rights reserved.
3  *
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.
7  *
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.
12  *
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.
17  */
18
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>
31
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"
38
39 #include "commands.h"
40 #include "help.h"
41 #include "mkfs/common.h"
42
43 static int print_replace_status(int fd, const char *path, int once);
44 static char *time2string(char *buf, size_t s, __u64 t);
45 static char *progress2string(char *buf, size_t s, int progress_1000);
46
47
48 static const char *replace_dev_result2string(__u64 result)
49 {
50         switch (result) {
51         case BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR:
52                 return "no error";
53         case BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED:
54                 return "not started";
55         case BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED:
56                 return "already started";
57         case BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS:
58                 return "scrub is in progress";
59         default:
60                 return "<illegal result value>";
61         }
62 }
63
64 static const char * const replace_cmd_group_usage[] = {
65         "btrfs replace <command> [<args>]",
66         NULL
67 };
68
69 static int dev_replace_cancel_fd = -1;
70 static void dev_replace_sigint_handler(int signal)
71 {
72         int ret;
73         struct btrfs_ioctl_dev_replace_args args = {0};
74
75         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
76         ret = ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
77         if (ret < 0)
78                 perror("Device replace cancel failed");
79 }
80
81 static int dev_replace_handle_sigint(int fd)
82 {
83         struct sigaction sa = {
84                 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
85         };
86
87         dev_replace_cancel_fd = fd;
88         return sigaction(SIGINT, &sa, NULL);
89 }
90
91 static const char *const cmd_replace_start_usage[] = {
92         "btrfs replace start [-Bfr] <srcdev>|<devid> <targetdev> <mount_point>",
93         "Replace device of a btrfs filesystem.",
94         "On a live filesystem, duplicate the data to the target device which",
95         "is currently stored on the source device. If the source device is not",
96         "available anymore, or if the -r option is set, the data is built",
97         "only using the RAID redundancy mechanisms. After completion of the",
98         "operation, the source device is removed from the filesystem.",
99         "If the <srcdev> is a numerical value, it is assumed to be the device id",
100         "of the filesystem which is mounted at <mount_point>, otherwise it is",
101         "the path to the source device. If the source device is disconnected,",
102         "from the system, you have to use the <devid> parameter format.",
103         "The <targetdev> needs to be same size or larger than the <srcdev>.",
104         "",
105         "-r     only read from <srcdev> if no other zero-defect mirror exists",
106         "       (enable this if your drive has lots of read errors, the access",
107         "       would be very slow)",
108         "-f     force using and overwriting <targetdev> even if it looks like",
109         "       containing a valid btrfs filesystem. A valid filesystem is",
110         "       assumed if a btrfs superblock is found which contains a",
111         "       correct checksum. Devices which are currently mounted are",
112         "       never allowed to be used as the <targetdev>",
113         "-B     do not background",
114         NULL
115 };
116
117 static int cmd_replace_start(int argc, char **argv)
118 {
119         struct btrfs_ioctl_dev_replace_args start_args = {0};
120         struct btrfs_ioctl_dev_replace_args status_args = {0};
121         int ret;
122         int i;
123         int c;
124         int fdmnt = -1;
125         int fddstdev = -1;
126         char *path;
127         char *srcdev;
128         char *dstdev = NULL;
129         int avoid_reading_from_srcdev = 0;
130         int force_using_targetdev = 0;
131         u64 dstdev_block_count;
132         int do_not_background = 0;
133         DIR *dirstream = NULL;
134         u64 srcdev_size;
135         u64 dstdev_size;
136
137         while ((c = getopt(argc, argv, "Brf")) != -1) {
138                 switch (c) {
139                 case 'B':
140                         do_not_background = 1;
141                         break;
142                 case 'r':
143                         avoid_reading_from_srcdev = 1;
144                         break;
145                 case 'f':
146                         force_using_targetdev = 1;
147                         break;
148                 case '?':
149                 default:
150                         usage(cmd_replace_start_usage);
151                 }
152         }
153
154         start_args.start.cont_reading_from_srcdev_mode =
155                 avoid_reading_from_srcdev ?
156                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
157                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
158         if (check_argc_exact(argc - optind, 3))
159                 usage(cmd_replace_start_usage);
160         path = argv[optind + 2];
161
162         fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
163         if (fdmnt < 0)
164                 goto leave_with_error;
165
166         /* check for possible errors before backgrounding */
167         status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
168         status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
169         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
170         if (ret < 0) {
171                 fprintf(stderr,
172                         "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m",
173                         path);
174                 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
175                         fprintf(stderr, ", %s\n",
176                                 replace_dev_result2string(status_args.result));
177                 else
178                         fprintf(stderr, "\n");
179                 goto leave_with_error;
180         }
181
182         if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
183                 error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s",
184                         path, replace_dev_result2string(status_args.result));
185                 goto leave_with_error;
186         }
187
188         if (status_args.status.replace_state ==
189             BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
190                 error("device replace on '%s' already started", path);
191                 goto leave_with_error;
192         }
193
194         srcdev = argv[optind];
195         dstdev = canonicalize_path(argv[optind + 1]);
196         if (!dstdev) {
197                 error("cannot canonicalize path '%s': %m",
198                         argv[optind + 1]);
199                 goto leave_with_error;
200         }
201
202         if (string_is_numerical(srcdev)) {
203                 struct btrfs_ioctl_fs_info_args fi_args;
204                 struct btrfs_ioctl_dev_info_args *di_args = NULL;
205
206                 start_args.start.srcdevid = arg_strtou64(srcdev);
207
208                 ret = get_fs_info(path, &fi_args, &di_args);
209                 if (ret) {
210                         error("failed to get device info: %s", strerror(-ret));
211                         free(di_args);
212                         goto leave_with_error;
213                 }
214                 if (!fi_args.num_devices) {
215                         error("no devices found");
216                         free(di_args);
217                         goto leave_with_error;
218                 }
219
220                 for (i = 0; i < fi_args.num_devices; i++)
221                         if (start_args.start.srcdevid == di_args[i].devid)
222                                 break;
223                 srcdev_size = di_args[i].total_bytes;
224                 free(di_args);
225                 if (i == fi_args.num_devices) {
226                         error("'%s' is not a valid devid for filesystem '%s'",
227                                 srcdev, path);
228                         goto leave_with_error;
229                 }
230         } else if (is_block_device(srcdev) > 0) {
231                 strncpy((char *)start_args.start.srcdev_name, srcdev,
232                         BTRFS_DEVICE_PATH_NAME_MAX);
233                 start_args.start.srcdevid = 0;
234                 srcdev_size = get_partition_size(srcdev);
235         } else {
236                 error("source device must be a block device or a devid");
237                 goto leave_with_error;
238         }
239
240         ret = test_dev_for_mkfs(dstdev, force_using_targetdev);
241         if (ret)
242                 goto leave_with_error;
243
244         dstdev_size = get_partition_size(dstdev);
245         if (srcdev_size > dstdev_size) {
246                 error("target device smaller than source device (required %llu bytes)",
247                         srcdev_size);
248                 goto leave_with_error;
249         }
250
251         fddstdev = open(dstdev, O_RDWR);
252         if (fddstdev < 0) {
253                 error("unable to open %s: %m", dstdev);
254                 goto leave_with_error;
255         }
256         strncpy((char *)start_args.start.tgtdev_name, dstdev,
257                 BTRFS_DEVICE_PATH_NAME_MAX);
258         ret = btrfs_prepare_device(fddstdev, dstdev, &dstdev_block_count, 0,
259                         PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE);
260         if (ret)
261                 goto leave_with_error;
262
263         close(fddstdev);
264         fddstdev = -1;
265         free(dstdev);
266         dstdev = NULL;
267
268         dev_replace_handle_sigint(fdmnt);
269         if (!do_not_background) {
270                 if (daemon(0, 0) < 0) {
271                         error("backgrounding failed: %m");
272                         goto leave_with_error;
273                 }
274         }
275
276         start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
277         start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
278         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
279         if (do_not_background) {
280                 if (ret < 0) {
281                         fprintf(stderr,
282                                 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %m",
283                                 path);
284                         if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
285                                 fprintf(stderr, ", %s\n",
286                                         replace_dev_result2string(start_args.result));
287                         else
288                                 fprintf(stderr, "\n");
289
290                         if (errno == EOPNOTSUPP)
291                                 warning("device replace of RAID5/6 not supported with this kernel");
292
293                         goto leave_with_error;
294                 }
295
296                 if (start_args.result !=
297                     BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
298                         error("ioctl(DEV_REPLACE_START) on '%s' returns error: %s",
299                                 path,
300                                 replace_dev_result2string(start_args.result));
301                         goto leave_with_error;
302                 }
303         }
304         close_file_or_dir(fdmnt, dirstream);
305         return 0;
306
307 leave_with_error:
308         if (dstdev)
309                 free(dstdev);
310         if (fdmnt != -1)
311                 close(fdmnt);
312         if (fddstdev != -1)
313                 close(fddstdev);
314         return 1;
315 }
316
317 static const char *const cmd_replace_status_usage[] = {
318         "btrfs replace status [-1] <mount_point>",
319         "Print status and progress information of a running device replace",
320         "operation",
321         "",
322         "-1     print once instead of print continuously until the replace",
323         "       operation finishes (or is canceled)",
324         NULL
325 };
326
327 static int cmd_replace_status(int argc, char **argv)
328 {
329         int fd;
330         int c;
331         char *path;
332         int once = 0;
333         int ret;
334         DIR *dirstream = NULL;
335
336         while ((c = getopt(argc, argv, "1")) != -1) {
337                 switch (c) {
338                 case '1':
339                         once = 1;
340                         break;
341                 case '?':
342                 default:
343                         usage(cmd_replace_status_usage);
344                 }
345         }
346
347         if (check_argc_exact(argc - optind, 1))
348                 usage(cmd_replace_status_usage);
349
350         path = argv[optind];
351         fd = btrfs_open_dir(path, &dirstream, 1);
352         if (fd < 0)
353                 return 1;
354
355         ret = print_replace_status(fd, path, once);
356         close_file_or_dir(fd, dirstream);
357         return !!ret;
358 }
359
360 static int print_replace_status(int fd, const char *path, int once)
361 {
362         struct btrfs_ioctl_dev_replace_args args = {0};
363         struct btrfs_ioctl_dev_replace_status_params *status;
364         int ret;
365         int prevent_loop = 0;
366         int skip_stats;
367         int num_chars;
368         char string1[80];
369         char string2[80];
370         char string3[80];
371
372         for (;;) {
373                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
374                 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
375                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
376                 if (ret < 0) {
377                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %m",
378                                 path);
379                         if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
380                                 fprintf(stderr, ", %s\n",
381                                         replace_dev_result2string(args.result));
382                         else
383                                 fprintf(stderr, "\n");
384                         return ret;
385                 }
386
387                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
388                         error("ioctl(DEV_REPLACE_STATUS) on '%s' returns error: %s",
389                                 path,
390                                 replace_dev_result2string(args.result));
391                         return -1;
392                 }
393
394                 status = &args.status;
395
396                 skip_stats = 0;
397                 num_chars = 0;
398                 switch (status->replace_state) {
399                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
400                         num_chars =
401                                 printf("%s done",
402                                        progress2string(string3,
403                                                        sizeof(string3),
404                                                        status->progress_1000));
405                         break;
406                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
407                         prevent_loop = 1;
408                         printf("Started on %s, finished on %s",
409                                time2string(string1, sizeof(string1),
410                                            status->time_started),
411                                time2string(string2, sizeof(string2),
412                                            status->time_stopped));
413                         break;
414                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
415                         prevent_loop = 1;
416                         printf("Started on %s, canceled on %s at %s",
417                                time2string(string1, sizeof(string1),
418                                            status->time_started),
419                                time2string(string2, sizeof(string2),
420                                            status->time_stopped),
421                                progress2string(string3, sizeof(string3),
422                                                status->progress_1000));
423                         break;
424                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
425                         prevent_loop = 1;
426                         printf("Started on %s, suspended on %s at %s",
427                                time2string(string1, sizeof(string1),
428                                            status->time_started),
429                                time2string(string2, sizeof(string2),
430                                            status->time_stopped),
431                                progress2string(string3, sizeof(string3),
432                                                status->progress_1000));
433                         break;
434                 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
435                         prevent_loop = 1;
436                         skip_stats = 1;
437                         printf("Never started");
438                         break;
439                 default:
440                         error("unknown status from ioctl DEV_REPLACE_STATUS on '%s': %llu",
441                                         path, status->replace_state);
442                         return -EINVAL;
443                 }
444
445                 if (!skip_stats)
446                         num_chars += printf(
447                                 ", %llu write errs, %llu uncorr. read errs",
448                                 (unsigned long long)status->num_write_errors,
449                                 (unsigned long long)
450                                  status->num_uncorrectable_read_errors);
451                 if (once || prevent_loop) {
452                         printf("\n");
453                         break;
454                 }
455
456                 fflush(stdout);
457                 sleep(1);
458                 while (num_chars > 0) {
459                         putchar('\b');
460                         num_chars--;
461                 }
462         }
463
464         return 0;
465 }
466
467 static char *
468 time2string(char *buf, size_t s, __u64 t)
469 {
470         struct tm t_tm;
471         time_t t_time_t;
472
473         t_time_t = (time_t)t;
474         assert((__u64)t_time_t == t);
475         localtime_r(&t_time_t, &t_tm);
476         strftime(buf, s, "%e.%b %T", &t_tm);
477         return buf;
478 }
479
480 static char *
481 progress2string(char *buf, size_t s, int progress_1000)
482 {
483         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
484         assert(s > 0);
485         buf[s - 1] = '\0';
486         return buf;
487 }
488
489 static const char *const cmd_replace_cancel_usage[] = {
490         "btrfs replace cancel <mount_point>",
491         "Cancel a running device replace operation.",
492         NULL
493 };
494
495 static int cmd_replace_cancel(int argc, char **argv)
496 {
497         struct btrfs_ioctl_dev_replace_args args = {0};
498         int ret;
499         int c;
500         int fd;
501         char *path;
502         DIR *dirstream = NULL;
503
504         while ((c = getopt(argc, argv, "")) != -1) {
505                 switch (c) {
506                 case '?':
507                 default:
508                         usage(cmd_replace_cancel_usage);
509                 }
510         }
511
512         if (check_argc_exact(argc - optind, 1))
513                 usage(cmd_replace_cancel_usage);
514
515         path = argv[optind];
516         fd = btrfs_open_dir(path, &dirstream, 1);
517         if (fd < 0)
518                 return 1;
519
520         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
521         args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
522         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
523         close_file_or_dir(fd, dirstream);
524         if (ret < 0) {
525                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %m",
526                         path);
527                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
528                         fprintf(stderr, ", %s\n",
529                                 replace_dev_result2string(args.result));
530                 else
531                         fprintf(stderr, "\n");
532                 return 1;
533         }
534         if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
535                 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
536                         path, replace_dev_result2string(args.result));
537                 return 2;
538         }
539         return 0;
540 }
541
542 static const char replace_cmd_group_info[] =
543 "replace a device in the filesystem";
544
545 const struct cmd_group replace_cmd_group = {
546         replace_cmd_group_usage, replace_cmd_group_info, {
547                 { "start", cmd_replace_start, cmd_replace_start_usage, NULL,
548                   0 },
549                 { "status", cmd_replace_status, cmd_replace_status_usage, NULL,
550                   0 },
551                 { "cancel", cmd_replace_cancel, cmd_replace_cancel_usage, NULL,
552                   0 },
553                 NULL_CMD_STRUCT
554         }
555 };
556
557 int cmd_replace(int argc, char **argv)
558 {
559         return handle_command_group(&replace_cmd_group, argc, argv);
560 }