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