btrfs-progs: Update the usage strings of some cmds
[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                         goto leave_with_error;
249                 }
250                 ret = fstat(fdsrcdev, &st);
251                 if (ret) {
252                         fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
253                         goto leave_with_error;
254                 }
255                 if (!S_ISBLK(st.st_mode)) {
256                         fprintf(stderr, "Error: '%s' is not a block device\n",
257                                 srcdev);
258                         goto leave_with_error;
259                 }
260                 strncpy((char *)start_args.start.srcdev_name, srcdev,
261                         BTRFS_DEVICE_PATH_NAME_MAX);
262                 close(fdsrcdev);
263                 fdsrcdev = -1;
264                 start_args.start.srcdevid = 0;
265         }
266
267         ret = check_mounted(dstdev);
268         if (ret < 0) {
269                 fprintf(stderr, "Error checking %s mount status\n", dstdev);
270                 goto leave_with_error;
271         }
272         if (ret == 1) {
273                 fprintf(stderr,
274                         "Error, target device %s is in use and currently mounted!\n",
275                         dstdev);
276                 goto leave_with_error;
277         }
278         fddstdev = open(dstdev, O_RDWR);
279         if (fddstdev < 0) {
280                 fprintf(stderr, "Unable to open %s\n", dstdev);
281                 goto leave_with_error;
282         }
283         ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
284                                     &total_devs, BTRFS_SUPER_INFO_OFFSET);
285         if (ret >= 0 && !force_using_targetdev) {
286                 fprintf(stderr,
287                         "Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
288                         dstdev);
289                 goto leave_with_error;
290         }
291         ret = fstat(fddstdev, &st);
292         if (ret) {
293                 fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
294                 goto leave_with_error;
295         }
296         if (!S_ISBLK(st.st_mode)) {
297                 fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
298                 goto leave_with_error;
299         }
300         strncpy((char *)start_args.start.tgtdev_name, dstdev,
301                 BTRFS_DEVICE_PATH_NAME_MAX);
302         if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
303                                  &mixed, 0)) {
304                 fprintf(stderr, "Error: Failed to prepare device '%s'\n",
305                         dstdev);
306                 goto leave_with_error;
307         }
308         close(fddstdev);
309         fddstdev = -1;
310
311         dev_replace_handle_sigint(fdmnt);
312         if (!do_not_background) {
313                 if (daemon(0, 0) < 0) {
314                         fprintf(stderr, "ERROR, backgrounding failed: %s\n",
315                                 strerror(errno));
316                         goto leave_with_error;
317                 }
318         }
319
320         start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
321         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
322         if (do_not_background) {
323                 if (ret) {
324                         fprintf(stderr,
325                                 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
326                                 path, strerror(errno),
327                                 replace_dev_result2string(start_args.result));
328                         goto leave_with_error;
329                 }
330
331                 if (start_args.result !=
332                     BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
333                         fprintf(stderr,
334                                 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
335                                 path,
336                                 replace_dev_result2string(start_args.result));
337                         goto leave_with_error;
338                 }
339         }
340         close_file_or_dir(fdmnt, dirstream);
341         return 0;
342
343 leave_with_error:
344         if (fdmnt != -1)
345                 close(fdmnt);
346         if (fdsrcdev != -1)
347                 close(fdsrcdev);
348         if (fddstdev != -1)
349                 close(fddstdev);
350         return -1;
351 }
352
353 static const char *const cmd_status_replace_usage[] = {
354         "btrfs replace status [-1] <mount_point>",
355         "Print status and progress information of a running device replace",
356         "operation",
357         "",
358         "-1     print once instead of print continuously until the replace",
359         "       operation finishes (or is canceled)",
360         NULL
361 };
362
363 static int cmd_status_replace(int argc, char **argv)
364 {
365         int fd;
366         int e;
367         int c;
368         char *path;
369         int once = 0;
370         int ret;
371         DIR *dirstream = NULL;
372
373         while ((c = getopt(argc, argv, "1")) != -1) {
374                 switch (c) {
375                 case '1':
376                         once = 1;
377                         break;
378                 case '?':
379                 default:
380                         usage(cmd_status_replace_usage);
381                 }
382         }
383
384         if (check_argc_exact(argc - optind, 1))
385                 usage(cmd_status_replace_usage);
386
387         path = argv[optind];
388         fd = open_file_or_dir(path, &dirstream);
389         e = errno;
390         if (fd < 0) {
391                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
392                         path, strerror(e));
393                 return -1;
394         }
395
396         ret = print_replace_status(fd, path, once);
397         close_file_or_dir(fd, dirstream);
398         return ret;
399 }
400
401 static int print_replace_status(int fd, const char *path, int once)
402 {
403         struct btrfs_ioctl_dev_replace_args args = {0};
404         struct btrfs_ioctl_dev_replace_status_params *status;
405         int ret;
406         int prevent_loop = 0;
407         int skip_stats;
408         int num_chars;
409         char string1[80];
410         char string2[80];
411         char string3[80];
412
413         for (;;) {
414                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
415                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
416                 if (ret) {
417                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
418                                 path, strerror(errno),
419                                 replace_dev_result2string(args.result));
420                         return ret;
421                 }
422
423                 status = &args.status;
424                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
425                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
426                                 path,
427                                 replace_dev_result2string(args.result));
428                         return -1;
429                 }
430
431                 skip_stats = 0;
432                 num_chars = 0;
433                 switch (status->replace_state) {
434                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
435                         num_chars =
436                                 printf("%s done",
437                                        progress2string(string3,
438                                                        sizeof(string3),
439                                                        status->progress_1000));
440                         break;
441                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
442                         prevent_loop = 1;
443                         printf("Started on %s, finished on %s",
444                                time2string(string1, sizeof(string1),
445                                            status->time_started),
446                                time2string(string2, sizeof(string2),
447                                            status->time_stopped));
448                         break;
449                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
450                         prevent_loop = 1;
451                         printf("Started on %s, canceled on %s at %s",
452                                time2string(string1, sizeof(string1),
453                                            status->time_started),
454                                time2string(string2, sizeof(string2),
455                                            status->time_stopped),
456                                progress2string(string3, sizeof(string3),
457                                                status->progress_1000));
458                         break;
459                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
460                         prevent_loop = 1;
461                         printf("Started on %s, suspended on %s at %s",
462                                time2string(string1, sizeof(string1),
463                                            status->time_started),
464                                time2string(string2, sizeof(string2),
465                                            status->time_stopped),
466                                progress2string(string3, sizeof(string3),
467                                                status->progress_1000));
468                         break;
469                 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
470                         prevent_loop = 1;
471                         skip_stats = 1;
472                         printf("Never started");
473                         break;
474                 default:
475                         prevent_loop = 1;
476                         assert(0);
477                         break;
478                 }
479
480                 if (!skip_stats)
481                         num_chars += printf(
482                                 ", %llu write errs, %llu uncorr. read errs",
483                                 (unsigned long long)status->num_write_errors,
484                                 (unsigned long long)
485                                  status->num_uncorrectable_read_errors);
486                 if (once || prevent_loop) {
487                         printf("\n");
488                         return 0;
489                 }
490
491                 fflush(stdout);
492                 sleep(1);
493                 while (num_chars > 0) {
494                         putchar('\b');
495                         num_chars--;
496                 }
497         }
498
499         return 0;
500 }
501
502 static char *
503 time2string(char *buf, size_t s, __u64 t)
504 {
505         struct tm t_tm;
506         time_t t_time_t;
507
508         t_time_t = (time_t)t;
509         assert((__u64)t_time_t == t);
510         localtime_r(&t_time_t, &t_tm);
511         strftime(buf, s, "%e.%b %T", &t_tm);
512         return buf;
513 }
514
515 static char *
516 progress2string(char *buf, size_t s, int progress_1000)
517 {
518         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
519         assert(s > 0);
520         buf[s - 1] = '\0';
521         return buf;
522 }
523
524 static const char *const cmd_cancel_replace_usage[] = {
525         "btrfs replace cancel <mount_point>",
526         "Cancel a running device replace operation.",
527         NULL
528 };
529
530 static int cmd_cancel_replace(int argc, char **argv)
531 {
532         struct btrfs_ioctl_dev_replace_args args = {0};
533         int ret;
534         int c;
535         int fd;
536         int e;
537         char *path;
538         DIR *dirstream = NULL;
539
540         while ((c = getopt(argc, argv, "")) != -1) {
541                 switch (c) {
542                 case '?':
543                 default:
544                         usage(cmd_cancel_replace_usage);
545                 }
546         }
547
548         if (check_argc_exact(argc - optind, 1))
549                 usage(cmd_cancel_replace_usage);
550
551         path = argv[optind];
552         fd = open_file_or_dir(path, &dirstream);
553         if (fd < 0) {
554                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
555                         path, strerror(errno));
556                 return -1;
557         }
558
559         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
560         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
561         e = errno;
562         close_file_or_dir(fd, dirstream);
563         if (ret) {
564                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
565                         path, strerror(e),
566                         replace_dev_result2string(args.result));
567                 return ret;
568         }
569
570         return 0;
571 }
572
573 const struct cmd_group replace_cmd_group = {
574         replace_cmd_group_usage, NULL, {
575                 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
576                   0 },
577                 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
578                   0 },
579                 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
580                   0 },
581                 { 0, 0, 0, 0, 0 }
582         }
583 };
584
585 int cmd_replace(int argc, char **argv)
586 {
587         return handle_command_group(&replace_cmd_group, argc, argv);
588 }