Merge branch 'for-chris' of git://repo.or.cz/btrfs-progs-unstable/devel into raid56
[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         struct btrfs_ioctl_dev_replace_args args = {0};
81
82         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
83         ioctl(dev_replace_cancel_fd, BTRFS_IOC_DEV_REPLACE, &args);
84 }
85
86 static int dev_replace_handle_sigint(int fd)
87 {
88         struct sigaction sa = {
89                 .sa_handler = fd == -1 ? SIG_DFL : dev_replace_sigint_handler
90         };
91
92         dev_replace_cancel_fd = fd;
93         return sigaction(SIGINT, &sa, NULL);
94 }
95
96 static const char *const cmd_start_replace_usage[] = {
97         "btrfs replace start srcdev|devid targetdev [-Bfr] mount_point",
98         "Replace device of a btrfs filesystem.",
99         "On a live filesystem, duplicate the data to the target device which",
100         "is currently stored on the source device. If the source device is not",
101         "available anymore, or if the -r option is set, the data is built",
102         "only using the RAID redundancy mechanisms. After completion of the",
103         "operation, the source device is removed from the filesystem.",
104         "If the srcdev is a numerical value, it is assumed to be the device id",
105         "of the filesystem which is mounted at mount_point, otherwise it is",
106         "the path to the source device. If the source device is disconnected,",
107         "from the system, you have to use the devid parameter format.",
108         "The targetdev needs to be same size or larger than the srcdev.",
109         "",
110         "-r     only read from srcdev if no other zero-defect mirror exists",
111         "       (enable this if your drive has lots of read errors, the access",
112         "       would be very slow)",
113         "-f     force using and overwriting targetdev even if it looks like",
114         "       containing a valid btrfs filesystem. A valid filesystem is",
115         "       assumed if a btrfs superblock is found which contains a",
116         "       correct checksum. Devices which are currently mounted are",
117         "       never allowed to be used as the targetdev",
118         "-B     do not background",
119         NULL
120 };
121
122 static int cmd_start_replace(int argc, char **argv)
123 {
124         struct btrfs_ioctl_dev_replace_args start_args = {0};
125         struct btrfs_ioctl_dev_replace_args status_args = {0};
126         int ret;
127         int i;
128         int c;
129         int fdmnt = -1;
130         int fdsrcdev = -1;
131         int fddstdev = -1;
132         char *path;
133         char *srcdev;
134         char *dstdev;
135         int avoid_reading_from_srcdev = 0;
136         int force_using_targetdev = 0;
137         u64 total_devs = 1;
138         struct btrfs_fs_devices *fs_devices_mnt = NULL;
139         struct stat st;
140         u64 dstdev_block_count;
141         int do_not_background = 0;
142         int mixed = 0;
143
144         while ((c = getopt(argc, argv, "Brf")) != -1) {
145                 switch (c) {
146                 case 'B':
147                         do_not_background = 1;
148                         break;
149                 case 'r':
150                         avoid_reading_from_srcdev = 1;
151                         break;
152                 case 'f':
153                         force_using_targetdev = 1;
154                         break;
155                 case '?':
156                 default:
157                         usage(cmd_start_replace_usage);
158                 }
159         }
160
161         start_args.start.cont_reading_from_srcdev_mode =
162                 avoid_reading_from_srcdev ?
163                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
164                  BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
165         if (check_argc_exact(argc - optind, 3))
166                 usage(cmd_start_replace_usage);
167         path = argv[optind + 2];
168         fdmnt = open_file_or_dir(path);
169         if (fdmnt < 0) {
170                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
171                         path, strerror(errno));
172                 goto leave_with_error;
173         }
174
175         /* check for possible errors before backgrounding */
176         status_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
177         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &status_args);
178         if (ret) {
179                 fprintf(stderr,
180                         "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
181                         path, strerror(errno),
182                         replace_dev_result2string(status_args.result));
183                 goto leave_with_error;
184         }
185
186         if (status_args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
187                 fprintf(stderr,
188                         "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
189                         path, replace_dev_result2string(status_args.result));
190                 goto leave_with_error;
191         }
192
193         if (status_args.status.replace_state ==
194             BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
195                 fprintf(stderr,
196                         "ERROR: btrfs replace on \"%s\" already started!\n",
197                         path);
198                 goto leave_with_error;
199         }
200
201         srcdev = argv[optind];
202         dstdev = argv[optind + 1];
203
204         if (is_numerical(srcdev)) {
205                 struct btrfs_ioctl_fs_info_args fi_args;
206                 struct btrfs_ioctl_dev_info_args *di_args = NULL;
207
208                 if (atoi(srcdev) == 0) {
209                         fprintf(stderr, "Error: Failed to parse the numerical devid value '%s'\n",
210                                 srcdev);
211                         goto leave_with_error;
212                 }
213                 start_args.start.srcdevid = (__u64)atoi(srcdev);
214
215                 ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
216                 if (ret) {
217                         fprintf(stderr, "ERROR: getting dev info for devstats failed: "
218                                         "%s\n", strerror(-ret));
219                         free(di_args);
220                         goto leave_with_error;
221                 }
222                 if (!fi_args.num_devices) {
223                         fprintf(stderr, "ERROR: no devices found\n");
224                         free(di_args);
225                         goto leave_with_error;
226                 }
227
228                 for (i = 0; i < fi_args.num_devices; i++)
229                         if (start_args.start.srcdevid == di_args[i].devid)
230                                 break;
231                 if (i == fi_args.num_devices) {
232                         fprintf(stderr, "Error: '%s' is not a valid devid for filesystem '%s'\n",
233                                 srcdev, path);
234                         goto leave_with_error;
235                 }
236         } else {
237                 fdsrcdev = open(srcdev, O_RDWR);
238                 if (!fdsrcdev) {
239                         fprintf(stderr, "Error: Unable to open device '%s'\n",
240                                 srcdev);
241                         goto leave_with_error;
242                 }
243                 ret = fstat(fdsrcdev, &st);
244                 if (ret) {
245                         fprintf(stderr, "Error: Unable to stat '%s'\n", srcdev);
246                         goto leave_with_error;
247                 }
248                 if (!S_ISBLK(st.st_mode)) {
249                         fprintf(stderr, "Error: '%s' is not a block device\n",
250                                 srcdev);
251                         goto leave_with_error;
252                 }
253                 strncpy((char *)start_args.start.srcdev_name, srcdev,
254                         BTRFS_DEVICE_PATH_NAME_MAX);
255                 close(fdsrcdev);
256                 fdsrcdev = -1;
257                 start_args.start.srcdevid = 0;
258         }
259
260         ret = check_mounted(dstdev);
261         if (ret < 0) {
262                 fprintf(stderr, "Error checking %s mount status\n", dstdev);
263                 goto leave_with_error;
264         }
265         if (ret == 1) {
266                 fprintf(stderr,
267                         "Error, target device %s is in use and currently mounted!\n",
268                         dstdev);
269                 goto leave_with_error;
270         }
271         fddstdev = open(dstdev, O_RDWR);
272         if (fddstdev < 0) {
273                 fprintf(stderr, "Unable to open %s\n", dstdev);
274                 goto leave_with_error;
275         }
276         ret = btrfs_scan_one_device(fddstdev, dstdev, &fs_devices_mnt,
277                                     &total_devs, BTRFS_SUPER_INFO_OFFSET);
278         if (ret >= 0 && !force_using_targetdev) {
279                 fprintf(stderr,
280                         "Error, target device %s contains filesystem, use '-f' to force overwriting.\n",
281                         dstdev);
282                 goto leave_with_error;
283         }
284         ret = fstat(fddstdev, &st);
285         if (ret) {
286                 fprintf(stderr, "Error: Unable to stat '%s'\n", dstdev);
287                 goto leave_with_error;
288         }
289         if (!S_ISBLK(st.st_mode)) {
290                 fprintf(stderr, "Error: '%s' is not a block device\n", dstdev);
291                 goto leave_with_error;
292         }
293         strncpy((char *)start_args.start.tgtdev_name, dstdev,
294                 BTRFS_DEVICE_PATH_NAME_MAX);
295         if (btrfs_prepare_device(fddstdev, dstdev, 1, &dstdev_block_count, 0,
296                                  &mixed, 0)) {
297                 fprintf(stderr, "Error: Failed to prepare device '%s'\n",
298                         dstdev);
299                 goto leave_with_error;
300         }
301         close(fddstdev);
302         fddstdev = -1;
303
304         dev_replace_handle_sigint(fdmnt);
305         if (!do_not_background) {
306                 if (daemon(0, 0) < 0) {
307                         fprintf(stderr, "ERROR, backgrounding failed: %s\n",
308                                 strerror(errno));
309                         goto leave_with_error;
310                 }
311         }
312
313         start_args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_START;
314         ret = ioctl(fdmnt, BTRFS_IOC_DEV_REPLACE, &start_args);
315         if (do_not_background) {
316                 if (ret) {
317                         fprintf(stderr,
318                                 "ERROR: ioctl(DEV_REPLACE_START) failed on \"%s\": %s, %s\n",
319                                 path, strerror(errno),
320                                 replace_dev_result2string(start_args.result));
321                         goto leave_with_error;
322                 }
323
324                 if (start_args.result !=
325                     BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
326                         fprintf(stderr,
327                                 "ERROR: ioctl(DEV_REPLACE_START) on \"%s\" returns error: %s\n",
328                                 path,
329                                 replace_dev_result2string(start_args.result));
330                         goto leave_with_error;
331                 }
332         }
333         close(fdmnt);
334         return 0;
335
336 leave_with_error:
337         if (fdmnt != -1)
338                 close(fdmnt);
339         if (fdsrcdev != -1)
340                 close(fdsrcdev);
341         if (fddstdev != -1)
342                 close(fddstdev);
343         return -1;
344 }
345
346 static const char *const cmd_status_replace_usage[] = {
347         "btrfs replace status mount_point [-1]",
348         "Print status and progress information of a running device replace",
349         "operation",
350         "",
351         "-1     print once instead of print continously until the replace",
352         "       operation finishes (or is canceled)",
353         NULL
354 };
355
356 static int cmd_status_replace(int argc, char **argv)
357 {
358         int fd;
359         int e;
360         int c;
361         char *path;
362         int once = 0;
363         int ret;
364
365         while ((c = getopt(argc, argv, "1")) != -1) {
366                 switch (c) {
367                 case '1':
368                         once = 1;
369                         break;
370                 case '?':
371                 default:
372                         usage(cmd_status_replace_usage);
373                 }
374         }
375
376         if (check_argc_exact(argc - optind, 1))
377                 usage(cmd_status_replace_usage);
378
379         path = argv[optind];
380         fd = open_file_or_dir(path);
381         e = errno;
382         if (fd < 0) {
383                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
384                         path, strerror(e));
385                 return -1;
386         }
387
388         ret = print_replace_status(fd, path, once);
389         close(fd);
390         return ret;
391 }
392
393 static int print_replace_status(int fd, const char *path, int once)
394 {
395         struct btrfs_ioctl_dev_replace_args args = {0};
396         struct btrfs_ioctl_dev_replace_status_params *status;
397         int ret;
398         int prevent_loop = 0;
399         int skip_stats;
400         int num_chars;
401         char string1[80];
402         char string2[80];
403         char string3[80];
404
405         for (;;) {
406                 args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS;
407                 ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
408                 if (ret) {
409                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) failed on \"%s\": %s, %s\n",
410                                 path, strerror(errno),
411                                 replace_dev_result2string(args.result));
412                         return ret;
413                 }
414
415                 status = &args.status;
416                 if (args.result != BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR) {
417                         fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_STATUS) on \"%s\" returns error: %s\n",
418                                 path,
419                                 replace_dev_result2string(args.result));
420                         return -1;
421                 }
422
423                 skip_stats = 0;
424                 num_chars = 0;
425                 switch (status->replace_state) {
426                 case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
427                         num_chars =
428                                 printf("%s done",
429                                        progress2string(string3,
430                                                        sizeof(string3),
431                                                        status->progress_1000));
432                         break;
433                 case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
434                         prevent_loop = 1;
435                         printf("Started on %s, finished on %s",
436                                time2string(string1, sizeof(string1),
437                                            status->time_started),
438                                time2string(string2, sizeof(string2),
439                                            status->time_stopped));
440                         break;
441                 case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
442                         prevent_loop = 1;
443                         printf("Started on %s, canceled on %s at %s",
444                                time2string(string1, sizeof(string1),
445                                            status->time_started),
446                                time2string(string2, sizeof(string2),
447                                            status->time_stopped),
448                                progress2string(string3, sizeof(string3),
449                                                status->progress_1000));
450                         break;
451                 case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
452                         prevent_loop = 1;
453                         printf("Started on %s, suspended on %s at %s",
454                                time2string(string1, sizeof(string1),
455                                            status->time_started),
456                                time2string(string2, sizeof(string2),
457                                            status->time_stopped),
458                                progress2string(string3, sizeof(string3),
459                                                status->progress_1000));
460                         break;
461                 case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED:
462                         prevent_loop = 1;
463                         skip_stats = 1;
464                         printf("Never started");
465                         break;
466                 default:
467                         prevent_loop = 1;
468                         assert(0);
469                         break;
470                 }
471
472                 if (!skip_stats)
473                         num_chars += printf(
474                                 ", %llu write errs, %llu uncorr. read errs",
475                                 (unsigned long long)status->num_write_errors,
476                                 (unsigned long long)
477                                  status->num_uncorrectable_read_errors);
478                 if (once || prevent_loop) {
479                         printf("\n");
480                         return 0;
481                 }
482
483                 fflush(stdout);
484                 sleep(1);
485                 while (num_chars > 0) {
486                         putchar('\b');
487                         num_chars--;
488                 }
489         }
490
491         return 0;
492 }
493
494 static char *
495 time2string(char *buf, size_t s, __u64 t)
496 {
497         struct tm t_tm;
498         time_t t_time_t;
499
500         t_time_t = (time_t)t;
501         assert((__u64)t_time_t == t);
502         localtime_r(&t_time_t, &t_tm);
503         strftime(buf, s, "%e.%b %T", &t_tm);
504         return buf;
505 }
506
507 static char *
508 progress2string(char *buf, size_t s, int progress_1000)
509 {
510         snprintf(buf, s, "%d.%01d%%", progress_1000 / 10, progress_1000 % 10);
511         assert(s > 0);
512         buf[s - 1] = '\0';
513         return buf;
514 }
515
516 static const char *const cmd_cancel_replace_usage[] = {
517         "btrfs replace cancel mount_point",
518         "Cancel a running device replace operation.",
519         NULL
520 };
521
522 static int cmd_cancel_replace(int argc, char **argv)
523 {
524         struct btrfs_ioctl_dev_replace_args args = {0};
525         int ret;
526         int c;
527         int fd;
528         int e;
529         char *path;
530
531         while ((c = getopt(argc, argv, "")) != -1) {
532                 switch (c) {
533                 case '?':
534                 default:
535                         usage(cmd_cancel_replace_usage);
536                 }
537         }
538
539         if (check_argc_exact(argc - optind, 1))
540                 usage(cmd_cancel_replace_usage);
541
542         path = argv[optind];
543         fd = open_file_or_dir(path);
544         if (fd < 0) {
545                 fprintf(stderr, "ERROR: can't access \"%s\": %s\n",
546                         path, strerror(errno));
547                 return -1;
548         }
549
550         args.cmd = BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL;
551         ret = ioctl(fd, BTRFS_IOC_DEV_REPLACE, &args);
552         e = errno;
553         close(fd);
554         if (ret) {
555                 fprintf(stderr, "ERROR: ioctl(DEV_REPLACE_CANCEL) failed on \"%s\": %s, %s\n",
556                         path, strerror(e),
557                         replace_dev_result2string(args.result));
558                 return ret;
559         }
560
561         return 0;
562 }
563
564 const struct cmd_group replace_cmd_group = {
565         replace_cmd_group_usage, NULL, {
566                 { "start", cmd_start_replace, cmd_start_replace_usage, NULL,
567                   0 },
568                 { "status", cmd_status_replace, cmd_status_replace_usage, NULL,
569                   0 },
570                 { "cancel", cmd_cancel_replace, cmd_cancel_replace_usage, NULL,
571                   0 },
572                 { 0, 0, 0, 0, 0 }
573         }
574 };
575
576 int cmd_replace(int argc, char **argv)
577 {
578         return handle_command_group(&replace_cmd_group, argc, argv);
579 }