btrfs-progs: doc: add description of missing and example, of device remove
[platform/upstream/btrfs-progs.git] / cmds-device.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 #include <getopt.h>
26
27 #include "kerncompat.h"
28 #include "ctree.h"
29 #include "ioctl.h"
30 #include "utils.h"
31 #include "volumes.h"
32 #include "cmds-fi-usage.h"
33
34 #include "commands.h"
35 #include "help.h"
36 #include "mkfs/common.h"
37
38 static const char * const device_cmd_group_usage[] = {
39         "btrfs device <command> [<args>]",
40         NULL
41 };
42
43 static const char * const cmd_device_add_usage[] = {
44         "btrfs device add [options] <device> [<device>...] <path>",
45         "Add a device to a filesystem",
46         "-K|--nodiscard    do not perform whole device TRIM",
47         "-f|--force        force overwrite existing filesystem on the disk",
48         NULL
49 };
50
51 static int cmd_device_add(int argc, char **argv)
52 {
53         char    *mntpnt;
54         int i, fdmnt, ret = 0;
55         DIR     *dirstream = NULL;
56         int discard = 1;
57         int force = 0;
58         int last_dev;
59
60         while (1) {
61                 int c;
62                 static const struct option long_options[] = {
63                         { "nodiscard", optional_argument, NULL, 'K'},
64                         { "force", no_argument, NULL, 'f'},
65                         { NULL, 0, NULL, 0}
66                 };
67
68                 c = getopt_long(argc, argv, "Kf", long_options, NULL);
69                 if (c < 0)
70                         break;
71                 switch (c) {
72                 case 'K':
73                         discard = 0;
74                         break;
75                 case 'f':
76                         force = 1;
77                         break;
78                 default:
79                         usage(cmd_device_add_usage);
80                 }
81         }
82
83         if (check_argc_min(argc - optind, 2))
84                 usage(cmd_device_add_usage);
85
86         last_dev = argc - 1;
87         mntpnt = argv[last_dev];
88
89         fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
90         if (fdmnt < 0)
91                 return 1;
92
93         for (i = optind; i < last_dev; i++){
94                 struct btrfs_ioctl_vol_args ioctl_args;
95                 int     devfd, res;
96                 u64 dev_block_count = 0;
97                 char *path;
98
99                 res = test_dev_for_mkfs(argv[i], force);
100                 if (res) {
101                         ret++;
102                         continue;
103                 }
104
105                 devfd = open(argv[i], O_RDWR);
106                 if (devfd < 0) {
107                         error("unable to open device '%s'", argv[i]);
108                         ret++;
109                         continue;
110                 }
111
112                 res = btrfs_prepare_device(devfd, argv[i], &dev_block_count, 0,
113                                 PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE |
114                                 (discard ? PREP_DEVICE_DISCARD : 0));
115                 close(devfd);
116                 if (res) {
117                         ret++;
118                         goto error_out;
119                 }
120
121                 path = canonicalize_path(argv[i]);
122                 if (!path) {
123                         error("could not canonicalize pathname '%s': %s",
124                                 argv[i], strerror(errno));
125                         ret++;
126                         goto error_out;
127                 }
128
129                 memset(&ioctl_args, 0, sizeof(ioctl_args));
130                 strncpy_null(ioctl_args.name, path);
131                 res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
132                 if (res < 0) {
133                         error("error adding device '%s': %s",
134                                 path, strerror(errno));
135                         ret++;
136                 }
137                 free(path);
138         }
139
140 error_out:
141         close_file_or_dir(fdmnt, dirstream);
142         return !!ret;
143 }
144
145 static int _cmd_device_remove(int argc, char **argv,
146                 const char * const *usagestr)
147 {
148         char    *mntpnt;
149         int i, fdmnt, ret = 0;
150         DIR     *dirstream = NULL;
151
152         clean_args_no_options(argc, argv, usagestr);
153
154         if (check_argc_min(argc - optind, 2))
155                 usage(usagestr);
156
157         mntpnt = argv[argc - 1];
158
159         fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
160         if (fdmnt < 0)
161                 return 1;
162
163         for(i = optind; i < argc - 1; i++) {
164                 struct  btrfs_ioctl_vol_args arg;
165                 struct btrfs_ioctl_vol_args_v2 argv2 = {0};
166                 int is_devid = 0;
167                 int     res;
168
169                 if (string_is_numerical(argv[i])) {
170                         argv2.devid = arg_strtou64(argv[i]);
171                         argv2.flags = BTRFS_DEVICE_SPEC_BY_ID;
172                         is_devid = 1;
173                 } else if (is_block_device(argv[i]) == 1 ||
174                                 strcmp(argv[i], "missing") == 0) {
175                         strncpy_null(argv2.name, argv[i]);
176                 } else {
177                         error("not a block device: %s", argv[i]);
178                         ret++;
179                         continue;
180                 }
181
182                 /*
183                  * Positive values are from BTRFS_ERROR_DEV_*,
184                  * otherwise it's a generic error, one of errnos
185                  */
186                 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2);
187
188                 /*
189                  * If BTRFS_IOC_RM_DEV_V2 is not supported we get ENOTTY and if
190                  * argv2.flags includes a flag which kernel doesn't understand then
191                  * we shall get EOPNOTSUPP
192                  */
193                 if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) {
194                         if (is_devid) {
195                                 error("device delete by id failed: %s",
196                                                         strerror(errno));
197                                 ret++;
198                                 continue;
199                         }
200                         memset(&arg, 0, sizeof(arg));
201                         strncpy_null(arg.name, argv[i]);
202                         res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
203                 }
204
205                 if (res) {
206                         const char *msg;
207
208                         if (res > 0)
209                                 msg = btrfs_err_str(res);
210                         else
211                                 msg = strerror(errno);
212                         if (is_devid) {
213                                 error("error removing devid %llu: %s",
214                                         (unsigned long long)argv2.devid, msg);
215                         } else {
216                                 error("error removing device '%s': %s",
217                                         argv[i], msg);
218                         }
219                         ret++;
220                 }
221         }
222
223         close_file_or_dir(fdmnt, dirstream);
224         return !!ret;
225 }
226
227 #define COMMON_USAGE_REMOVE_DELETE                                      \
228         "If 'missing' is specified for <device>, the first device that is",     \
229         "described by the filesystem metadata, but not present at the mount",   \
230         "time will be removed. (only in degraded mode)"
231
232 static const char * const cmd_device_remove_usage[] = {
233         "btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
234         "Remove a device from a filesystem",
235         "",
236         COMMON_USAGE_REMOVE_DELETE,
237         NULL
238 };
239
240 static int cmd_device_remove(int argc, char **argv)
241 {
242         return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
243 }
244
245 static const char * const cmd_device_delete_usage[] = {
246         "btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
247         "Remove a device from a filesystem (alias of \"btrfs device remove\")",
248         "",
249         COMMON_USAGE_REMOVE_DELETE,
250         NULL
251 };
252
253 static int cmd_device_delete(int argc, char **argv)
254 {
255         return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
256 }
257
258 static const char * const cmd_device_scan_usage[] = {
259         "btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
260         "Scan devices for a btrfs filesystem",
261         " -d|--all-devices (deprecated)",
262         NULL
263 };
264
265 static int cmd_device_scan(int argc, char **argv)
266 {
267         int i;
268         int devstart;
269         int all = 0;
270         int ret = 0;
271
272         while (1) {
273                 int c;
274                 static const struct option long_options[] = {
275                         { "all-devices", no_argument, NULL, 'd'},
276                         { NULL, 0, NULL, 0}
277                 };
278
279                 c = getopt_long(argc, argv, "d", long_options, NULL);
280                 if (c < 0)
281                         break;
282                 switch (c) {
283                 case 'd':
284                         all = 1;
285                         break;
286                 default:
287                         usage(cmd_device_scan_usage);
288                 }
289         }
290         devstart = optind;
291
292         if (all && check_argc_max(argc - optind, 1))
293                 usage(cmd_device_scan_usage);
294
295         if (all || argc - optind == 0) {
296                 printf("Scanning for Btrfs filesystems\n");
297                 ret = btrfs_scan_devices();
298                 error_on(ret, "error %d while scanning", ret);
299                 ret = btrfs_register_all_devices();
300                 error_on(ret, "there are %d errors while registering devices", ret);
301                 goto out;
302         }
303
304         for( i = devstart ; i < argc ; i++ ){
305                 char *path;
306
307                 if (is_block_device(argv[i]) != 1) {
308                         error("not a block device: %s", argv[i]);
309                         ret = 1;
310                         goto out;
311                 }
312                 path = canonicalize_path(argv[i]);
313                 if (!path) {
314                         error("could not canonicalize path '%s': %s",
315                                 argv[i], strerror(errno));
316                         ret = 1;
317                         goto out;
318                 }
319                 printf("Scanning for Btrfs filesystems in '%s'\n", path);
320                 if (btrfs_register_one_device(path) != 0) {
321                         ret = 1;
322                         free(path);
323                         goto out;
324                 }
325                 free(path);
326         }
327
328 out:
329         return !!ret;
330 }
331
332 static const char * const cmd_device_ready_usage[] = {
333         "btrfs device ready <device>",
334         "Check device to see if it has all of its devices in cache for mounting",
335         NULL
336 };
337
338 static int cmd_device_ready(int argc, char **argv)
339 {
340         struct  btrfs_ioctl_vol_args args;
341         int     fd;
342         int     ret;
343         char    *path;
344
345         clean_args_no_options(argc, argv, cmd_device_ready_usage);
346
347         if (check_argc_exact(argc - optind, 1))
348                 usage(cmd_device_ready_usage);
349
350         fd = open("/dev/btrfs-control", O_RDWR);
351         if (fd < 0) {
352                 perror("failed to open /dev/btrfs-control");
353                 return 1;
354         }
355
356         path = canonicalize_path(argv[optind]);
357         if (!path) {
358                 error("could not canonicalize pathname '%s': %s",
359                         argv[optind], strerror(errno));
360                 ret = 1;
361                 goto out;
362         }
363
364         if (is_block_device(path) != 1) {
365                 error("not a block device: %s", path);
366                 ret = 1;
367                 goto out;
368         }
369
370         memset(&args, 0, sizeof(args));
371         strncpy_null(args.name, path);
372         ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
373         if (ret < 0) {
374                 error("unable to determine if device '%s' is ready for mount: %s",
375                         path, strerror(errno));
376                 ret = 1;
377         }
378
379 out:
380         free(path);
381         close(fd);
382         return ret;
383 }
384
385 static const char * const cmd_device_stats_usage[] = {
386         "btrfs device stats [options] <path>|<device>",
387         "Show device IO error statistics",
388         "Show device IO error statistics for all devices of the given filesystem",
389         "identified by PATH or DEVICE. The filesystem must be mounted.",
390         "",
391         "-c|--check             return non-zero if any stat counter is not zero",
392         "-z|--reset             show current stats and reset values to zero",
393         NULL
394 };
395
396 static int cmd_device_stats(int argc, char **argv)
397 {
398         char *dev_path;
399         struct btrfs_ioctl_fs_info_args fi_args;
400         struct btrfs_ioctl_dev_info_args *di_args = NULL;
401         int ret;
402         int fdmnt;
403         int i;
404         int err = 0;
405         int check = 0;
406         __u64 flags = 0;
407         DIR *dirstream = NULL;
408
409         while (1) {
410                 int c;
411                 static const struct option long_options[] = {
412                         {"check", no_argument, NULL, 'c'},
413                         {"reset", no_argument, NULL, 'z'},
414                         {NULL, 0, NULL, 0}
415                 };
416
417                 c = getopt_long(argc, argv, "cz", long_options, NULL);
418                 if (c < 0)
419                         break;
420
421                 switch (c) {
422                 case 'c':
423                         check = 1;
424                         break;
425                 case 'z':
426                         flags = BTRFS_DEV_STATS_RESET;
427                         break;
428                 case '?':
429                 default:
430                         usage(cmd_device_stats_usage);
431                 }
432         }
433
434         if (check_argc_exact(argc - optind, 1))
435                 usage(cmd_device_stats_usage);
436
437         dev_path = argv[optind];
438
439         fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1);
440         if (fdmnt < 0)
441                 return 1;
442
443         ret = get_fs_info(dev_path, &fi_args, &di_args);
444         if (ret) {
445                 error("getting device info for %s failed: %s", dev_path,
446                         strerror(-ret));
447                 err = 1;
448                 goto out;
449         }
450         if (!fi_args.num_devices) {
451                 error("no devices found");
452                 err = 1;
453                 goto out;
454         }
455
456         for (i = 0; i < fi_args.num_devices; i++) {
457                 struct btrfs_ioctl_get_dev_stats args = {0};
458                 char path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
459
460                 strncpy(path, (char *)di_args[i].path,
461                         BTRFS_DEVICE_PATH_NAME_MAX);
462                 path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
463
464                 args.devid = di_args[i].devid;
465                 args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
466                 args.flags = flags;
467
468                 if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
469                         error("device stats ioctl failed on %s: %s",
470                               path, strerror(errno));
471                         err |= 1;
472                 } else {
473                         char *canonical_path;
474                         int j;
475                         static const struct {
476                                 const char name[32];
477                                 u64 num;
478                         } dev_stats[] = {
479                                 { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS },
480                                 { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS },
481                                 { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS },
482                                 { "corruption_errs",
483                                         BTRFS_DEV_STAT_CORRUPTION_ERRS },
484                                 { "generation_errs",
485                                         BTRFS_DEV_STAT_GENERATION_ERRS },
486                         };
487
488                         canonical_path = canonicalize_path(path);
489
490                         /* No path when device is missing. */
491                         if (!canonical_path) {
492                                 canonical_path = malloc(32);
493                                 if (!canonical_path) {
494                                         error("not enough memory for path buffer");
495                                         goto out;
496                                 }
497                                 snprintf(canonical_path, 32,
498                                          "devid:%llu", args.devid);
499                         }
500
501                         for (j = 0; j < ARRAY_SIZE(dev_stats); j++) {
502                                 /* We got fewer items than we know */
503                                 if (args.nr_items < dev_stats[j].num + 1)
504                                         continue;
505                                 printf("[%s].%-16s %llu\n", canonical_path,
506                                         dev_stats[j].name,
507                                         (unsigned long long)
508                                          args.values[dev_stats[j].num]);
509                                 if ((check == 1)
510                                     && (args.values[dev_stats[j].num] > 0))
511                                         err |= 64;
512                         }
513
514                         free(canonical_path);
515                 }
516         }
517
518 out:
519         free(di_args);
520         close_file_or_dir(fdmnt, dirstream);
521
522         return err;
523 }
524
525 static const char * const cmd_device_usage_usage[] = {
526         "btrfs device usage [options] <path> [<path>..]",
527         "Show detailed information about internal allocations in devices.",
528         HELPINFO_UNITS_SHORT_LONG,
529         NULL
530 };
531
532 static int _cmd_device_usage(int fd, char *path, unsigned unit_mode)
533 {
534         int i;
535         int ret = 0;
536         struct chunk_info *chunkinfo = NULL;
537         struct device_info *devinfo = NULL;
538         int chunkcount = 0;
539         int devcount = 0;
540
541         ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount, &devinfo,
542                         &devcount);
543         if (ret)
544                 goto out;
545
546         for (i = 0; i < devcount; i++) {
547                 printf("%s, ID: %llu\n", devinfo[i].path, devinfo[i].devid);
548                 print_device_sizes(&devinfo[i], unit_mode);
549                 print_device_chunks(&devinfo[i], chunkinfo, chunkcount,
550                                 unit_mode);
551                 printf("\n");
552         }
553
554 out:
555         free(devinfo);
556         free(chunkinfo);
557
558         return ret;
559 }
560
561 static int cmd_device_usage(int argc, char **argv)
562 {
563         unsigned unit_mode;
564         int ret = 0;
565         int i;
566
567         unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
568
569         clean_args_no_options(argc, argv, cmd_device_usage_usage);
570
571         if (check_argc_min(argc - optind, 1))
572                 usage(cmd_device_usage_usage);
573
574         for (i = optind; i < argc; i++) {
575                 int fd;
576                 DIR *dirstream = NULL;
577
578                 if (i > 1)
579                         printf("\n");
580
581                 fd = btrfs_open_dir(argv[i], &dirstream, 1);
582                 if (fd < 0) {
583                         ret = 1;
584                         break;
585                 }
586
587                 ret = _cmd_device_usage(fd, argv[i], unit_mode);
588                 close_file_or_dir(fd, dirstream);
589
590                 if (ret)
591                         break;
592         }
593
594         return !!ret;
595 }
596
597 static const char device_cmd_group_info[] =
598 "manage and query devices in the filesystem";
599
600 const struct cmd_group device_cmd_group = {
601         device_cmd_group_usage, device_cmd_group_info, {
602                 { "add", cmd_device_add, cmd_device_add_usage, NULL, 0 },
603                 { "delete", cmd_device_delete, cmd_device_delete_usage, NULL,
604                         CMD_ALIAS },
605                 { "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 },
606                 { "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 },
607                 { "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 },
608                 { "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 },
609                 { "usage", cmd_device_usage,
610                         cmd_device_usage_usage, NULL, 0 },
611                 NULL_CMD_STRUCT
612         }
613 };
614
615 int cmd_device(int argc, char **argv)
616 {
617         return handle_command_group(&device_cmd_group, argc, argv);
618 }