btrfs-progs: move utils code out of header
[btrfs-progs-unstable/devel.git] / cmds-device.c
blob7915bc97fb266c86e2892c25b3e312a7d4ef106d
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': %s",
124 argv[i], strerror(errno));
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': %s",
134 path, strerror(errno));
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: %s",
196 strerror(errno));
197 ret++;
198 continue;
200 memset(&arg, 0, sizeof(arg));
201 strncpy_null(arg.name, argv[i]);
202 res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
205 if (res) {
206 const char *msg;
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);
219 ret++;
223 close_file_or_dir(fdmnt, dirstream);
224 return !!ret;
227 static const char * const cmd_device_remove_usage[] = {
228 "btrfs device remove <device>|<devid> [<device>|<devid>...] <path>",
229 "Remove a device from a filesystem",
230 NULL
233 static int cmd_device_remove(int argc, char **argv)
235 return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
238 static const char * const cmd_device_delete_usage[] = {
239 "btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
240 "Remove a device from a filesystem",
241 NULL
244 static int cmd_device_delete(int argc, char **argv)
246 return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
249 static const char * const cmd_device_scan_usage[] = {
250 "btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
251 "Scan devices for a btrfs filesystem",
252 " -d|--all-devices (deprecated)",
253 NULL
256 static int cmd_device_scan(int argc, char **argv)
258 int i;
259 int devstart;
260 int all = 0;
261 int ret = 0;
263 while (1) {
264 int c;
265 static const struct option long_options[] = {
266 { "all-devices", no_argument, NULL, 'd'},
267 { NULL, 0, NULL, 0}
270 c = getopt_long(argc, argv, "d", long_options, NULL);
271 if (c < 0)
272 break;
273 switch (c) {
274 case 'd':
275 all = 1;
276 break;
277 default:
278 usage(cmd_device_scan_usage);
281 devstart = optind;
283 if (all && check_argc_max(argc - optind, 1))
284 usage(cmd_device_scan_usage);
286 if (all || argc - optind == 0) {
287 printf("Scanning for Btrfs filesystems\n");
288 ret = btrfs_scan_devices();
289 error_on(ret, "error %d while scanning", ret);
290 ret = btrfs_register_all_devices();
291 error_on(ret, "there are %d errors while registering devices", ret);
292 goto out;
295 for( i = devstart ; i < argc ; i++ ){
296 char *path;
298 if (is_block_device(argv[i]) != 1) {
299 error("not a block device: %s", argv[i]);
300 ret = 1;
301 goto out;
303 path = canonicalize_path(argv[i]);
304 if (!path) {
305 error("could not canonicalize path '%s': %s",
306 argv[i], strerror(errno));
307 ret = 1;
308 goto out;
310 printf("Scanning for Btrfs filesystems in '%s'\n", path);
311 if (btrfs_register_one_device(path) != 0) {
312 ret = 1;
313 free(path);
314 goto out;
316 free(path);
319 out:
320 return !!ret;
323 static const char * const cmd_device_ready_usage[] = {
324 "btrfs device ready <device>",
325 "Check device to see if it has all of its devices in cache for mounting",
326 NULL
329 static int cmd_device_ready(int argc, char **argv)
331 struct btrfs_ioctl_vol_args args;
332 int fd;
333 int ret;
334 char *path;
336 clean_args_no_options(argc, argv, cmd_device_ready_usage);
338 if (check_argc_exact(argc - optind, 1))
339 usage(cmd_device_ready_usage);
341 fd = open("/dev/btrfs-control", O_RDWR);
342 if (fd < 0) {
343 perror("failed to open /dev/btrfs-control");
344 return 1;
347 path = canonicalize_path(argv[optind]);
348 if (!path) {
349 error("could not canonicalize pathname '%s': %s",
350 argv[optind], strerror(errno));
351 ret = 1;
352 goto out;
355 if (is_block_device(path) != 1) {
356 error("not a block device: %s", path);
357 ret = 1;
358 goto out;
361 memset(&args, 0, sizeof(args));
362 strncpy_null(args.name, path);
363 ret = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
364 if (ret < 0) {
365 error("unable to determine if device '%s' is ready for mount: %s",
366 path, strerror(errno));
367 ret = 1;
370 out:
371 free(path);
372 close(fd);
373 return ret;
376 static const char * const cmd_device_stats_usage[] = {
377 "btrfs device stats [options] <path>|<device>",
378 "Show device IO error statistics",
379 "Show device IO error statistics for all devices of the given filesystem",
380 "identified by PATH or DEVICE. The filesystem must be mounted.",
382 "-c|--check return non-zero if any stat counter is not zero",
383 "-z|--reset show current stats and reset values to zero",
384 NULL
387 static int cmd_device_stats(int argc, char **argv)
389 char *dev_path;
390 struct btrfs_ioctl_fs_info_args fi_args;
391 struct btrfs_ioctl_dev_info_args *di_args = NULL;
392 int ret;
393 int fdmnt;
394 int i;
395 int err = 0;
396 int check = 0;
397 __u64 flags = 0;
398 DIR *dirstream = NULL;
400 while (1) {
401 int c;
402 static const struct option long_options[] = {
403 {"reset", no_argument, NULL, 'z'},
404 {NULL, 0, NULL, 0}
407 c = getopt_long(argc, argv, "cz", long_options, NULL);
408 if (c < 0)
409 break;
411 switch (c) {
412 case 'c':
413 check = 1;
414 break;
415 case 'z':
416 flags = BTRFS_DEV_STATS_RESET;
417 break;
418 case '?':
419 default:
420 usage(cmd_device_stats_usage);
424 if (check_argc_exact(argc - optind, 1))
425 usage(cmd_device_stats_usage);
427 dev_path = argv[optind];
429 fdmnt = open_path_or_dev_mnt(dev_path, &dirstream, 1);
430 if (fdmnt < 0)
431 return 1;
433 ret = get_fs_info(dev_path, &fi_args, &di_args);
434 if (ret) {
435 error("getting device info for %s failed: %s", dev_path,
436 strerror(-ret));
437 err = 1;
438 goto out;
440 if (!fi_args.num_devices) {
441 error("no devices found");
442 err = 1;
443 goto out;
446 for (i = 0; i < fi_args.num_devices; i++) {
447 struct btrfs_ioctl_get_dev_stats args = {0};
448 char path[BTRFS_DEVICE_PATH_NAME_MAX + 1];
450 strncpy(path, (char *)di_args[i].path,
451 BTRFS_DEVICE_PATH_NAME_MAX);
452 path[BTRFS_DEVICE_PATH_NAME_MAX] = 0;
454 args.devid = di_args[i].devid;
455 args.nr_items = BTRFS_DEV_STAT_VALUES_MAX;
456 args.flags = flags;
458 if (ioctl(fdmnt, BTRFS_IOC_GET_DEV_STATS, &args) < 0) {
459 error("device stats ioctl failed on %s: %s",
460 path, strerror(errno));
461 err |= 1;
462 } else {
463 char *canonical_path;
464 int j;
465 static const struct {
466 const char name[32];
467 u64 num;
468 } dev_stats[] = {
469 { "write_io_errs", BTRFS_DEV_STAT_WRITE_ERRS },
470 { "read_io_errs", BTRFS_DEV_STAT_READ_ERRS },
471 { "flush_io_errs", BTRFS_DEV_STAT_FLUSH_ERRS },
472 { "corruption_errs",
473 BTRFS_DEV_STAT_CORRUPTION_ERRS },
474 { "generation_errs",
475 BTRFS_DEV_STAT_GENERATION_ERRS },
478 canonical_path = canonicalize_path(path);
480 /* No path when device is missing. */
481 if (!canonical_path) {
482 canonical_path = malloc(32);
483 if (!canonical_path) {
484 error("not enough memory for path buffer");
485 goto out;
487 snprintf(canonical_path, 32,
488 "devid:%llu", args.devid);
491 for (j = 0; j < ARRAY_SIZE(dev_stats); j++) {
492 /* We got fewer items than we know */
493 if (args.nr_items < dev_stats[j].num + 1)
494 continue;
495 printf("[%s].%-16s %llu\n", canonical_path,
496 dev_stats[j].name,
497 (unsigned long long)
498 args.values[dev_stats[j].num]);
499 if ((check == 1)
500 && (args.values[dev_stats[j].num] > 0))
501 err |= 64;
504 free(canonical_path);
508 out:
509 free(di_args);
510 close_file_or_dir(fdmnt, dirstream);
512 return err;
515 static const char * const cmd_device_usage_usage[] = {
516 "btrfs device usage [options] <path> [<path>..]",
517 "Show detailed information about internal allocations in devices.",
518 HELPINFO_UNITS_SHORT_LONG,
519 NULL
522 static int _cmd_device_usage(int fd, char *path, unsigned unit_mode)
524 int i;
525 int ret = 0;
526 struct chunk_info *chunkinfo = NULL;
527 struct device_info *devinfo = NULL;
528 int chunkcount = 0;
529 int devcount = 0;
531 ret = load_chunk_and_device_info(fd, &chunkinfo, &chunkcount, &devinfo,
532 &devcount);
533 if (ret)
534 goto out;
536 for (i = 0; i < devcount; i++) {
537 printf("%s, ID: %llu\n", devinfo[i].path, devinfo[i].devid);
538 print_device_sizes(fd, &devinfo[i], unit_mode);
539 print_device_chunks(fd, &devinfo[i], chunkinfo, chunkcount,
540 unit_mode);
541 printf("\n");
544 out:
545 free(devinfo);
546 free(chunkinfo);
548 return ret;
551 static int cmd_device_usage(int argc, char **argv)
553 unsigned unit_mode;
554 int ret = 0;
555 int i;
557 unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
559 clean_args_no_options(argc, argv, cmd_device_usage_usage);
561 if (check_argc_min(argc - optind, 1))
562 usage(cmd_device_usage_usage);
564 for (i = optind; i < argc; i++) {
565 int fd;
566 DIR *dirstream = NULL;
568 if (i > 1)
569 printf("\n");
571 fd = btrfs_open_dir(argv[i], &dirstream, 1);
572 if (fd < 0) {
573 ret = 1;
574 break;
577 ret = _cmd_device_usage(fd, argv[i], unit_mode);
578 close_file_or_dir(fd, dirstream);
580 if (ret)
581 break;
584 return !!ret;
587 static const char device_cmd_group_info[] =
588 "manage and query devices in the filesystem";
590 const struct cmd_group device_cmd_group = {
591 device_cmd_group_usage, device_cmd_group_info, {
592 { "add", cmd_device_add, cmd_device_add_usage, NULL, 0 },
593 { "delete", cmd_device_delete, cmd_device_delete_usage, NULL,
594 CMD_ALIAS },
595 { "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 },
596 { "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 },
597 { "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 },
598 { "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 },
599 { "usage", cmd_device_usage,
600 cmd_device_usage_usage, NULL, 0 },
601 NULL_CMD_STRUCT
605 int cmd_device(int argc, char **argv)
607 return handle_command_group(&device_cmd_group, argc, argv);