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