]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/blockdev.c
Merge branch 'meson-more-build-options' of https://github.com/jwillikers/util-linux
[thirdparty/util-linux.git] / disk-utils / blockdev.c
CommitLineData
eb63b9b8 1/*
9e95aa12
KZ
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
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.
8 *
eb63b9b8
KZ
9 * blockdev.c --- Do various simple block device ioctls from the command line
10 * aeb, 991028
9e95aa12
KZ
11 *
12 * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
eb63b9b8 13 */
eb63b9b8
KZ
14#include <stdio.h>
15#include <fcntl.h>
eb63b9b8
KZ
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19#include <sys/ioctl.h>
3281d426 20#include <errno.h>
4832fd9f
TW
21#ifdef HAVE_LINUX_BLKZONED_H
22#include <linux/blkzoned.h>
23#endif
eb63b9b8 24
515422fd 25#include "c.h"
eb63b9b8 26#include "nls.h"
bc8b6383 27#include "blkdev.h"
14c76d8b 28#include "pathnames.h"
45ca68ec 29#include "closestream.h"
40775268 30#include "strutils.h"
569d1dac 31#include "sysfs.h"
612721db 32
eb63b9b8 33struct bdc {
c3f62be7
KZ
34 long ioc; /* ioctl code */
35 const char *iocname; /* ioctl name (e.g. BLKROSET) */
36 long argval; /* default argument */
37
38 const char *name; /* --setfoo */
39 const char *argname; /* argument name or NULL */
40
41 const char *help;
42
43 int argtype;
44 int flags;
45};
46
47/* command flags */
48enum {
49 FL_NOPTR = (1 << 1), /* does not assume pointer (ARG_INT only)*/
50 FL_NORESULT = (1 << 2) /* does not return any data */
51};
52
53/* ioctl argument types */
54enum {
55 ARG_NONE,
726fa24e 56 ARG_USHRT,
c3f62be7 57 ARG_INT,
726fa24e 58 ARG_UINT,
c3f62be7
KZ
59 ARG_LONG,
60 ARG_ULONG,
61 ARG_LLONG,
62 ARG_ULLONG
63};
64
65#define IOCTL_ENTRY( io ) .ioc = io, .iocname = # io
66
00239f33 67static const struct bdc bdcms[] =
c3f62be7
KZ
68{
69 {
70 IOCTL_ENTRY(BLKROSET),
71 .name = "--setro",
72 .argtype = ARG_INT,
73 .argval = 1,
74 .flags = FL_NORESULT,
75 .help = N_("set read-only")
76 },{
77 IOCTL_ENTRY(BLKROSET),
78 .name = "--setrw",
79 .argtype = ARG_INT,
80 .argval = 0,
81 .flags = FL_NORESULT,
82 .help = N_("set read-write")
83 },{
84 IOCTL_ENTRY(BLKROGET),
85 .name = "--getro",
86 .argtype = ARG_INT,
87 .argval = -1,
88 .help = N_("get read-only")
1519ab5f
KZ
89 },{
90 IOCTL_ENTRY(BLKDISCARDZEROES),
91 .name = "--getdiscardzeroes",
92 .argtype = ARG_UINT,
93 .argval = -1,
94 .help = N_("get discard zeroes support status")
c3f62be7
KZ
95 },{
96 IOCTL_ENTRY(BLKSSZGET),
97 .name = "--getss",
98 .argtype = ARG_INT,
99 .argval = -1,
d965d63f
KZ
100 .help = N_("get logical block (sector) size")
101 },{
102 IOCTL_ENTRY(BLKPBSZGET),
103 .name = "--getpbsz",
104 .argtype = ARG_UINT,
105 .argval = -1,
106 .help = N_("get physical block (sector) size")
107 },{
108 IOCTL_ENTRY(BLKIOMIN),
109 .name = "--getiomin",
110 .argtype = ARG_UINT,
111 .argval = -1,
112 .help = N_("get minimum I/O size")
113 },{
114 IOCTL_ENTRY(BLKIOOPT),
115 .name = "--getioopt",
116 .argtype = ARG_UINT,
117 .argval = -1,
118 .help = N_("get optimal I/O size")
119 },{
120 IOCTL_ENTRY(BLKALIGNOFF),
121 .name = "--getalignoff",
122 .argtype = ARG_INT,
ed98508a 123 .argval = -1,
455fdf4a 124 .help = N_("get alignment offset in bytes")
d965d63f
KZ
125 },{
126 IOCTL_ENTRY(BLKSECTGET),
127 .name = "--getmaxsect",
128 .argtype = ARG_USHRT,
129 .argval = -1,
130 .help = N_("get max sectors per request")
c3f62be7
KZ
131 },{
132 IOCTL_ENTRY(BLKBSZGET),
133 .name = "--getbsz",
134 .argtype = ARG_INT,
135 .argval = -1,
136 .help = N_("get blocksize")
39d2e706
KZ
137 },{
138 IOCTL_ENTRY(BLKBSZSET),
139 .name = "--setbsz",
140 .argname = "<bytes>",
141 .argtype = ARG_INT,
142 .flags = FL_NORESULT,
7ab32ae6 143 .help = N_("set blocksize on file descriptor opening the block device")
c3f62be7
KZ
144 },{
145 IOCTL_ENTRY(BLKGETSIZE),
146 .name = "--getsize",
147 .argtype = ARG_ULONG,
148 .argval = -1,
455fdf4a 149 .help = N_("get 32-bit sector count (deprecated, use --getsz)")
c3f62be7
KZ
150 },{
151 IOCTL_ENTRY(BLKGETSIZE64),
152 .name = "--getsize64",
153 .argtype = ARG_ULLONG,
154 .argval = -1,
155 .help = N_("get size in bytes")
156 },{
157 IOCTL_ENTRY(BLKRASET),
158 .name = "--setra",
455fdf4a 159 .argname = "<sectors>",
c3f62be7
KZ
160 .argtype = ARG_INT,
161 .flags = FL_NOPTR | FL_NORESULT,
162 .help = N_("set readahead")
163 },{
164 IOCTL_ENTRY(BLKRAGET),
165 .name = "--getra",
166 .argtype = ARG_LONG,
167 .argval = -1,
168 .help = N_("get readahead")
169 },{
170 IOCTL_ENTRY(BLKFRASET),
171 .name = "--setfra",
455fdf4a 172 .argname = "<sectors>",
c3f62be7
KZ
173 .argtype = ARG_INT,
174 .flags = FL_NOPTR | FL_NORESULT,
175 .help = N_("set filesystem readahead")
176 },{
177 IOCTL_ENTRY(BLKFRAGET),
178 .name = "--getfra",
179 .argtype = ARG_LONG,
180 .argval = -1,
181 .help = N_("get filesystem readahead")
c10adc20
TW
182 },{
183 IOCTL_ENTRY(BLKGETDISKSEQ),
184 .name = "--getdiskseq",
185 .argtype = ARG_ULLONG,
186 .argval = -1,
187 .help = N_("get disk sequence number")
c3f62be7 188 },{
ae20b7fb 189#ifdef BLKGETZONESZ
4832fd9f
TW
190 IOCTL_ENTRY(BLKGETZONESZ),
191 .name = "--getzonesz",
192 .argtype = ARG_UINT,
193 .argval = -1,
194 .help = N_("get zone size")
195 },{
196#endif
c3f62be7
KZ
197 IOCTL_ENTRY(BLKFLSBUF),
198 .name = "--flushbufs",
199 .help = N_("flush buffers")
200 },{
201 IOCTL_ENTRY(BLKRRPART),
202 .name = "--rereadpt",
203 .help = N_("reread partition table")
204 }
eb63b9b8
KZ
205};
206
9325dbfd 207static void __attribute__((__noreturn__)) usage(void)
bded204c 208{
75737ad6 209 size_t i;
9325dbfd 210
a861538c 211 fputs(USAGE_HEADER, stdout);
bad4c729 212 fprintf(stdout, _(
a861538c
RM
213 " %1$s [-v|-q] commands devices\n"
214 " %1$s --report [devices]\n"
215 " %1$s -h|-V\n"
216 ), program_invocation_short_name);
a424171c 217
a861538c 218 fputs(USAGE_SEPARATOR, stdout);
f11785b5 219 fputsln( _("Call block device ioctls from the command line."), stdout);
a861538c
RM
220
221 fputs(USAGE_OPTIONS, stdout);
f11785b5
TW
222 fputsln( _(" -q quiet mode"), stdout);
223 fputsln( _(" -v verbose mode"), stdout);
224 fputsln( _(" --report print report for specified (or all) devices"), stdout);
a861538c 225 fputs(USAGE_SEPARATOR, stdout);
bad4c729 226 fprintf(stdout, USAGE_HELP_OPTIONS(16));
a861538c
RM
227
228 fputs(USAGE_SEPARATOR, stdout);
f11785b5 229 fputsln( _("Available commands:"), stdout);
bad4c729 230 fprintf(stdout, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
515422fd 231 for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
eb63b9b8 232 if (bdcms[i].argname)
bad4c729 233 fprintf(stdout, " %s %-*s %s\n", bdcms[i].name,
bded204c
SK
234 (int)(24 - strlen(bdcms[i].name)),
235 bdcms[i].argname, _(bdcms[i].help));
a424171c 236 else
bad4c729 237 fprintf(stdout, " %-25s %s\n", bdcms[i].name,
bded204c 238 _(bdcms[i].help));
eb63b9b8 239 }
9325dbfd 240
bad4c729 241 fprintf(stdout, USAGE_MAN_TAIL("blockdev(8)"));
9325dbfd 242 exit(EXIT_SUCCESS);
eb63b9b8
KZ
243}
244
baaa4479
SK
245static int find_cmd(char *s)
246{
75737ad6 247 size_t j;
eb63b9b8 248
515422fd 249 for (j = 0; j < ARRAY_SIZE(bdcms); j++)
eb63b9b8
KZ
250 if (!strcmp(s, bdcms[j].name))
251 return j;
252 return -1;
253}
254
f61a097b
WG
255static void do_commands(int fd, char **argv, int d);
256static void report_header(void);
b3e9aadc
E
257static int report_device(char *device, int quiet);
258static int report_all_devices(void);
eb63b9b8 259
baaa4479
SK
260int main(int argc, char **argv)
261{
eb63b9b8 262 int fd, d, j, k;
eb63b9b8 263
eb63b9b8
KZ
264 setlocale(LC_ALL, "");
265 bindtextdomain(PACKAGE, LOCALEDIR);
266 textdomain(PACKAGE);
2c308875 267 close_stdout_atexit();
eb63b9b8 268
9325dbfd
RM
269 if (argc < 2) {
270 warnx(_("not enough arguments"));
271 errtryhelp(EXIT_FAILURE);
272 }
eb63b9b8
KZ
273
274 /* -V not together with commands */
2c308875
KZ
275 if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))
276 print_version(EXIT_SUCCESS);
bded204c 277 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
9325dbfd 278 usage();
eb63b9b8 279
612721db
KZ
280 /* --report not together with other commands */
281 if (!strcmp(argv[1], "--report")) {
b3e9aadc 282 int rc = 0;
612721db
KZ
283 report_header();
284 if (argc > 2) {
285 for (d = 2; d < argc; d++)
b3e9aadc 286 rc += report_device(argv[d], 0);
612721db 287 } else {
b3e9aadc 288 rc = report_all_devices();
612721db 289 }
b3e9aadc 290 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
612721db
KZ
291 }
292
eb63b9b8
KZ
293 /* do each of the commands on each of the devices */
294 /* devices start after last command */
295 for (d = 1; d < argc; d++) {
296 j = find_cmd(argv[d]);
297 if (j >= 0) {
c3f62be7 298 if (bdcms[j].argname)
eb63b9b8
KZ
299 d++;
300 continue;
301 }
c129767e
KZ
302 if (!strcmp(argv[d], "--getsz"))
303 continue;
eb63b9b8
KZ
304 if (!strcmp(argv[d], "--")) {
305 d++;
306 break;
307 }
308 if (argv[d][0] != '-')
309 break;
310 }
311
9325dbfd
RM
312 if (d >= argc) {
313 warnx(_("no device specified"));
314 errtryhelp(EXIT_FAILURE);
315 }
eb63b9b8
KZ
316
317 for (k = d; k < argc; k++) {
318 fd = open(argv[k], O_RDONLY, 0);
6b515d0b
SK
319 if (fd < 0)
320 err(EXIT_FAILURE, _("cannot open %s"), argv[k]);
eb63b9b8 321 do_commands(fd, argv, d);
e8f26419 322 close(fd);
eb63b9b8 323 }
6b515d0b 324 return EXIT_SUCCESS;
eb63b9b8
KZ
325}
326
f61a097b 327static void do_commands(int fd, char **argv, int d)
baaa4479 328{
eb63b9b8 329 int res, i, j;
15705de0
KZ
330 int iarg = 0;
331 unsigned int uarg = 0;
332 unsigned short huarg = 0;
333 long larg = 0;
334 long long llarg = 0;
335 unsigned long lu = 0;
336 unsigned long long llu = 0;
eb63b9b8
KZ
337 int verbose = 0;
338
339 for (i = 1; i < d; i++) {
340 if (!strcmp(argv[i], "-v")) {
341 verbose = 1;
342 continue;
baaa4479 343 }
eb63b9b8
KZ
344 if (!strcmp(argv[i], "-q")) {
345 verbose = 0;
346 continue;
347 }
348
c129767e 349 if (!strcmp(argv[i], "--getsz")) {
bc8b6383 350 res = blkdev_get_sectors(fd, &llu);
d546d751
CAM
351 if (res == 0) {
352 if (verbose)
353 printf(_("get size in 512-byte sectors: "));
bc8b6383 354 printf("%lld\n", llu);
d546d751 355 }
c129767e 356 else
baaa4479
SK
357 errx(EXIT_FAILURE,
358 _("could not get device size"));
c129767e
KZ
359 continue;
360 }
361
eb63b9b8
KZ
362 j = find_cmd(argv[i]);
363 if (j == -1) {
cc4d8d72 364 warnx(_("Unknown command: %s"), argv[i]);
9325dbfd 365 errtryhelp(EXIT_FAILURE);
eb63b9b8
KZ
366 }
367
baaa4479 368 switch (bdcms[j].argtype) {
eb63b9b8 369 default:
c3f62be7 370 case ARG_NONE:
eb63b9b8
KZ
371 res = ioctl(fd, bdcms[j].ioc, 0);
372 break;
726fa24e
KZ
373 case ARG_USHRT:
374 huarg = bdcms[j].argval;
375 res = ioctl(fd, bdcms[j].ioc, &huarg);
376 break;
c3f62be7
KZ
377 case ARG_INT:
378 if (bdcms[j].argname) {
baaa4479 379 if (i == d - 1) {
cc4d8d72 380 warnx(_("%s requires an argument"),
baaa4479 381 bdcms[j].name);
9325dbfd 382 errtryhelp(EXIT_FAILURE);
c3f62be7 383 }
40775268 384 iarg = strtos32_or_err(argv[++i], _("failed to parse command argument"));
c3f62be7
KZ
385 } else
386 iarg = bdcms[j].argval;
387
388 res = bdcms[j].flags & FL_NOPTR ?
baaa4479
SK
389 ioctl(fd, bdcms[j].ioc, iarg) :
390 ioctl(fd, bdcms[j].ioc, &iarg);
eb63b9b8 391 break;
726fa24e
KZ
392 case ARG_UINT:
393 uarg = bdcms[j].argval;
394 res = ioctl(fd, bdcms[j].ioc, &uarg);
395 break;
c3f62be7 396 case ARG_LONG:
eb63b9b8
KZ
397 larg = bdcms[j].argval;
398 res = ioctl(fd, bdcms[j].ioc, &larg);
399 break;
c3f62be7 400 case ARG_LLONG:
c129767e
KZ
401 llarg = bdcms[j].argval;
402 res = ioctl(fd, bdcms[j].ioc, &llarg);
403 break;
c3f62be7 404 case ARG_ULONG:
1dea05a8
KZ
405 lu = bdcms[j].argval;
406 res = ioctl(fd, bdcms[j].ioc, &lu);
407 break;
c3f62be7 408 case ARG_ULLONG:
1dea05a8
KZ
409 llu = bdcms[j].argval;
410 res = ioctl(fd, bdcms[j].ioc, &llu);
411 break;
eb63b9b8 412 }
c3f62be7 413
eb63b9b8 414 if (res == -1) {
338a6bc5 415 warn(_("ioctl error on %s"), bdcms[j].iocname);
eb63b9b8 416 if (verbose)
65b27d36 417 printf(_("%s failed.\n"), _(bdcms[j].help));
6b515d0b 418 exit(EXIT_FAILURE);
eb63b9b8 419 }
c3f62be7
KZ
420
421 if (bdcms[j].argtype == ARG_NONE ||
422 (bdcms[j].flags & FL_NORESULT)) {
eb63b9b8 423 if (verbose)
c3f62be7
KZ
424 printf(_("%s succeeded.\n"), _(bdcms[j].help));
425 continue;
426 }
427
428 if (verbose)
429 printf("%s: ", _(bdcms[j].help));
430
baaa4479 431 switch (bdcms[j].argtype) {
726fa24e
KZ
432 case ARG_USHRT:
433 printf("%hu\n", huarg);
434 break;
c3f62be7
KZ
435 case ARG_INT:
436 printf("%d\n", iarg);
eb63b9b8 437 break;
726fa24e
KZ
438 case ARG_UINT:
439 printf("%u\n", uarg);
440 break;
c3f62be7
KZ
441 case ARG_LONG:
442 printf("%ld\n", larg);
c129767e 443 break;
c3f62be7
KZ
444 case ARG_LLONG:
445 printf("%lld\n", llarg);
1dea05a8 446 break;
c3f62be7
KZ
447 case ARG_ULONG:
448 printf("%lu\n", lu);
1dea05a8 449 break;
c3f62be7
KZ
450 case ARG_ULLONG:
451 printf("%llu\n", llu);
eb63b9b8
KZ
452 break;
453 }
454 }
455}
612721db 456
b3e9aadc 457static int report_all_devices(void)
baaa4479 458{
612721db
KZ
459 FILE *procpt;
460 char line[200];
657d9adb 461 char ptname[200 + 1];
612721db
KZ
462 char device[210];
463 int ma, mi, sz;
b3e9aadc 464 int rc = 0;
612721db 465
14c76d8b 466 procpt = fopen(_PATH_PROC_PARTITIONS, "r");
cc4d8d72 467 if (!procpt)
14c76d8b 468 err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_PARTITIONS);
612721db
KZ
469
470 while (fgets(line, sizeof(line), procpt)) {
baaa4479
SK
471 if (sscanf(line, " %d %d %d %200[^\n ]",
472 &ma, &mi, &sz, ptname) != 4)
612721db
KZ
473 continue;
474
26266436 475 snprintf(device, sizeof(device), "/dev/%s", ptname);
b3e9aadc 476 rc += report_device(device, 1);
612721db 477 }
122db55d 478
479 fclose(procpt);
b3e9aadc 480 return rc;
612721db
KZ
481}
482
b3e9aadc 483static int report_device(char *device, int quiet)
baaa4479 484{
612721db
KZ
485 int fd;
486 int ro, ssz, bsz;
b3e9aadc 487 int rc = 0;
bc8b6383
KZ
488 long ra;
489 unsigned long long bytes;
569d1dac 490 uint64_t start = 0;
d73a7184 491 char start_str[16] = { "\0" };
569d1dac 492 struct stat st;
612721db
KZ
493
494 fd = open(device, O_RDONLY | O_NONBLOCK);
495 if (fd < 0) {
496 if (!quiet)
6b515d0b 497 warn(_("cannot open %s"), device);
b3e9aadc 498 return 1;
612721db
KZ
499 }
500
501 ro = ssz = bsz = 0;
569d1dac 502 ra = 0;
345ddd28
KZ
503 if (fstat(fd, &st) == 0) {
504 dev_t disk;
505 struct path_cxt *pc;
506
507 pc = ul_new_sysfs_path(st.st_rdev, NULL, NULL);
508 if (pc &&
509 sysfs_blkdev_get_wholedisk(pc, NULL, 0, &disk) == 0 &&
510 disk != st.st_rdev) {
511
512 if (ul_path_read_u64(pc, &start, "start") != 0)
d73a7184 513 /* TRANSLATORS: Start sector not available. Max. 15 letters. */
26266436 514 snprintf(start_str, sizeof(start_str), "%15s", _("N/A"));
345ddd28
KZ
515 }
516 ul_unref_path(pc);
569d1dac 517 }
9147d2ad 518 if (!*start_str)
26266436 519 snprintf(start_str, sizeof(start_str), "%15ju", start);
9147d2ad 520
baaa4479
SK
521 if (ioctl(fd, BLKROGET, &ro) == 0 &&
522 ioctl(fd, BLKRAGET, &ra) == 0 &&
523 ioctl(fd, BLKSSZGET, &ssz) == 0 &&
524 ioctl(fd, BLKBSZGET, &bsz) == 0 &&
baaa4479 525 blkdev_get_size(fd, &bytes) == 0) {
c4a543ff 526 printf("%s %5ld %5d %5d %s %15lld %s\n",
9147d2ad 527 ro ? "ro" : "rw", ra, ssz, bsz, start_str, bytes, device);
612721db
KZ
528 } else {
529 if (!quiet)
cc4d8d72 530 warnx(_("ioctl error on %s"), device);
b3e9aadc 531 rc = 1;
612721db 532 }
122db55d 533
534 close(fd);
b3e9aadc 535 return rc;
612721db
KZ
536}
537
16bd8025 538static void report_header(void)
baaa4479 539{
d73a7184 540 printf(_("RO RA SSZ BSZ StartSec Size Device\n"));
612721db 541}