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