]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/blockdev.c
Merge branch 'lsfd--close-all' of https://github.com/masatake/util-linux
[thirdparty/util-linux.git] / disk-utils / blockdev.c
index 0674780b8f45f1f6098b29b5a5973375294eda8c..55b47acf61a7a04342b941745afa6dff3fef8504 100644 (file)
@@ -1,8 +1,16 @@
 /*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
  * blockdev.c --- Do various simple block device ioctls from the command line
  * aeb, 991028
+ *
+ * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
  */
-
 #include <stdio.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <errno.h>
+#ifdef HAVE_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
 
 #include "c.h"
 #include "nls.h"
 #include "blkdev.h"
 #include "pathnames.h"
+#include "closestream.h"
+#include "strutils.h"
+#include "sysfs.h"
 
 struct bdc {
        long            ioc;            /* ioctl code */
@@ -126,7 +140,7 @@ static const struct bdc bdcms[] =
                .argname = "<bytes>",
                .argtype = ARG_INT,
                .flags = FL_NORESULT,
-               .help = N_("set blocksize")
+               .help = N_("set blocksize on file descriptor opening the block device")
        },{
                IOCTL_ENTRY(BLKGETSIZE),
                .name = "--getsize",
@@ -166,6 +180,20 @@ static const struct bdc bdcms[] =
                .argval = -1,
                .help = N_("get filesystem readahead")
        },{
+               IOCTL_ENTRY(BLKGETDISKSEQ),
+               .name = "--getdiskseq",
+               .argtype = ARG_ULLONG,
+               .argval = -1,
+               .help = N_("get disk sequence number")
+       },{
+#ifdef BLKGETZONESZ
+               IOCTL_ENTRY(BLKGETZONESZ),
+               .name = "--getzonesz",
+               .argtype = ARG_UINT,
+               .argval = -1,
+               .help = N_("get zone size")
+       },{
+#endif
                IOCTL_ENTRY(BLKFLSBUF),
                .name = "--flushbufs",
                .help = N_("flush buffers")
@@ -176,32 +204,47 @@ static const struct bdc bdcms[] =
        }
 };
 
