btrfs-progs: tests/fuzz: Add image for bko-200409
[btrfs-progs-unstable/devel.git] / cmds-device.c
blob2a05f70a76a9cdaa610ac235045e22181d61830a
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 optind = 0;
61 while (1) {
62 int c;
63 static const struct option long_options[] = {
64 { "nodiscard", optional_argument, NULL, 'K'},
65 { "force", no_argument, NULL, 'f'},
66 { NULL, 0, NULL, 0}
69 c = getopt_long(argc, argv, "Kf", long_options, NULL);
70 if (c < 0)
71 break;
72 switch (c) {
73 case 'K':
74 discard = 0;
75 break;
76 case 'f':
77 force = 1;
78 break;
79 default:
80 usage(cmd_device_add_usage);
84 if (check_argc_min(argc - optind, 2))
85 usage(cmd_device_add_usage);
87 last_dev = argc - 1;
88 mntpnt = argv[last_dev];
90 fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
91 if (fdmnt < 0)
92 return 1;
94 for (i = optind; i < last_dev; i++){
95 struct btrfs_ioctl_vol_args ioctl_args;
96 int devfd, res;
97 u64 dev_block_count = 0;
98 char *path;
100 res = test_dev_for_mkfs(argv[i], force);
101 if (res) {
102 ret++;
103 continue;
106 devfd = open(argv[i], O_RDWR);
107 if (devfd < 0) {
108 error("unable to open device '%s'", argv[i]);
109 ret++;
110 continue;
113 res = btrfs_prepare_device(devfd, argv[i], &dev_block_count, 0,
114 PREP_DEVICE_ZERO_END | PREP_DEVICE_VERBOSE |
115 (discard ? PREP_DEVICE_DISCARD : 0));
116 close(devfd);
117 if (res) {
118 ret++;
119 goto error_out;
122 path = canonicalize_path(argv[i]);
123 if (!path) {
124 error("could not canonicalize pathname '%s': %m",
125 argv[i]);
126 ret++;
127 goto error_out;
130 memset(&ioctl_args, 0, sizeof(ioctl_args));
131 strncpy_null(ioctl_args.name, path);
132 res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
133 if (res < 0) {
134 error("error adding device '%s': %m", path);
135 ret++;
137 free(path);
140 error_out:
141 close_file_or_dir(fdmnt, dirstream);
142 return !!ret;
145 static int _cmd_device_remove(int argc, char **argv,
146 const char * const *usagestr)
148 char *mntpnt;
149 int i, fdmnt, ret = 0;
150 DIR *dirstream = NULL;
152 clean_args_no_options(argc, argv, usagestr);
154 if (check_argc_min(argc - optind, 2))
155 usage(usagestr);
157 mntpnt = argv[argc - 1];
159 fdmnt = btrfs_open_dir(mntpnt, &dirstream, 1);
160 if (fdmnt < 0)
161 return 1;
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;
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;
183 * Positive values are from BTRFS_ERROR_DEV_*,
184 * otherwise it's a generic error, one of errnos
186 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV_V2, &argv2);
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
193 if (res < 0 && (errno == ENOTTY || errno == EOPNOTSUPP)) {
194 if (is_devid) {
195 error("device delete by id failed: %m");
196 ret++;
197 continue;
199 memset(&arg, 0, sizeof(arg));
200 strncpy_null(arg.name, argv[i]);
201 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
204 if (res) {
205 const char *msg;
207 if (res > 0)
208 msg = btrfs_err_str(res);
209 else
210 msg = strerror(errno);
211 if (is_devid) {
212 error("error removing devid %llu: %s",
213 (unsigned long long)argv2.devid, msg);
214 } else {
215 error("error removing device '%s': %s",
216 argv[i], msg);
218 ret++;
222 close_file_or_dir(fdmnt, dirstream);
223 return !!ret;
226 #define COMMON_USAGE_REMOVE_DELETE \
227 "If 'missing' is specified for <device>, the first device that is", \
228 "described by the filesystem metadata, but not present at the mount", \
229 "time will be removed. (only in degraded mode)"
231 static const char * const cmd_device_remove_usage[] = {
232 "btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
233 "Remove a device from a filesystem",
235 COMMON_USAGE_REMOVE_DELETE,
236 NULL
239 static int cmd_device_remove(int argc, char **argv)
241 return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
244 static const char * const cmd_device_delete_usage[] = {
245 "btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
246 "Remove a device from a filesystem (alias of \"btrfs device remove\")",
248 COMMON_USAGE_REMOVE_DELETE,
249 NULL
252 static int cmd_device_delete(int argc, char **argv)
254 return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
257 static const char * const cmd_device_scan_usage[] = {
258 "btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
259 "Scan devices for a btrfs filesystem",
260 " -d|--all-devices (deprecated)",
261 NULL
264 static int cmd_device_scan(int argc, char **argv)
266 int i;
267 int devstart;
268 int all = 0;
269 int ret = 0;
271 optind = 0;
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}
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);
290 devstart = optind;
292 if (all && check_argc_max(argc - optind, 1))
293 usage(cmd_device_scan_usage);
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;
304 for( i = devstart ; i < argc ; i++ ){
305 char *path;
307 if (is_block_device(argv[i]) != 1) {
308 error("not a block device: %s", argv[i]);
309 ret = 1;
310 goto out;
312 path = canonicalize_path(argv[i]);
313 if (!path) {
314 error("could not canonicalize path '%s': %m", argv[i]);
315 ret = 1;
316 goto out;
318 printf("Scanning for Btrfs filesystems in '%s'\n", path);
319 if (btrfs_register_one_device(path) != 0) {
320 ret = 1;
321 free(path);
322 goto out;
324 free(path);
327 out:
328 return !!ret;
331 static const char * const cmd_device_ready_usage[] = {
332 "btrfs device ready <device>",
333 "Check device to see if it has all of its devices in cache for mounting",
334 NULL
337 static int cmd_device_ready(int argc, char **argv)
339 struct btrfs_ioctl_vol_args args;
340 int fd;
341 int ret;
342 char *path;
344 clean_args_no_options(argc, argv, cmd_device_ready_usage);
346 if (check_argc_exact(argc - optind, 1))
347 usage(cmd_device_ready_usage);
349 fd = open("/dev/btrfs-control", O_RDWR);
350 if (fd < 0) {
351 perror("failed to open /dev/btrfs-control");
352 return 1;
355 path = canonicalize_path(argv[optind]);
356 if (!path) {
357 error("could not canonicalize pathname '%s': %m",
358 argv[optind]);
359 ret = 1;
360 goto out;
363 if (is_block_device(path) != 1) {
364 error("not a block device: %s", path);
365 ret = 1;
366 goto out;
369 memset(&args, 0, sizeof(args));
370 strncpy_null(args.name, path);
371 ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
372 if (ret < 0) {
373 error("unable to determine if device '%s' is ready for mount: %m",
374 path);
375 ret = 1;
378 out:
379 free(path);
380 close(fd);
381 return ret;
384 static const char * const cmd_device_stats_usage[] = {
385 "btrfs device stats [options] <path>|<device>",
386 "Show device IO error statistics",
387 "Show device IO error statistics for all devices of the given filesystem",
388 "identified by PATH or DEVICE. The filesystem must be mounted.",
390 "-c|--check return non-zero if any stat counter is not zero",
391 "-z|--reset show current stats and reset values to zero",
392 NULL
395 static int cmd_device_stats(int argc, char **argv)
397 char *dev_path;
398 struct btrfs_ioctl_fs_info_args fi_args;
399 struct btrfs_ioctl_dev_info_args *di_args = NULL;
400 int ret;
401 int fdmnt;
402 int i;
403 int err = 0;
404 int check = 0;
405 __u64 flags = 0;
406 DIR *dirstream = NULL;
408 optind = 0;
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}
417 c = getopt_long(argc, argv, "cz", long_options, NULL);
418 if (c < 0)
419 break;
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);
434 if (check_argc_exact(argc - optind, 1))
435 usage(cmd_device_stats_usage);
437 dev_path = argv[optind];
439 fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1);
440 if (fdmnt < 0)
441 return 1;
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;
450 if (!fi_args.num_devices) {
451 error("no devices found");
452 err = 1;
453 goto out;
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];
460 strncpy(path, (char *)di_args[i].path,
461 BTRFS_DEVICE_PATH_NAME_MAX);
462 path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
464 args.devid = di_args[i].devid;
465 args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
466 args.flags = flags;
468 if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
469 error("device stats ioctl failed on %s: %m",
470 path);
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 },
488 canonical_path = canonicalize_path(path);
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;
497 snprintf(canonical_path, 32,
498 "devid:%llu", args.devid);
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;
514 free(canonical_path);
518 out:
519 free(di_args);
520 close_file_or_dir(fdmnt, dirstream);
522 return err;
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
532 static int _cmd_device_usage(int fd, const char *path, unsigned unit_mode)
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;
541 ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount, &devinfo,
542 &devcount);
543 if (ret)
544 goto out;
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");
554 out:
555 free(devinfo);
556 free(chunkinfo);
558 return ret;
561 static int cmd_device_usage(int argc, char **argv)
563 unsigned unit_mode;
564 int ret = 0;
565 int i;
567 unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
569 clean_args_no_options(argc, argv, cmd_device_usage_usage);
571 if (check_argc_min(argc - optind, 1))
572 usage(cmd_device_usage_usage);
574 for (i = optind; i < argc; i++) {
575 int fd;
576 DIR *dirstream = NULL;
578 if (i > 1)
579 printf("\n");
581 fd = btrfs_open_dir(argv[i], &dirstream, 1);
582 if (fd < 0) {
583 ret = 1;
584 break;
587 ret = _cmd_device_usage(fd, argv[i], unit_mode);
588 close_file_or_dir(fd, dirstream);
590 if (ret)
591 break;
594 return !!ret;
597 static const char device_cmd_group_info[] =
598 "manage and query devices in the filesystem";
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
615 int cmd_device(int argc, char **argv)
617 return handle_command_group(&device_cmd_group, argc, argv);