btrfs-progs: print error within test_dev_for_mkfs
[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
41
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);
45
46
47 static const char *replace_dev_result2string(__u64 result)
48 {
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>";
60         }
61 }
62
63 static const char * const replace_cmd_group_usage[] = {
64         "btrfs replace <command> [<args>]",
65         NULL
66 };
67
68 static int is_numerical(const char *str)
69 {
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;
77 }
78
79 static int dev_replace_cancel_fd = -1;
80 static void dev_replace_sigint_handler(int signal)
81 {
82         int ret;
83         struct btrfs_ioctl_dev_replace_args args = {0};
84
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");
89 }
90
91 static int dev_replace_handle_sigint(int fd)
92 {
93         struct sigaction sa = {
94                 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
95         };
96
97         dev_replace_cancel_fd = fd;
98         return sigaction(SIGINT, &sa, NULL);
99 }
100
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>.",
114         "",
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
125 };
126
127 static int cmd_start_replace(int argc, char **argv)
128 {
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 fddstdev = -1;
136         char *path;
137         char *srcdev;
138         char *dstdev = NULL;
139         int avoid_reading_from_srcdev = 0;
140         int force_using_targetdev = 0;
141         u64 dstdev_block_count;
142         int do_not_background = 0;
143         int mixed = 0;
144         DIR *dirstream = NULL;
145
146         while ((c = getopt(argc, argv, "Brf")) != -1) {
147                 switch (c) {
148                 case 'B':
149                         do_not_background = 1;
150                         break;
151                 case 'r':
152                         avoid_reading_from_srcdev = 1;
153                         break;
154                 case 'f':
155                         force_using_targetdev = 1;
156                         break;
157                 case '?':
158                 default:
159                         usage(cmd_start_replace_usage);
160                 }
161         }
162
163         start_args.start.cont_reading_from_srcdev_mode =
164                 avoid_reading_from_srcdev ?
165                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
166                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
167         if (check_argc_exact(argc - optind, 3))
168                 usage(cmd_start_replace_usage);
169         path = argv[optind + 2];
170
171         fdmnt = open_path_or_dev_mnt(path, &dirstream);
172
173         if (fdmnt < 0) {
174                 if (errno == EINVAL)
175                         fprintf(stderr,
176                                 "ERROR: '%s' is not a mounted btrfs device\n",
177                                 path);
178                 else
179                         fprintf(stderr, "ERROR: can't access '%s': %s\n",
180                                 path, strerror(errno));
181                 goto leave_with_error;
182         }
183
184         /* check for possible errors before backgrounding */
185         status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
186         status_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
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",
191                         path, strerror(errno));
192                 if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
193                         fprintf(stderr, ", %s\n",
194                                 replace_dev_result2string(status_args.result));
195                 else
196                         fprintf(stderr, "\n");
197                 goto leave_with_error;
198         }
199
200         if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
201                 fprintf(stderr,
202                         "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
203                         path, replace_dev_result2string(status_args.result));
204                 goto leave_with_error;
205         }
206
207         if (status_args.status.replace_state ==
208             BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
209                 fprintf(stderr,
210                         "ERROR: btrfs replace on \"%s\" already started!\n",
211                         path);
212                 goto leave_with_error;
213         }
214
215         srcdev = argv[optind];
216         dstdev = canonicalize_path(argv[optind + 1]);
217         if (!dstdev) {
218                 fprintf(stderr,
219                         "ERROR: Could not canonicalize path '%s': %s\n",
220                         argv[optind + 1], strerror(errno));
221                 goto leave_with_error;
222         }
223
224         if (is_numerical(srcdev)) {
225                 struct btrfs_ioctl_fs_info_args fi_args;
226                 struct btrfs_ioctl_dev_info_args *di_args = NULL;
227
228                 start_args.start.srcdevid = arg_strtou64(srcdev);
229
230                 ret = get_fs_info(path, &fi_args, &di_args);
231                 if (ret) {
232                         fprintf(stderr, "ERROR: getting dev info for devstats failed: "
233                                         "%s\n", strerror(-ret));
234                         free(di_args);
235                         goto leave_with_error;
236                 }
237                 if (!fi_args.num_devices) {
238                         fprintf(stderr, "ERROR: no devices found\n");
239                         free(di_args);
240                         goto leave_with_error;
241                 }
242
243                 for (i = 0; i < fi_args.num_devices; i++)
244                         if (start_args.start.srcdevid == di_args[i].devid)
245                                 break;
246                 free(di_args);
247                 if (i == fi_args.num_devices) {
248                         fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
249                                 srcdev, path);
250                         goto leave_with_error;
251                 }
252         } else if (is_block_device(srcdev)) {
253                 strncpy((char *)start_args.start.srcdev_name, srcdev,
254                         BTRFS_DEVICE_PATH_NAME_MAX);
255                 start_args.start.srcdevid = 0;
256         }
257
258         ret = test_dev_for_mkfs(dstdev, force_using_targetdev);
259         if (ret)
260                 goto leave_with_error;
261
262         fddstdev = open(dstdev, O_RDWR);
263         if (fddstdev < 0) {
264                 fprintf(stderr, "Unable to open %s\n", dstdev);
265                 goto leave_with_error;
266         }
267         strncpy((char *)start_args.start.tgtdev_name, dstdev,
268                 BTRFS_DEVICE_PATH_NAME_MAX);
269         ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
270                                  &mixed, 0);
271         if (ret)
272                 goto leave_with_error;
273
274         close(fddstdev);
275         fddstdev = -1;
276         free(dstdev);
277         dstdev = NULL;
278
279         dev_replace_handle_sigint(fdmnt);
280         if (!do_not_background) {
281                 if (daemon(0, 0) < 0) {
282                         fprintf(stderr, "ERROR, backgrounding failed: %s\n",
283                                 strerror(errno));
284                         goto leave_with_error;
285                 }
286         }
287
288         start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
289         start_args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
290         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
291         if (do_not_background) {
292                 if (ret) {
293                         fprintf(stderr,
294                                 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s",
295                                 path, strerror(errno));
296                         if (start_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
297                                 fprintf(stderr, ", %s\n",
298                                         replace_dev_result2string(start_args.result));
299                         else
300                                 fprintf(stderr, "\n");
301
302                         if (errno == EOPNOTSUPP)
303                                 fprintf(stderr,
304                                         "WARNING: dev_replace does not yet handle RAID5/6\n");
305
306                         goto leave_with_error;
307                 }
308
309                 if (start_args.result !=
310                     BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
311                         fprintf(stderr,
312                                 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
313                                 path,
314                                 replace_dev_result2string(start_args.result));
315                         goto leave_with_error;
316                 }
317         }
318         close_file_or_dir(fdmnt, dirstream);
319         return 0;
320
321 leave_with_error:
322         if (dstdev)
323                 free(dstdev);
324         if (fdmnt != -1)
325                 close(fdmnt);
326         if (fddstdev != -1)
327                 close(fddstdev);
328         return 1;
329 }
330
331 static const char *const cmd_status_replace_usage[] = {
332         "btrfs replace status [-1] <mount_point>",
333         "Print status and progress information of a running device replace",
334         "operation",
335         "",
336         "-1     print once instead of print continuously until the replace",
337         "       operation finishes (or is canceled)",
338         NULL
339 };
340
341 static int cmd_status_replace(int argc, char **argv)
342 {
343         int fd;
344         int e;
345         int c;
346         char *path;
347         int once = 0;
348         int ret;
349         DIR *dirstream = NULL;
350
351         while ((c = getopt(argc, argv, "1")) != -1) {
352                 switch (c) {
353                 case '1':
354                         once = 1;
355                         break;
356                 case '?':
357                 default:
358                         usage(cmd_status_replace_usage);
359                 }
360         }
361
362         if (check_argc_exact(argc - optind, 1))
363                 usage(cmd_status_replace_usage);
364
365         path = argv[optind];
366         fd = open_file_or_dir(path, &dirstream);
367         e = errno;
368         if (fd < 0) {
369                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
370                         path, strerror(e));
371                 return 1;
372         }
373
374         ret = print_replace_status(fd, path, once);
375         close_file_or_dir(fd, dirstream);
376         return !!ret;
377 }
378
379 static int print_replace_status(int fd, const char *path, int once)
380 {
381         struct btrfs_ioctl_dev_replace_args args = {0};
382         struct btrfs_ioctl_dev_replace_status_params *status;
383         int ret;
384         int prevent_loop = 0;
385         int skip_stats;
386         int num_chars;
387         char string1[80];
388         char string2[80];
389         char string3[80];
390
391         for (;;) {
392                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
393                 args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
394                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
395                 if (ret) {
396                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s",
397                                 path, strerror(errno));
398                         if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
399                                 fprintf(stderr, ", %s\n",
400                                         replace_dev_result2string(args.result));
401                         else
402                                 fprintf(stderr, "\n");
403                         return ret;
404                 }
405
406                 status = &args.status;
407                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
408                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
409                                 path,
410                                 replace_dev_result2string(args.result));
411                         return -1;
412                 }
413
414                 skip_stats = 0;
415                 num_chars = 0;
416                 switch (status->replace_state) {
417                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
418                         num_chars =
419                                 printf("%s done",
420                                        progress2string(string3,
421                                                        sizeof(string3),
422                                                        status->progress_1000));
423                         break;
424                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
425                         prevent_loop = 1;
426                         printf("Started on %s, finished on %s",
427                                time2string(string1, sizeof(string1),
428                                            status->time_started),
429                                time2string(string2, sizeof(string2),
430                                            status->time_stopped));
431                         break;
432                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
433                         prevent_loop = 1;
434                         printf("Started on %s, canceled on %s at %s",
435                                time2string(string1, sizeof(string1),
436                                            status->time_started),
437                                time2string(string2, sizeof(string2),
438                                            status->time_stopped),
439                                progress2string(string3, sizeof(string3),
440                                                status->progress_1000));
441                         break;
442                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
443                         prevent_loop = 1;
444                         printf("Started on %s, suspended on %s at %s",
445                                time2string(string1, sizeof(string1),
446                                            status->time_started),
447                                time2string(string2, sizeof(string2),
448                                            status->time_stopped),
449                                progress2string(string3, sizeof(string3),
450                                                status->progress_1000));
451                         break;
452                 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
453                         prevent_loop = 1;
454                         skip_stats = 1;
455                         printf("Never started");
456                         break;
457                 default:
458                         prevent_loop = 1;
459                         fprintf(stderr,
460                                 "Unknown btrfs dev replace status:%llu",
461                                 status->replace_state);
462                         ret = -EINVAL;
463                         break;
464                 }
465
466                 if (!skip_stats)
467                         num_chars += printf(
468                                 ", %llu write errs, %llu uncorr. read errs",
469                                 (unsigned long long)status->num_write_errors,
470                                 (unsigned long long)
471                                  status->num_uncorrectable_read_errors);
472                 if (once || prevent_loop || ret) {
473                         printf("\n");
474                         return ret;
475                 }
476
477                 fflush(stdout);
478                 sleep(1);
479                 while (num_chars > 0) {
480                         putchar('\b');
481                         num_chars--;
482                 }
483         }
484
485         return 0;
486 }
487
488 static char *
489 time2string(char *buf, size_t s, __u64 t)
490 {
491         struct tm t_tm;
492         time_t t_time_t;
493
494         t_time_t = (time_t)t;
495         assert((__u64)t_time_t == t);
496         localtime_r(&t_time_t, &t_tm);
497         strftime(buf, s, "%e.%b %T", &t_tm);
498         return buf;
499 }
500
501 static char *
502 progress2string(char *buf, size_t s, int progress_1000)
503 {
504         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
505         assert(s > 0);
506         buf[s - 1] = '\0';
507         return buf;
508 }
509
510 static const char *const cmd_cancel_replace_usage[] = {
511         "btrfs replace cancel <mount_point>",
512         "Cancel a running device replace operation.",
513         NULL
514 };
515
516 static int cmd_cancel_replace(int argc, char **argv)
517 {
518         struct btrfs_ioctl_dev_replace_args args = {0};
519         int ret;
520         int c;
521         int fd;
522         int e;
523         char *path;
524         DIR *dirstream = NULL;
525
526         while ((c = getopt(argc, argv, "")) != -1) {
527                 switch (c) {
528                 case '?':
529                 default:
530                         usage(cmd_cancel_replace_usage);
531                 }
532         }
533
534         if (check_argc_exact(argc - optind, 1))
535                 usage(cmd_cancel_replace_usage);
536
537         path = argv[optind];
538         fd = open_file_or_dir(path, &dirstream);
539         if (fd < 0) {
540                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
541                         path, strerror(errno));
542                 return 1;
543         }
544
545         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
546         args.result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT;
547         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
548         e = errno;
549         close_file_or_dir(fd, dirstream);
550         if (ret) {
551                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s",
552                         path, strerror(e));
553                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_RESULT)
554                         fprintf(stderr, ", %s\n",
555                                 replace_dev_result2string(args.result));
556                 else
557                         fprintf(stderr, "\n");
558                 return 1;
559         }
560         if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
561                 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
562                         path, replace_dev_result2string(args.result));
563                 return 2;
564         }
565         return 0;
566 }
567
568 static const char replace_cmd_group_info[] =
569 "replace a device in the filesystem";
570
571 const struct cmd_group replace_cmd_group = {
572         replace_cmd_group_usage, replace_cmd_group_info, {
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
580         }
581 };
582
583 int cmd_replace(int argc, char **argv)
584 {
585         return handle_command_group(&replace_cmd_group, argc, argv);
586 }