btrfs-progs: return error when canonicalize_path failed
[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 = NULL;
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 = canonicalize_path(argv[optind + 1]);
213         if (!dstdev) {
214                 fprintf(stderr,
215                         "ERROR: Could not canonicalize path '%s': %s\n",
216                         argv[optind + 1], strerror(errno));
217                 goto leave_with_error;
218         }
219
220         if (is_numerical(srcdev)) {
221                 struct btrfs_ioctl_fs_info_args fi_args;
222                 struct btrfs_ioctl_dev_info_args *di_args = NULL;
223
224                 start_args.start.srcdevid = arg_strtou64(srcdev);
225
226                 ret = get_fs_info(path, &fi_args, &di_args);
227                 if (ret) {
228                         fprintf(stderr, "ERROR: getting dev info for devstats failed: "
229                                         "%s\n", strerror(-ret));
230                         free(di_args);
231                         goto leave_with_error;
232                 }
233                 if (!fi_args.num_devices) {
234                         fprintf(stderr, "ERROR: no devices found\n");
235                         free(di_args);
236                         goto leave_with_error;
237                 }
238
239                 for (i = 0; i < fi_args.num_devices; i++)
240                         if (start_args.start.srcdevid == di_args[i].devid)
241                                 break;
242                 free(di_args);
243                 if (i == fi_args.num_devices) {
244                         fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
245                                 srcdev, path);
246                         goto leave_with_error;
247                 }
248         } else {
249                 fdsrcdev = open(srcdev, O_RDWR);
250                 if (fdsrcdev < 0) {
251                         fprintf(stderr, "Error: Unable to open device '%s'\n",
252                                 srcdev);
253                         fprintf(stderr, "\tTry using the devid instead of the path\n");
254                         goto leave_with_error;
255                 }
256                 ret = fstat(fdsrcdev, &st);
257                 if (ret) {
258                         fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
259                         goto leave_with_error;
260                 }
261                 if (!S_ISBLK(st.st_mode)) {
262                         fprintf(stderr, "Error: '%s' is not a block device\n",
263                                 srcdev);
264                         goto leave_with_error;
265                 }
266                 strncpy((char *)start_args.start.srcdev_name, srcdev,
267                         BTRFS_DEVICE_PATH_NAME_MAX);
268                 close(fdsrcdev);
269                 fdsrcdev = -1;
270                 start_args.start.srcdevid = 0;
271         }
272
273         ret = test_dev_for_mkfs(dstdev, force_using_targetdev, estr);
274         if (ret) {
275                 fprintf(stderr, "%s", estr);
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         strncpy((char *)start_args.start.tgtdev_name, dstdev,
284                 BTRFS_DEVICE_PATH_NAME_MAX);
285         ret = btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
286                                  &mixed, 0);
287         if (ret)
288                 goto leave_with_error;
289
290         close(fddstdev);
291         fddstdev = -1;
292         free(dstdev);
293         dstdev = NULL;
294
295         dev_replace_handle_sigint(fdmnt);
296         if (!do_not_background) {
297                 if (daemon(0, 0) < 0) {
298                         fprintf(stderr, "ERROR, backgrounding failed: %s\n",
299                                 strerror(errno));
300                         goto leave_with_error;
301                 }
302         }
303
304         start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
305         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
306         if (do_not_background) {
307                 if (ret) {
308                         fprintf(stderr,
309                                 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
310                                 path, strerror(errno),
311                                 replace_dev_result2string(start_args.result));
312
313                         if (errno == EOPNOTSUPP)
314                                 fprintf(stderr,
315                                         "WARNING: dev_replace does not yet handle RAID5/6\n");
316
317                         goto leave_with_error;
318                 }
319
320                 if (start_args.result !=
321                     BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
322                         fprintf(stderr,
323                                 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
324                                 path,
325                                 replace_dev_result2string(start_args.result));
326                         goto leave_with_error;
327                 }
328         }
329         close_file_or_dir(fdmnt, dirstream);
330         return 0;
331
332 leave_with_error:
333         if (dstdev)
334                 free(dstdev);
335         if (fdmnt != -1)
336                 close(fdmnt);
337         if (fdsrcdev != -1)
338                 close(fdsrcdev);
339         if (fddstdev != -1)
340                 close(fddstdev);
341         return 1;
342 }
343
344 static const char *const cmd_status_replace_usage[] = {
345         "btrfs replace status [-1] <mount_point>",
346         "Print status and progress information of a running device replace",
347         "operation",
348         "",
349         "-1     print once instead of print continuously until the replace",
350         "       operation finishes (or is canceled)",
351         NULL
352 };
353
354 static int cmd_status_replace(int argc, char **argv)
355 {
356         int fd;
357         int e;
358         int c;
359         char *path;
360         int once = 0;
361         int ret;
362         DIR *dirstream = NULL;
363
364         while ((c = getopt(argc, argv, "1")) != -1) {
365                 switch (c) {
366                 case '1':
367                         once = 1;
368                         break;
369                 case '?':
370                 default:
371                         usage(cmd_status_replace_usage);
372                 }
373         }
374
375         if (check_argc_exact(argc - optind, 1))
376                 usage(cmd_status_replace_usage);
377
378         path = argv[optind];
379         fd = open_file_or_dir(path, &dirstream);
380         e = errno;
381         if (fd < 0) {
382                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
383                         path, strerror(e));
384                 return 1;
385         }
386
387         ret = print_replace_status(fd, path, once);
388         close_file_or_dir(fd, dirstream);
389         return !!ret;
390 }
391
392 static int print_replace_status(int fd, const char *path, int once)
393 {
394         struct btrfs_ioctl_dev_replace_args args = {0};
395         struct btrfs_ioctl_dev_replace_status_params *status;
396         int ret;
397         int prevent_loop = 0;
398         int skip_stats;
399         int num_chars;
400         char string1[80];
401         char string2[80];
402         char string3[80];
403
404         for (;;) {
405                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
406                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
407                 if (ret) {
408                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
409                                 path, strerror(errno),
410                                 replace_dev_result2string(args.result));
411                         return ret;
412                 }
413
414                 status = &args.status;
415                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
416                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
417                                 path,
418                                 replace_dev_result2string(args.result));
419                         return -1;
420                 }
421
422                 skip_stats = 0;
423                 num_chars = 0;
424                 switch (status->replace_state) {
425                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
426                         num_chars =
427                                 printf("%s done",
428                                        progress2string(string3,
429                                                        sizeof(string3),
430                                                        status->progress_1000));
431                         break;
432                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
433                         prevent_loop = 1;
434                         printf("Started on %s, finished on %s",
435                                time2string(string1, sizeof(string1),
436                                            status->time_started),
437                                time2string(string2, sizeof(string2),
438                                            status->time_stopped));
439                         break;
440                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
441                         prevent_loop = 1;
442                         printf("Started on %s, canceled on %s at %s",
443                                time2string(string1, sizeof(string1),
444                                            status->time_started),
445                                time2string(string2, sizeof(string2),
446                                            status->time_stopped),
447                                progress2string(string3, sizeof(string3),
448                                                status->progress_1000));
449                         break;
450                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
451                         prevent_loop = 1;
452                         printf("Started on %s, suspended 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_NEVER_STARTED:
461                         prevent_loop = 1;
462                         skip_stats = 1;
463                         printf("Never started");
464                         break;
465                 default:
466                         prevent_loop = 1;
467                         fprintf(stderr,
468                                 "Unknown btrfs dev replace status:%llu",
469                                 status->replace_state);
470                         ret = -EINVAL;
471                         break;
472                 }
473
474                 if (!skip_stats)
475                         num_chars += printf(
476                                 ", %llu write errs, %llu uncorr. read errs",
477                                 (unsigned long long)status->num_write_errors,
478                                 (unsigned long long)
479                                  status->num_uncorrectable_read_errors);
480                 if (once || prevent_loop || ret) {
481                         printf("\n");
482                         return ret;
483                 }
484
485                 fflush(stdout);
486                 sleep(1);
487                 while (num_chars > 0) {
488                         putchar('\b');
489                         num_chars--;
490                 }
491         }
492
493         return 0;
494 }
495
496 static char *
497 time2string(char *buf, size_t s, __u64 t)
498 {
499         struct tm t_tm;
500         time_t t_time_t;
501
502         t_time_t = (time_t)t;
503         assert((__u64)t_time_t == t);
504         localtime_r(&t_time_t, &t_tm);
505         strftime(buf, s, "%e.%b %T", &t_tm);
506         return buf;
507 }
508
509 static char *
510 progress2string(char *buf, size_t s, int progress_1000)
511 {
512         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
513         assert(s > 0);
514         buf[s - 1] = '\0';
515         return buf;
516 }
517
518 static const char *const cmd_cancel_replace_usage[] = {
519         "btrfs replace cancel <mount_point>",
520         "Cancel a running device replace operation.",
521         NULL
522 };
523
524 static int cmd_cancel_replace(int argc, char **argv)
525 {
526         struct btrfs_ioctl_dev_replace_args args = {0};
527         int ret;
528         int c;
529         int fd;
530         int e;
531         char *path;
532         DIR *dirstream = NULL;
533
534         while ((c = getopt(argc, argv, "")) != -1) {
535                 switch (c) {
536                 case '?':
537                 default:
538                         usage(cmd_cancel_replace_usage);
539                 }
540         }
541
542         if (check_argc_exact(argc - optind, 1))
543                 usage(cmd_cancel_replace_usage);
544
545         path = argv[optind];
546         fd = open_file_or_dir(path, &dirstream);
547         if (fd < 0) {
548                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
549                         path, strerror(errno));
550                 return 1;
551         }
552
553         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
554         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
555         e = errno;
556         close_file_or_dir(fd, dirstream);
557         if (ret) {
558                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
559                         path, strerror(e),
560                         replace_dev_result2string(args.result));
561                 return 1;
562         }
563         if (args.result == BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED) {
564                 printf("INFO: ioctl(DEV_REPLACE_CANCEL)\"%s\": %s\n",
565                         path, replace_dev_result2string(args.result));
566                 return 2;
567         }
568         return 0;
569 }
570
571 const struct cmd_group replace_cmd_group = {
572         replace_cmd_group_usage, NULL, {
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 }