-static void __attribute__ ((__noreturn__)) usage(FILE * out)
+static void __attribute__((__noreturn__)) usage(void)
 {
-       int i;
-       fprintf(out, _("\nUsage:\n"
-                      "  %1$s -V\n"
-                      "  %1$s --report [devices]\n"
-                      "  %1$s [-v|-q] commands devices\n\n"
-                      "Available commands:\n"), program_invocation_short_name);
-
-       fprintf(out, _("  %-25s get size in 512-byte sectors\n"), "--getsz");
+       size_t i;
+
+       fputs(USAGE_HEADER, stdout);
+       fprintf(stdout, _(
+                " %1$s [-v|-q] commands devices\n"
+                " %1$s --report [devices]\n"
+                " %1$s -h|-V\n"
+               ), program_invocation_short_name);
+
+       fputs(USAGE_SEPARATOR, stdout);
+       fputsln(  _("Call block device ioctls from the command line."), stdout);
+
+       fputs(USAGE_OPTIONS, stdout);
+       fputsln(  _(" -q             quiet mode"), stdout);
+       fputsln(  _(" -v             verbose mode"), stdout);
+       fputsln(  _("     --report   print report for specified (or all) devices"), stdout);
+       fputs(USAGE_SEPARATOR, stdout);
+       fprintf(stdout, USAGE_HELP_OPTIONS(16));
+
+       fputs(USAGE_SEPARATOR, stdout);
+       fputsln(  _("Available commands:"), stdout);
+       fprintf(stdout, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
        for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
                if (bdcms[i].argname)
-                       fprintf(out, "  %s %-*s %s\n", bdcms[i].name,
+                       fprintf(stdout, " %s %-*s %s\n", bdcms[i].name,
                                (int)(24 - strlen(bdcms[i].name)),
                                bdcms[i].argname, _(bdcms[i].help));
                else
-                       fprintf(out, "  %-25s %s\n", bdcms[i].name,
+                       fprintf(stdout, " %-25s %s\n", bdcms[i].name,
                                _(bdcms[i].help));
        }
-       fputc('\n', out);
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+
+       fprintf(stdout, USAGE_MAN_TAIL("blockdev(8)"));
+       exit(EXIT_SUCCESS);
 }
 
-static int
-find_cmd(char *s) {
-       int j;
+static int find_cmd(char *s)
+{
+       size_t j;
 
        for (j = 0; j < ARRAY_SIZE(bdcms); j++)
                if (!strcmp(s, bdcms[j].name))
@@ -209,40 +252,42 @@ find_cmd(char *s) {
        return -1;
 }
 
-void do_commands(int fd, char **argv, int d);
-void report_header(void);
-void report_device(char *device, int quiet);
-void report_all_devices(void);
+static void do_commands(int fd, char **argv, int d);
+static void report_header(void);
+static int report_device(char *device, int quiet);
+static int report_all_devices(void);
 
-int
-main(int argc, char **argv) {
+int main(int argc, char **argv)
+{
        int fd, d, j, k;
 
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
+       close_stdout_atexit();
 
-       if (argc < 2)
-               usage(stderr);
+       if (argc < 2) {
+               warnx(_("not enough arguments"));
+               errtryhelp(EXIT_FAILURE);
+       }
 
        /* -V not together with commands */
-       if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
-               printf(_("%s (%s)\n"), program_invocation_short_name, PACKAGE_STRING);
-               return EXIT_SUCCESS;
-       }
+       if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))
+               print_version(EXIT_SUCCESS);
        if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
-               usage(stdout);
+               usage();
 
        /* --report not together with other commands */
        if (!strcmp(argv[1], "--report")) {
+               int rc = 0;
                report_header();
                if (argc > 2) {
                        for (d = 2; d < argc; d++)
-                               report_device(argv[d], 0);
+                               rc += report_device(argv[d], 0);
                } else {
-                       report_all_devices();
+                       rc = report_all_devices();
                }
-               return EXIT_SUCCESS;
+               return rc ? EXIT_FAILURE : EXIT_SUCCESS;
        }
 
        /* do each of the commands on each of the devices */
@@ -264,8 +309,10 @@ main(int argc, char **argv) {
                        break;
        }
 
-       if (d >= argc)
-               usage(stderr);
+       if (d >= argc) {
+               warnx(_("no device specified"));
+               errtryhelp(EXIT_FAILURE);
+       }
 
        for (k = d; k < argc; k++) {
                fd = open(argv[k], O_RDONLY, 0);
@@ -277,23 +324,23 @@ main(int argc, char **argv) {
        return EXIT_SUCCESS;
 }
 
-void
-do_commands(int fd, char **argv, int d) {
+static void do_commands(int fd, char **argv, int d)
+{
        int res, i, j;
-       int iarg;
-       unsigned int uarg;
-       unsigned short huarg;
-       long larg;
-       long long llarg;
-       unsigned long lu;
-       unsigned long long llu;
+       int iarg = 0;
+       unsigned int uarg = 0;
+       unsigned short huarg = 0;
+       long larg = 0;
+       long long llarg = 0;
+       unsigned long lu = 0;
+       unsigned long long llu = 0;
        int verbose = 0;
 
        for (i = 1; i < d; i++) {
                if (!strcmp(argv[i], "-v")) {
                        verbose = 1;
                        continue;
-                }
+               }
                if (!strcmp(argv[i], "-q")) {
                        verbose = 0;
                        continue;
@@ -301,20 +348,24 @@ do_commands(int fd, char **argv, int d) {
 
                if (!strcmp(argv[i], "--getsz")) {
                        res = blkdev_get_sectors(fd, &llu);
-                       if (res == 0)
+                       if (res == 0) {
+                               if (verbose)
+                                       printf(_("get size in 512-byte sectors: "));
                                printf("%lld\n", llu);
+                       }
                        else
-                               errx(EXIT_FAILURE, _("could not get device size"));
+                               errx(EXIT_FAILURE,
+                                    _("could not get device size"));
                        continue;
                }
 
                j = find_cmd(argv[i]);
                if (j == -1) {
                        warnx(_("Unknown command: %s"), argv[i]);
-                       usage(stderr);
+                       errtryhelp(EXIT_FAILURE);
                }
 
-               switch(bdcms[j].argtype) {
+               switch (bdcms[j].argtype) {
                default:
                case ARG_NONE:
                        res = ioctl(fd, bdcms[j].ioc, 0);
@@ -325,18 +376,18 @@ do_commands(int fd, char **argv, int d) {
                        break;
                case ARG_INT:
                        if (bdcms[j].argname) {
-                               if (i == d-1) {
+                               if (i == d - 1) {
                                        warnx(_("%s requires an argument"),
-                                               bdcms[j].name);
-                                       usage(stderr);
+                                             bdcms[j].name);
+                                       errtryhelp(EXIT_FAILURE);
                                }
-                               iarg = atoi(argv[++i]);
+                               iarg = strtos32_or_err(argv[++i], _("failed to parse command argument"));
                        } else
                                iarg = bdcms[j].argval;
 
                        res = bdcms[j].flags & FL_NOPTR ?
-                                       ioctl(fd, bdcms[j].ioc, iarg) :
-                                       ioctl(fd, bdcms[j].ioc, &iarg);
+                           ioctl(fd, bdcms[j].ioc, iarg) :
+                           ioctl(fd, bdcms[j].ioc, &iarg);
                        break;
                case ARG_UINT:
                        uarg = bdcms[j].argval;
@@ -361,7 +412,7 @@ do_commands(int fd, char **argv, int d) {
                }
 
                if (res == -1) {
-                       perror(bdcms[j].iocname);
+                       warn(_("ioctl error on %s"), bdcms[j].iocname);
                        if (verbose)
                                printf(_("%s failed.\n"), _(bdcms[j].help));
                        exit(EXIT_FAILURE);
@@ -377,7 +428,7 @@ do_commands(int fd, char **argv, int d) {
                if (verbose)
                        printf("%s: ", _(bdcms[j].help));
 
-               switch(bdcms[j].argtype) {
+               switch (bdcms[j].argtype) {
                case ARG_USHRT:
                        printf("%hu\n", huarg);
                        break;
@@ -403,64 +454,88 @@ do_commands(int fd, char **argv, int d) {
        }
 }
 
-void
-report_all_devices(void) {
+static int report_all_devices(void)
+{
        FILE *procpt;
        char line[200];
-       char ptname[200];
+       char ptname[200 + 1];
        char device[210];
        int ma, mi, sz;
+       int rc = 0;
 
        procpt = fopen(_PATH_PROC_PARTITIONS, "r");
        if (!procpt)
                err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_PARTITIONS);
 
        while (fgets(line, sizeof(line), procpt)) {
-               if (sscanf (line, " %d %d %d %200[^\n ]",
-                           &ma, &mi, &sz, ptname) != 4)
+               if (sscanf(line, " %d %d %d %200[^\n ]",
+                          &ma, &mi, &sz, ptname) != 4)
                        continue;
 
-               sprintf(device, "/dev/%s", ptname);
-               report_device(device, 1);
+               snprintf(device, sizeof(device), "/dev/%s", ptname);
+               rc += report_device(device, 1);
        }
 
        fclose(procpt);
+       return rc;
 }
 
-void
-report_device(char *device, int quiet) {
+static int report_device(char *device, int quiet)
+{
        int fd;
        int ro, ssz, bsz;
+       int rc = 0;
        long ra;
        unsigned long long bytes;
-       struct hd_geometry g;
+       uint64_t start = 0;
+       char start_str[16] = { "\0" };
+       struct stat st;
 
        fd = open(device, O_RDONLY | O_NONBLOCK);
        if (fd < 0) {
                if (!quiet)
                        warn(_("cannot open %s"), device);
-               return;
+               return 1;
        }
 
        ro = ssz = bsz = 0;
-       g.start = ra = 0;
-       if (ioctl (fd, BLKROGET, &ro) == 0 &&
-           ioctl (fd, BLKRAGET, &ra) == 0 &&
-           ioctl (fd, BLKSSZGET, &ssz) == 0 &&
-           ioctl (fd, BLKBSZGET, &bsz) == 0 &&
-           ioctl (fd, HDIO_GETGEO, &g) == 0 &&
-           blkdev_get_size (fd, &bytes) == 0) {
-               printf("%s %5ld %5d %5d %10ld %15lld   %s\n",
-                      ro ? "ro" : "rw", ra, ssz, bsz, g.start, bytes, device);
+       ra = 0;
+       if (fstat(fd, &st) == 0) {
+               dev_t disk;
+               struct path_cxt *pc;
+
+               pc = ul_new_sysfs_path(st.st_rdev, NULL, NULL);
+               if (pc &&
+                   sysfs_blkdev_get_wholedisk(pc, NULL, 0, &disk) == 0 &&
+                   disk != st.st_rdev) {
+
+                       if (ul_path_read_u64(pc, &start, "start") != 0)
+                               /* TRANSLATORS: Start sector not available. Max. 15 letters. */
+                               snprintf(start_str, sizeof(start_str), "%15s", _("N/A"));
+               }
+               ul_unref_path(pc);
+       }
+       if (!*start_str)
+               snprintf(start_str, sizeof(start_str), "%15ju", start);
+
+       if (ioctl(fd, BLKROGET, &ro) == 0 &&
+           ioctl(fd, BLKRAGET, &ra) == 0 &&
+           ioctl(fd, BLKSSZGET, &ssz) == 0 &&
+           ioctl(fd, BLKBSZGET, &bsz) == 0 &&
+           blkdev_get_size(fd, &bytes) == 0) {
+               printf("%s %5ld %5d %5d %s %15lld   %s\n",
+                       ro ? "ro" : "rw", ra, ssz, bsz, start_str, bytes, device);
        } else {
                if (!quiet)
                        warnx(_("ioctl error on %s"), device);
+               rc = 1;
        }
 
        close(fd);
+       return rc;
 }
 
-void
-report_header() {
-       printf(_("RO    RA   SSZ   BSZ   StartSec            Size   Device\n"));
+static void report_header(void)
+{
+       printf(_("RO    RA   SSZ   BSZ        StartSec            Size   Device\n"));
 }