btrfs-progs: let user know that devid can be used if path is missing
[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         default:
57                 return "<illegal result value>";
58         }
59 }
60
61 static const char * const replace_cmd_group_usage[] = {
62         "btrfs replace <command> [<args>]",
63         NULL
64 };
65
66 static int is_numerical(const char *str)
67 {
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;
75 }
76
77 static int dev_replace_cancel_fd = -1;
78 static void dev_replace_sigint_handler(int signal)
79 {
80         int ret;
81         struct btrfs_ioctl_dev_replace_args args = {0};
82
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");
87 }
88
89 static int dev_replace_handle_sigint(int fd)
90 {
91         struct sigaction sa = {
92                 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
93         };
94
95         dev_replace_cancel_fd = fd;
96         return sigaction(SIGINT, &sa, NULL);
97 }
98
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>.",
112         "",
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
123 };
124
125 static int cmd_start_replace(int argc, char **argv)
126 {
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         u64 total_devs = 1;
141         struct btrfs_fs_devices *fs_devices_mnt = NULL;
142         struct stat st;
143         u64 dstdev_block_count;
144         int do_not_background = 0;
145         int mixed = 0;
146         DIR *dirstream = NULL;
147
148         while ((c = getopt(argc, argv, "Brf")) != -1) {
149                 switch (c) {
150                 case 'B':
151                         do_not_background = 1;
152                         break;
153                 case 'r':
154                         avoid_reading_from_srcdev = 1;
155                         break;
156                 case 'f':
157                         force_using_targetdev = 1;
158                         break;
159                 case '?':
160                 default:
161                         usage(cmd_start_replace_usage);
162                 }
163         }
164
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];
172
173         fdmnt = open_path_or_dev_mnt(path, &dirstream);
174
175         if (fdmnt < 0) {
176                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
177                         path, strerror(errno));
178                 goto leave_with_error;
179         }
180
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);
184         if (ret) {
185                 fprintf(stderr,
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;
190         }
191
192         if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
193                 fprintf(stderr,
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;
197         }
198
199         if (status_args.status.replace_state ==
200             BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
201                 fprintf(stderr,
202                         "ERROR: btrfs replace on \"%s\" already started!\n",
203                         path);
204                 goto leave_with_error;
205         }
206
207         srcdev = argv[optind];
208         dstdev = argv[optind + 1];
209
210         if (is_numerical(srcdev)) {
211                 struct btrfs_ioctl_fs_info_args fi_args;
212                 struct btrfs_ioctl_dev_info_args *di_args = NULL;
213
214                 if (atoi(srcdev) == 0) {
215                         fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
216                                 srcdev);
217                         goto leave_with_error;
218                 }
219                 start_args.start.srcdevid = (__u64)atoi(srcdev);
220
221                 ret = get_fs_info(path, &fi_args, &di_args);
222                 if (ret) {
223                         fprintf(stderr, "ERROR: getting dev info for devstats failed: "
224                                         "%s\n", strerror(-ret));
225                         free(di_args);
226                         goto leave_with_error;
227                 }
228                 if (!fi_args.num_devices) {
229                         fprintf(stderr, "ERROR: no devices found\n");
230                         free(di_args);
231                         goto leave_with_error;
232                 }
233
234                 for (i = 0; i < fi_args.num_devices; i++)
235                         if (start_args.start.srcdevid == di_args[i].devid)
236                                 break;
237                 free(di_args);
238                 if (i == fi_args.num_devices) {
239                         fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
240                                 srcdev, path);
241                         goto leave_with_error;
242                 }
243         } else {
244                 fdsrcdev = open(srcdev, O_RDWR);
245                 if (fdsrcdev < 0) {
246                         fprintf(stderr, "Error: Unable to open device '%s'\n",
247                                 srcdev);
248                         fprintf(stderr, "\tTry using the devid instead of the path\n");
249                         goto leave_with_error;
250                 }
251                 ret = fstat(fdsrcdev, &st);
252                 if (ret) {
253                         fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
254                         goto leave_with_error;
255                 }
256                 if (!S_ISBLK(st.st_mode)) {
257                         fprintf(stderr, "Error: '%s' is not a block device\n",
258                                 srcdev);
259                         goto leave_with_error;
260                 }
261                 strncpy((char *)start_args.start.srcdev_name, srcdev,
262                         BTRFS_DEVICE_PATH_NAME_MAX);
263                 close(fdsrcdev);
264                 fdsrcdev = -1;
265                 start_args.start.srcdevid = 0;
266         }
267
268         ret = check_mounted(dstdev);
269         if (ret < 0) {
270                 fprintf(stderr, "Error checking %s mount status\n", dstdev);
271                 goto leave_with_error;
272         }
273         if (ret == 1) {
274                 fprintf(stderr,
275                         "Error, target device %s is in use and currently mounted!\n",
276                         dstdev);
277                 goto leave_with_error;
278         }
279         fddstdev = open(dstdev, O_RDWR);
280         if (fddstdev < 0) {
281                 fprintf(stderr, "Unable to open %s\n", dstdev);
282                 goto leave_with_error;
283         }
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) {
287                 fprintf(stderr,
288                         "Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
289                         dstdev);
290                 goto leave_with_error;
291         }
292         ret = fstat(fddstdev, &st);
293         if (ret) {
294                 fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
295                 goto leave_with_error;
296         }
297         if (!S_ISBLK(st.st_mode)) {
298                 fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
299                 goto leave_with_error;
300         }
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,
304                                  &mixed, 0)) {
305                 fprintf(stderr, "Error: Failed to prepare device '%s'\n",
306                         dstdev);
307                 goto leave_with_error;
308         }
309         close(fddstdev);
310         fddstdev = -1;
311
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",
316                                 strerror(errno));
317                         goto leave_with_error;
318                 }
319         }
320
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) {
324                 if (ret) {
325                         fprintf(stderr,
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;
330                 }
331
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;
339                 }
340         }
341         close_file_or_dir(fdmnt, dirstream);
342         return 0;
343
344 leave_with_error:
345         if (fdmnt != -1)
346                 close(fdmnt);
347         if (fdsrcdev != -1)
348                 close(fdsrcdev);
349         if (fddstdev != -1)
350                 close(fddstdev);
351         return -1;
352 }
353
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",
357         "operation",
358         "",
359         "-1     print once instead of print continuously until the replace",
360         "       operation finishes (or is canceled)",
361         NULL
362 };
363
364 static int cmd_status_replace(int argc, char **argv)
365 {
366         int fd;
367         int e;
368         int c;
369         char *path;
370         int once = 0;
371         int ret;
372         DIR *dirstream = NULL;
373
374         while ((c = getopt(argc, argv, "1")) != -1) {
375                 switch (c) {
376                 case '1':
377                         once = 1;
378                         break;
379                 case '?':
380                 default:
381                         usage(cmd_status_replace_usage);
382                 }
383         }
384
385         if (check_argc_exact(argc - optind, 1))
386                 usage(cmd_status_replace_usage);
387
388         path = argv[optind];
389         fd = open_file_or_dir(path, &dirstream);
390         e = errno;
391         if (fd < 0) {
392                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
393                         path, strerror(e));
394                 return -1;
395         }
396
397         ret = print_replace_status(fd, path, once);
398         close_file_or_dir(fd, dirstream);
399         return ret;
400 }
401
402 static int print_replace_status(int fd, const char *path, int once)
403 {
404         struct btrfs_ioctl_dev_replace_args args = {0};
405         struct btrfs_ioctl_dev_replace_status_params *status;
406         int ret;
407         int prevent_loop = 0;
408         int skip_stats;
409         int num_chars;
410         char string1[80];
411         char string2[80];
412         char string3[80];
413
414         for (;;) {
415                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
416                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
417                 if (ret) {
418                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
419                                 path, strerror(errno),
420                                 replace_dev_result2string(args.result));
421                         return ret;
422                 }
423
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",
427                                 path,
428                                 replace_dev_result2string(args.result));
429                         return -1;
430                 }
431
432                 skip_stats = 0;
433                 num_chars = 0;
434                 switch (status->replace_state) {
435                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
436                         num_chars =
437                                 printf("%s done",
438                                        progress2string(string3,
439                                                        sizeof(string3),
440                                                        status->progress_1000));
441                         break;
442                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
443                         prevent_loop = 1;
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));
449                         break;
450                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
451                         prevent_loop = 1;
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));
459                         break;
460                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
461                         prevent_loop = 1;
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));
469                         break;
470                 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
471                         prevent_loop = 1;
472                         skip_stats = 1;
473                         printf("Never started");
474                         break;
475                 default:
476                         prevent_loop = 1;
477                         assert(0);
478                         break;
479                 }
480
481                 if (!skip_stats)
482                         num_chars += printf(
483                                 ", %llu write errs, %llu uncorr. read errs",
484                                 (unsigned long long)status->num_write_errors,
485                                 (unsigned long long)
486                                  status->num_uncorrectable_read_errors);
487                 if (once || prevent_loop) {
488                         printf("\n");
489                         return 0;
490                 }
491
492                 fflush(stdout);
493                 sleep(1);
494                 while (num_chars > 0) {
495                         putchar('\b');
496                         num_chars--;
497                 }
498         }
499
500         return 0;
501 }
502
503 static char *
504 time2string(char *buf, size_t s, __u64 t)
505 {
506         struct tm t_tm;
507         time_t t_time_t;
508
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);
513         return buf;
514 }
515
516 static char *
517 progress2string(char *buf, size_t s, int progress_1000)
518 {
519         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
520         assert(s > 0);
521         buf[s - 1] = '\0';
522         return buf;
523 }
524
525 static const char *const cmd_cancel_replace_usage[] = {
526         "btrfs replace cancel <mount_point>",
527         "Cancel a running device replace operation.",
528         NULL
529 };
530
531 static int cmd_cancel_replace(int argc, char **argv)
532 {
533         struct btrfs_ioctl_dev_replace_args args = {0};
534         int ret;
535         int c;
536         int fd;
537         int e;
538         char *path;
539         DIR *dirstream = NULL;
540
541         while ((c = getopt(argc, argv, "")) != -1) {
542                 switch (c) {
543                 case '?':
544                 default:
545                         usage(cmd_cancel_replace_usage);
546                 }
547         }
548
549         if (check_argc_exact(argc - optind, 1))
550                 usage(cmd_cancel_replace_usage);
551
552         path = argv[optind];
553         fd = open_file_or_dir(path, &dirstream);
554         if (fd < 0) {
555                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
556                         path, strerror(errno));
557                 return -1;
558         }
559
560         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
561         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
562         e = errno;
563         close_file_or_dir(fd, dirstream);
564         if (ret) {
565                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
566                         path, strerror(e),
567                         replace_dev_result2string(args.result));
568                 return ret;
569         }
570
571         return 0;
572 }
573
574 const struct cmd_group replace_cmd_group = {
575         replace_cmd_group_usage, NULL, {
576                 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
577                   0 },
578                 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
579                   0 },
580                 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
581                   0 },
582                 { 0, 0, 0, 0, 0 }
583         }
584 };
585
586 int cmd_replace(int argc, char **argv)
587 {
588         return handle_command_group(&replace_cmd_group, argc, argv);
589 }