2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * blockdev.c --- Do various simple block device ioctls from the command line
12 * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
19 #include <sys/ioctl.h>
21 #ifdef HAVE_LINUX_BLKZONED_H
22 #include <linux/blkzoned.h>
28 #include "pathnames.h"
29 #include "closestream.h"
34 long ioc
; /* ioctl code */
35 const char *iocname
; /* ioctl name (e.g. BLKROSET) */
36 long argval
; /* default argument */
38 const char *name
; /* --setfoo */
39 const char *argname
; /* argument name or NULL */
49 FL_NOPTR
= (1 << 1), /* does not assume pointer (ARG_INT only)*/
50 FL_NORESULT
= (1 << 2) /* does not return any data */
53 /* ioctl argument types */
65 #define IOCTL_ENTRY( io ) .ioc = io, .iocname = # io
67 static const struct bdc bdcms
[] =
70 IOCTL_ENTRY(BLKROSET
),
75 .help
= N_("set read-only")
77 IOCTL_ENTRY(BLKROSET
),
82 .help
= N_("set read-write")
84 IOCTL_ENTRY(BLKROGET
),
88 .help
= N_("get read-only")
90 IOCTL_ENTRY(BLKDISCARDZEROES
),
91 .name
= "--getdiscardzeroes",
94 .help
= N_("get discard zeroes support status")
96 IOCTL_ENTRY(BLKSSZGET
),
100 .help
= N_("get logical block (sector) size")
102 IOCTL_ENTRY(BLKPBSZGET
),
106 .help
= N_("get physical block (sector) size")
108 IOCTL_ENTRY(BLKIOMIN
),
109 .name
= "--getiomin",
112 .help
= N_("get minimum I/O size")
114 IOCTL_ENTRY(BLKIOOPT
),
115 .name
= "--getioopt",
118 .help
= N_("get optimal I/O size")
120 IOCTL_ENTRY(BLKALIGNOFF
),
121 .name
= "--getalignoff",
124 .help
= N_("get alignment offset in bytes")
126 IOCTL_ENTRY(BLKSECTGET
),
127 .name
= "--getmaxsect",
128 .argtype
= ARG_USHRT
,
130 .help
= N_("get max sectors per request")
132 IOCTL_ENTRY(BLKBSZGET
),
136 .help
= N_("get blocksize")
138 IOCTL_ENTRY(BLKBSZSET
),
140 .argname
= "<bytes>",
142 .flags
= FL_NORESULT
,
143 .help
= N_("set blocksize on file descriptor opening the block device")
145 IOCTL_ENTRY(BLKGETSIZE
),
147 .argtype
= ARG_ULONG
,
149 .help
= N_("get 32-bit sector count (deprecated, use --getsz)")
151 IOCTL_ENTRY(BLKGETSIZE64
),
152 .name
= "--getsize64",
153 .argtype
= ARG_ULLONG
,
155 .help
= N_("get size in bytes")
157 IOCTL_ENTRY(BLKRASET
),
159 .argname
= "<sectors>",
161 .flags
= FL_NOPTR
| FL_NORESULT
,
162 .help
= N_("set readahead")
164 IOCTL_ENTRY(BLKRAGET
),
168 .help
= N_("get readahead")
170 IOCTL_ENTRY(BLKFRASET
),
172 .argname
= "<sectors>",
174 .flags
= FL_NOPTR
| FL_NORESULT
,
175 .help
= N_("set filesystem readahead")
177 IOCTL_ENTRY(BLKFRAGET
),
181 .help
= N_("get filesystem readahead")
183 IOCTL_ENTRY(BLKGETDISKSEQ
),
184 .name
= "--getdiskseq",
185 .argtype
= ARG_ULLONG
,
187 .help
= N_("get disk sequence number")
190 IOCTL_ENTRY(BLKGETZONESZ
),
191 .name
= "--getzonesz",
194 .help
= N_("get zone size")
197 IOCTL_ENTRY(BLKFLSBUF
),
198 .name
= "--flushbufs",
199 .help
= N_("flush buffers")
201 IOCTL_ENTRY(BLKRRPART
),
202 .name
= "--rereadpt",
203 .help
= N_("reread partition table")
207 static void __attribute__((__noreturn__
)) usage(void)
211 fputs(USAGE_HEADER
, stdout
);
213 " %1$s [-v|-q] commands devices\n"
214 " %1$s --report [devices]\n"
216 ), program_invocation_short_name
);
218 fputs(USAGE_SEPARATOR
, stdout
);
219 fputs( _("Call block device ioctls from the command line."), stdout
);
221 fputs(USAGE_OPTIONS
, stdout
);
222 fputs( _(" -q quiet mode"), stdout
);
223 fputs( _(" -v verbose mode"), stdout
);
224 fputs( _(" --report print report for specified (or all) devices"), stdout
);
225 fputs(USAGE_SEPARATOR
, stdout
);
226 fprintf(stdout
, USAGE_HELP_OPTIONS(16));
228 fputs(USAGE_SEPARATOR
, stdout
);
229 fputs(_("Available commands:"), stdout
);
230 fprintf(stdout
, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
231 for (i
= 0; i
< ARRAY_SIZE(bdcms
); i
++) {
232 if (bdcms
[i
].argname
)
233 fprintf(stdout
, " %s %-*s %s\n", bdcms
[i
].name
,
234 (int)(24 - strlen(bdcms
[i
].name
)),
235 bdcms
[i
].argname
, _(bdcms
[i
].help
));
237 fprintf(stdout
, " %-25s %s\n", bdcms
[i
].name
,
241 fprintf(stdout
, USAGE_MAN_TAIL("blockdev(8)"));
245 static int find_cmd(char *s
)
249 for (j
= 0; j
< ARRAY_SIZE(bdcms
); j
++)
250 if (!strcmp(s
, bdcms
[j
].name
))
255 static void do_commands(int fd
, char **argv
, int d
);
256 static void report_header(void);
257 static void report_device(char *device
, int quiet
);
258 static void report_all_devices(void);
260 int main(int argc
, char **argv
)
264 setlocale(LC_ALL
, "");
265 bindtextdomain(PACKAGE
, LOCALEDIR
);
267 close_stdout_atexit();
270 warnx(_("not enough arguments"));
271 errtryhelp(EXIT_FAILURE
);
274 /* -V not together with commands */
275 if (!strcmp(argv
[1], "-V") || !strcmp(argv
[1], "--version"))
276 print_version(EXIT_SUCCESS
);
277 if (!strcmp(argv
[1], "-h") || !strcmp(argv
[1], "--help"))
280 /* --report not together with other commands */
281 if (!strcmp(argv
[1], "--report")) {
284 for (d
= 2; d
< argc
; d
++)
285 report_device(argv
[d
], 0);
287 report_all_devices();
292 /* do each of the commands on each of the devices */
293 /* devices start after last command */
294 for (d
= 1; d
< argc
; d
++) {
295 j
= find_cmd(argv
[d
]);
297 if (bdcms
[j
].argname
)
301 if (!strcmp(argv
[d
], "--getsz"))
303 if (!strcmp(argv
[d
], "--")) {
307 if (argv
[d
][0] != '-')
312 warnx(_("no device specified"));
313 errtryhelp(EXIT_FAILURE
);
316 for (k
= d
; k
< argc
; k
++) {
317 fd
= open(argv
[k
], O_RDONLY
, 0);
319 err(EXIT_FAILURE
, _("cannot open %s"), argv
[k
]);
320 do_commands(fd
, argv
, d
);
326 static void do_commands(int fd
, char **argv
, int d
)
330 unsigned int uarg
= 0;
331 unsigned short huarg
= 0;
334 unsigned long lu
= 0;
335 unsigned long long llu
= 0;
338 for (i
= 1; i
< d
; i
++) {
339 if (!strcmp(argv
[i
], "-v")) {
343 if (!strcmp(argv
[i
], "-q")) {
348 if (!strcmp(argv
[i
], "--getsz")) {
349 res
= blkdev_get_sectors(fd
, &llu
);
351 printf("%lld\n", llu
);
354 _("could not get device size"));
358 j
= find_cmd(argv
[i
]);
360 warnx(_("Unknown command: %s"), argv
[i
]);
361 errtryhelp(EXIT_FAILURE
);
364 switch (bdcms
[j
].argtype
) {
367 res
= ioctl(fd
, bdcms
[j
].ioc
, 0);
370 huarg
= bdcms
[j
].argval
;
371 res
= ioctl(fd
, bdcms
[j
].ioc
, &huarg
);
374 if (bdcms
[j
].argname
) {
376 warnx(_("%s requires an argument"),
378 errtryhelp(EXIT_FAILURE
);
380 iarg
= strtos32_or_err(argv
[++i
], _("failed to parse command argument"));
382 iarg
= bdcms
[j
].argval
;
384 res
= bdcms
[j
].flags
& FL_NOPTR
?
385 ioctl(fd
, bdcms
[j
].ioc
, iarg
) :
386 ioctl(fd
, bdcms
[j
].ioc
, &iarg
);
389 uarg
= bdcms
[j
].argval
;
390 res
= ioctl(fd
, bdcms
[j
].ioc
, &uarg
);
393 larg
= bdcms
[j
].argval
;
394 res
= ioctl(fd
, bdcms
[j
].ioc
, &larg
);
397 llarg
= bdcms
[j
].argval
;
398 res
= ioctl(fd
, bdcms
[j
].ioc
, &llarg
);
401 lu
= bdcms
[j
].argval
;
402 res
= ioctl(fd
, bdcms
[j
].ioc
, &lu
);
405 llu
= bdcms
[j
].argval
;
406 res
= ioctl(fd
, bdcms
[j
].ioc
, &llu
);
411 warn(_("ioctl error on %s"), bdcms
[j
].iocname
);
413 printf(_("%s failed.\n"), _(bdcms
[j
].help
));
417 if (bdcms
[j
].argtype
== ARG_NONE
||
418 (bdcms
[j
].flags
& FL_NORESULT
)) {
420 printf(_("%s succeeded.\n"), _(bdcms
[j
].help
));
425 printf("%s: ", _(bdcms
[j
].help
));
427 switch (bdcms
[j
].argtype
) {
429 printf("%hu\n", huarg
);
432 printf("%d\n", iarg
);
435 printf("%u\n", uarg
);
438 printf("%ld\n", larg
);
441 printf("%lld\n", llarg
);
447 printf("%llu\n", llu
);
453 static void report_all_devices(void)
457 char ptname
[200 + 1];
461 procpt
= fopen(_PATH_PROC_PARTITIONS
, "r");
463 err(EXIT_FAILURE
, _("cannot open %s"), _PATH_PROC_PARTITIONS
);
465 while (fgets(line
, sizeof(line
), procpt
)) {
466 if (sscanf(line
, " %d %d %d %200[^\n ]",
467 &ma
, &mi
, &sz
, ptname
) != 4)
470 snprintf(device
, sizeof(device
), "/dev/%s", ptname
);
471 report_device(device
, 1);
477 static void report_device(char *device
, int quiet
)
482 unsigned long long bytes
;
484 char start_str
[16] = { "\0" };
487 fd
= open(device
, O_RDONLY
| O_NONBLOCK
);
490 warn(_("cannot open %s"), device
);
496 if (fstat(fd
, &st
) == 0) {
500 pc
= ul_new_sysfs_path(st
.st_rdev
, NULL
, NULL
);
502 sysfs_blkdev_get_wholedisk(pc
, NULL
, 0, &disk
) == 0 &&
503 disk
!= st
.st_rdev
) {
505 if (ul_path_read_u64(pc
, &start
, "start") != 0)
506 /* TRANSLATORS: Start sector not available. Max. 15 letters. */
507 snprintf(start_str
, sizeof(start_str
), "%15s", _("N/A"));
512 snprintf(start_str
, sizeof(start_str
), "%15ju", start
);
514 if (ioctl(fd
, BLKROGET
, &ro
) == 0 &&
515 ioctl(fd
, BLKRAGET
, &ra
) == 0 &&
516 ioctl(fd
, BLKSSZGET
, &ssz
) == 0 &&
517 ioctl(fd
, BLKBSZGET
, &bsz
) == 0 &&
518 blkdev_get_size(fd
, &bytes
) == 0) {
519 printf("%s %5ld %5d %5d %s %15lld %s\n",
520 ro
? "ro" : "rw", ra
, ssz
, bsz
, start_str
, bytes
, device
);
523 warnx(_("ioctl error on %s"), device
);
529 static void report_header(void)
531 printf(_("RO RA SSZ BSZ StartSec Size Device\n"));