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