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