Btrfs progs v4.16.1
[btrfs-progs-unstable/devel.git] / cmds-device.c
blob86459d1b956430585c5649986dbd346a16cb6409
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.
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.
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.
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>
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"
34 #include "commands.h"
35 #include "help.h"
36 #include "mkfs/common.h"
38 static const char * const device_cmd_group_usage[] = {
39 "btrfs device <command> [<args>]",
40 NULL
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
51 static int cmd_device_add(int argc, char **argv)
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;
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}
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);
83 if (check_argc_min(argc - optind, 2))
84 usage(cmd_device_add_usage);
86 last_dev = argc - 1;
87 mntpnt = argv[last_dev];
89 fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
90 if (fdmnt < 0)
91 return 1;
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;
99 res = test_dev_for_mkfs(argv[i], force);
100 if (res) {
101 ret++;
102 continue;
105 devfd = open(argv[i], O_RDWR);
106 if (devfd < 0) {
107 error("unable to open device '%s'", argv[i]);
108 ret++;
109 continue;
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;
121 path = canonicalize_path(argv[i]);
122 if (!path) {
123 error("could not canonicalize pathname '%s': %m",
124 argv[i]);
125 ret++;
126 goto error_out;
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': %m", path);
134 ret++;
136 free(path);
139 error_out:
140 close_file_or_dir(fdmnt, dirstream);
141 return !!ret;
144 static int _cmd_device_remove(int argc, char **argv,
145 const char * const *usagestr)
147 char *mntpnt;
148 int i, fdmnt, ret = 0;
149 DIR *dirstream = NULL;
151 clean_args_no_options(argc, argv, usagestr);
153 if (check_argc_min(argc - optind, 2))
154 usage(usagestr);
156 mntpnt = argv[argc - 1];
158 fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
159 if (fdmnt < 0)
160 return 1;
162 for(i = optind; i < argc - 1; i++) {
163 struct btrfs_ioctl_vol_args arg;
164 struct btrfs_ioctl_vol_args_v2 argv2 = {0};
165 int is_devid = 0;
166 int res;
168 if (string_is_numerical(argv[i])) {
169 argv2.devid = arg_strtou64(argv[i]);
170 argv2.flags = BTRFS_DEVICE_SPEC_BY_ID;
171 is_devid = 1;
172 } else if (is_block_device(argv[i]) == 1 ||
173 strcmp(argv[i], "missing") == 0) {
174 strncpy_null(argv2.name, argv[i]);
175 } else {
176 error("not a block device: %s", argv[i]);
177 ret++;
178 continue;
182 * Positive values are from BTRFS_ERROR_DEV_*,
183 * otherwise it's a generic error, one of errnos
185 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2);
188 * If BTRFS_IOC_RM_DEV_V2 is not supported we get ENOTTY and if
189 * argv2.flags includes a flag which kernel doesn't understand then
190 * we shall get EOPNOTSUPP
192 if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) {
193 if (is_devid) {
194 error("device delete by id failed: %m");
195 ret++;
196 continue;
198 memset(&arg, 0, sizeof(arg));
199 strncpy_null(arg.name, argv[i]);
200 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
203 if (res) {
204 const char *msg;
206 if (res > 0)
207 msg = btrfs_err_str(res);
208 else
209 msg = strerror(errno);
210 if (is_devid) {
211 error("error removing devid %llu: %s",
212 (unsigned long long)argv2.devid, msg);
213 } else {
214 error("error removing device '%s': %s",
215 argv[i], msg);
217 ret++;
221 close_file_or_dir(fdmnt, dirstream);
222 return !!ret;
225 #define COMMON_USAGE_REMOVE_DELETE \
226 "If 'missing' is specified for <device>, the first device that is", \
227 "described by the filesystem metadata, but not present at the mount", \
228 "time will be removed. (only in degraded mode)"
230 static const char * const cmd_device_remove_usage[] = {
231 "btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
232 "Remove a device from a filesystem",
234 COMMON_USAGE_REMOVE_DELETE,
235 NULL
238 static int cmd_device_remove(int argc, char **argv)
240 return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
243 static const char * const cmd_device_delete_usage[] = {
244 "btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
245 "Remove a device from a filesystem (alias of \"btrfs device remove\")",
247 COMMON_USAGE_REMOVE_DELETE,
248 NULL
251 static int cmd_device_delete(int argc, char **argv)
253 return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
256 static const char * const cmd_device_scan_usage[] = {
257 "btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
258 "Scan devices for a btrfs filesystem",
259 " -d|--all-devices (deprecated)",
260 NULL
263 static int cmd_device_scan(int argc, char **argv)
265 int i;
266 int devstart;
267 int all = 0;
268 int ret = 0;
270 while (1) {
271 int c;
272 static const struct option long_options[] = {
273 { "all-devices", no_argument, NULL, 'd'},
274 { NULL, 0, NULL, 0}
277 c = getopt_long(argc, argv, "d", long_options, NULL);
278 if (c < 0)
279 break;
280 switch (c) {
281 case 'd':
282 all = 1;
283 break;
284 default:
285 usage(cmd_device_scan_usage);
288 devstart = optind;
290 if (all && check_argc_max(argc - optind, 1))
291 usage(cmd_device_scan_usage);
293 if (all || argc - optind == 0) {
294 printf("Scanning for Btrfs filesystems\n");
295 ret = btrfs_scan_devices();
296 error_on(ret, "error %d while scanning", ret);
297 ret = btrfs_register_all_devices();
298 error_on(ret, "there are %d errors while registering devices", ret);
299 goto out;
302 for( i = devstart ; i < argc ; i++ ){
303 char *path;
305 if (is_block_device(argv[i]) != 1) {
306 error("not a block device: %s", argv[i]);
307 ret = 1;
308 goto out;
310 path = canonicalize_path(argv[i]);
311 if (!path) {
312 error("could not canonicalize path '%s': %m", argv[i]);
313 ret = 1;
314 goto out;
316 printf("Scanning for Btrfs filesystems in '%s'\n", path);
317 if (btrfs_register_one_device(path) != 0) {
318 ret = 1;
319 free(path);
320 goto out;
322 free(path);
325 out:
326 return !!ret;
329 static const char * const cmd_device_ready_usage[] = {
330 "btrfs device ready <device>",
331 "Check device to see if it has all of its devices in cache for mounting",
332 NULL
335 static int cmd_device_ready(int argc, char **argv)
337 struct btrfs_ioctl_vol_args args;
338 int fd;
339 int ret;
340 char *path;
342 clean_args_no_options(argc, argv, cmd_device_ready_usage);
344 if (check_argc_exact(argc - optind, 1))
345 usage(cmd_device_ready_usage);
347 fd = open("/dev/btrfs-control", O_RDWR);
348 if (fd < 0) {
349 perror("failed to open /dev/btrfs-control");
350 return 1;
353 path = canonicalize_path(argv[optind]);
354 if (!path) {
355 error("could not canonicalize pathname '%s': %m",
356 argv[optind]);
357 ret = 1;
358 goto out;
361 if (is_block_device(path) != 1) {
362 error("not a block device: %s", path);
363 ret = 1;
364 goto out;
367 memset(&args, 0, sizeof(args));
368 strncpy_null(args.name, path);
369 ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
370 if (ret < 0) {
371 error("unable to determine if device '%s' is ready for mount: %m",
372 path);
373 ret = 1;
376 out:
377 free(path);
378 close(fd);
379 return ret;
382 static const char * const cmd_device_stats_usage[] = {
383 "btrfs device stats [options] <path>|<device>",
384 "Show device IO error statistics",
385 "Show device IO error statistics for all devices of the given filesystem",
386 "identified by PATH or DEVICE. The filesystem must be mounted.",
388 "-c|--check return non-zero if any stat counter is not zero",
389 "-z|--reset show current stats and reset values to zero",
390 NULL
393 static int cmd_device_stats(int argc, char **argv)
395 char *dev_path;
396 struct btrfs_ioctl_fs_info_args fi_args;
397 struct btrfs_ioctl_dev_info_args *di_args = NULL;
398 int ret;
399 int fdmnt;
400 int i;
401 int err = 0;
402 int check = 0;
403 __u64 flags = 0;
404 DIR *dirstream = NULL;
406 while (1) {
407 int c;
408 static const struct option long_options[] = {
409 {"check", no_argument, NULL, 'c'},
410 {"reset", no_argument, NULL, 'z'},
411 {NULL, 0, NULL, 0}
414 c = getopt_long(argc, argv, "cz", long_options, NULL);
415 if (c < 0)
416 break;
418 switch (c) {
419 case 'c':
420 check = 1;
421 break;
422 case 'z':
423 flags = BTRFS_DEV_STATS_RESET;
424 break;
425 case '?':
426 default:
427 usage(cmd_device_stats_usage);
431 if (check_argc_exact(argc - optind, 1))
432 usage(cmd_device_stats_usage);
434 dev_path = argv[optind];
436 fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1);
437 if (fdmnt < 0)
438 return 1;
440 ret = get_fs_info(dev_path, &fi_args, &di_args);
441 if (ret) {
442 error("getting device info for %s failed: %s", dev_path,
443 strerror(-ret));
444 err = 1;
445 goto out;
447 if (!fi_args.num_devices) {
448 error("no devices found");
449 err = 1;
450 goto out;
453 for (i = 0; i < fi_args.num_devices; i++) {
454 struct btrfs_ioctl_get_dev_stats args = {0};
455 char path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
457 strncpy(path, (char *)di_args[i].path,
458 BTRFS_DEVICE_PATH_NAME_MAX);
459 path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
461 args.devid = di_args[i].devid;
462 args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
463 args.flags = flags;
465 if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
466 error("device stats ioctl failed on %s: %m",
467 path);
468 err |= 1;
469 } else {
470 char *canonical_path;
471 int j;
472 static const struct {
473 const char name[32];
474 u64 num;
475 } dev_stats[] = {
476 { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS },
477 { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS },
478 { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS },
479 { "corruption_errs",
480 BTRFS_DEV_STAT_CORRUPTION_ERRS },
481 { "generation_errs",
482 BTRFS_DEV_STAT_GENERATION_ERRS },
485 canonical_path = canonicalize_path(path);
487 /* No path when device is missing. */
488 if (!canonical_path) {
489 canonical_path = malloc(32);
490 if (!canonical_path) {
491 error("not enough memory for path buffer");
492 goto out;
494 snprintf(canonical_path, 32,
495 "devid:%llu", args.devid);
498 for (j = 0; j < ARRAY_SIZE(dev_stats); j++) {
499 /* We got fewer items than we know */
500 if (args.nr_items < dev_stats[j].num + 1)
501 continue;
502 printf("[%s].%-16s %llu\n", canonical_path,
503 dev_stats[j].name,
504 (unsigned long long)
505 args.values[dev_stats[j].num]);
506 if ((check == 1)
507 && (args.values[dev_stats[j].num] > 0))
508 err |= 64;
511 free(canonical_path);
515 out:
516 free(di_args);
517 close_file_or_dir(fdmnt, dirstream);
519 return err;
522 static const char * const cmd_device_usage_usage[] = {
523 "btrfs device usage [options] <path> [<path>..]",
524 "Show detailed information about internal allocations in devices.",
525 HELPINFO_UNITS_SHORT_LONG,
526 NULL
529 static int _cmd_device_usage(int fd, char *path, unsigned unit_mode)
531 int i;
532 int ret = 0;
533 struct chunk_info *chunkinfo = NULL;
534 struct device_info *devinfo = NULL;
535 int chunkcount = 0;
536 int devcount = 0;
538 ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount, &devinfo,
539 &devcount);
540 if (ret)
541 goto out;
543 for (i = 0; i < devcount; i++) {
544 printf("%s, ID: %llu\n", devinfo[i].path, devinfo[i].devid);
545 print_device_sizes(&devinfo[i], unit_mode);
546 print_device_chunks(&devinfo[i], chunkinfo, chunkcount,
547 unit_mode);
548 printf("\n");
551 out:
552 free(devinfo);
553 free(chunkinfo);
555 return ret;
558 static int cmd_device_usage(int argc, char **argv)
560 unsigned unit_mode;
561 int ret = 0;
562 int i;
564 unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
566 clean_args_no_options(argc, argv, cmd_device_usage_usage);
568 if (check_argc_min(argc - optind, 1))
569 usage(cmd_device_usage_usage);
571 for (i = optind; i < argc; i++) {
572 int fd;
573 DIR *dirstream = NULL;
575 if (i > 1)
576 printf("\n");
578 fd = btrfs_open_dir(argv[i], &dirstream, 1);
579 if (fd < 0) {
580 ret = 1;
581 break;
584 ret = _cmd_device_usage(fd, argv[i], unit_mode);
585 close_file_or_dir(fd, dirstream);
587 if (ret)
588 break;
591 return !!ret;
594 static const char device_cmd_group_info[] =
595 "manage and query devices in the filesystem";
597 const struct cmd_group device_cmd_group = {
598 device_cmd_group_usage, device_cmd_group_info, {
599 { "add", cmd_device_add, cmd_device_add_usage, NULL, 0 },
600 { "delete", cmd_device_delete, cmd_device_delete_usage, NULL,
601 CMD_ALIAS },
602 { "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 },
603 { "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 },
604 { "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 },
605 { "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 },
606 { "usage", cmd_device_usage,
607 cmd_device_usage_usage, NULL, 0 },
608 NULL_CMD_STRUCT
612 int cmd_device(int argc, char **argv)
614 return handle_command_group(&device_cmd_group, argc, argv);