busybox/util-linux/blockdev.c
<<
>>
Prefs
   1/*
   2 * blockdev implementation for busybox
   3 *
   4 * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
   5 *
   6 * Licensed under GPLv2, see file LICENSE in this source tree.
   7 */
   8//config:config BLOCKDEV
   9//config:       bool "blockdev (2.3 kb)"
  10//config:       default y
  11//config:       help
  12//config:       Performs some ioctls with block devices.
  13
  14//applet:IF_BLOCKDEV(APPLET_NOEXEC(blockdev, blockdev, BB_DIR_SBIN, BB_SUID_DROP, blockdev))
  15
  16//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
  17
  18//usage:#define blockdev_trivial_usage
  19//usage:        "OPTION BLOCKDEV"
  20//usage:#define blockdev_full_usage "\n\n"
  21//usage:       "        --setro         Set ro"
  22//usage:     "\n        --setrw         Set rw"
  23//usage:     "\n        --getro         Get ro"
  24//usage:     "\n        --getss         Get sector size"
  25//usage:     "\n        --getbsz        Get block size"
  26//usage:     "\n        --setbsz BYTES  Set block size"
  27//usage:     "\n        --getsz         Get device size in 512-byte sectors"
  28///////:     "\n        --getsize       Get device size in sectors (deprecated)"
  29///////^^^^^ supported, but not shown in help ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  30//usage:     "\n        --getsize64     Get device size in bytes"
  31//usage:     "\n        --getra         Get readahead in 512-byte sectors"
  32//usage:     "\n        --setra SECTORS Set readahead"
  33//usage:     "\n        --flushbufs     Flush buffers"
  34//usage:     "\n        --rereadpt      Reread partition table"
  35// util-linux 2.31 also has:
  36//      --getdiscardzeroes      BLKDISCARDZEROES        Get discard zeroes support status
  37//      --getpbsz               BLKPBSZGET      Get physical block (sector) size
  38//      --getiomin              BLKIOMIN        Get minimum I/O size
  39//      --getioopt              BLKIOOPT        Get optimal I/O size
  40//      --getalignoff           BLKALIGNOFF     Get alignment offset in bytes
  41//      --getmaxsect            BLKSECTGET      Get max sectors per request
  42//      --setra SECTORS         BLKRASET        Set readahead
  43//      --getra                 BLKRAGET        Get readahead
  44//      --setfra SECTORS        BLKFRASET       Set filesystem readahead
  45//      --getfra                BLKFRAGET       Get filesystem readahead
  46
  47#include "libbb.h"
  48#include <linux/fs.h>
  49
  50/* Takes less space is separate arrays than one array of struct */
  51static const char bdcmd_names[] ALIGN1 =
  52        "setro"     "\0"
  53#define CMD_SETRO 0
  54        "setrw"     "\0"
  55        "getro"     "\0"
  56        "getss"     "\0"
  57        "getbsz"    "\0"
  58        "setbsz"    "\0"
  59#define CMD_SETBSZ 5
  60        "getsz"     "\0"
  61        "getsize"   "\0"
  62        "getsize64" "\0"
  63        "getra"     "\0"
  64        "setra"     "\0"
  65#define CMD_SETRA 10
  66        "flushbufs" "\0"
  67        "rereadpt"  "\0"
  68;
  69static const uint32_t bdcmd_ioctl[] ALIGN4 = {
  70        BLKROSET,       //setro
  71        BLKROSET,       //setrw
  72        BLKROGET,       //getro
  73        BLKSSZGET,      //getss
  74        BLKBSZGET,      //getbsz
  75        BLKBSZSET,      //setbsz
  76        BLKGETSIZE64,   //getsz
  77        BLKGETSIZE,     //getsize
  78        BLKGETSIZE64,   //getsize64
  79        BLKRAGET,       //getra
  80        BLKRASET,       //setra
  81        BLKFLSBUF,      //flushbufs
  82        BLKRRPART,      //rereadpt
  83};
  84enum {
  85        ARG_NONE   = 0,
  86        ARG_INT    = 1,
  87        ARG_ULONG  = 2,
  88        /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
  89        ARG_U64    = 3,
  90        ARG_MASK   = 3,
  91
  92        FL_USRARG   = 4, /* argument is provided by user */
  93        FL_NORESULT = 8,
  94        FL_SCALE512 = 16,
  95};
  96static const uint8_t bdcmd_flags[] ALIGN1 = {
  97        ARG_INT + FL_NORESULT,             //setro
  98        ARG_INT + FL_NORESULT,             //setrw
  99        ARG_INT,                           //getro
 100        ARG_INT,                           //getss
 101        ARG_INT,                           //getbsz
 102        ARG_INT + FL_NORESULT + FL_USRARG, //setbsz
 103        ARG_U64 + FL_SCALE512,             //getsz
 104        ARG_ULONG,                         //getsize
 105        ARG_U64,                           //getsize64
 106        ARG_ULONG,                         //getra
 107        ARG_ULONG + FL_NORESULT,           //setra
 108        ARG_NONE + FL_NORESULT,            //flushbufs
 109        ARG_NONE + FL_NORESULT,            //rereadpt
 110};
 111
 112static unsigned find_cmd(const char *s)
 113{
 114        if (s[0] == '-' && s[1] == '-') {
 115                int n = index_in_strings(bdcmd_names, s + 2);
 116                if (n >= 0)
 117                        return n;
 118        }
 119        bb_show_usage();
 120}
 121
 122int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 123int blockdev_main(int argc UNUSED_PARAM, char **argv)
 124{
 125        unsigned bdcmd;
 126        unsigned flags;
 127        int fd;
 128        uint64_t u64;
 129        union {
 130                int i;
 131                unsigned long lu;
 132                uint64_t u64;
 133        } ioctl_val_on_stack;
 134
 135        argv++;
 136        if (!argv[0] || !argv[1]) /* must have at least 2 args */
 137                bb_show_usage();
 138
 139        bdcmd = find_cmd(*argv);
 140        /* setrw translates to BLKROSET(0), most other ioctls don't care... */
 141        /* ...setro will do BLKROSET(1) */
 142        u64 = (bdcmd == CMD_SETRO);
 143        if (bdcmd == CMD_SETBSZ || bdcmd == CMD_SETRA) {
 144                /* ...setbsz is BLKBSZSET(bytes) */
 145                /* ...setra is BLKRASET(512 bytes) */
 146                u64 = xatoi_positive(*++argv);
 147        }
 148
 149        argv++;
 150        if (!argv[0] || argv[1])
 151                bb_show_usage();
 152        fd = xopen(argv[0], O_RDONLY);
 153
 154        ioctl_val_on_stack.u64 = u64;
 155        flags = bdcmd_flags[bdcmd];
 156#if BB_BIG_ENDIAN
 157        /* Store data properly wrt data size.
 158         * (1) It's no-op for little-endian.
 159         * (2) it's no-op for 0 and -1.
 160         * --setro uses arg != 0 and != -1, and it is ARG_INT
 161         * --setbsz USER_VAL is also ARG_INT
 162         * --setra USER_VAL is ARG_ULONG, but it is passed by value,
 163         * not reference (see below in ioctl call).
 164         * Thus, we don't need to handle ARG_ULONG.
 165         */
 166        switch (flags & ARG_MASK) {
 167        case ARG_INT:
 168                ioctl_val_on_stack.i = (int)u64;
 169                break;
 170# if 0 /* unused */
 171        case ARG_ULONG:
 172                ioctl_val_on_stack.lu = (unsigned long)u64;
 173                break;
 174# endif
 175        }
 176#endif
 177
 178        if (ioctl(fd, bdcmd_ioctl[bdcmd],
 179                        bdcmd == CMD_SETRA
 180                                ? (void*)(uintptr_t)u64 /* BLKRASET passes _value_, not pointer to it */
 181                                : &ioctl_val_on_stack.u64
 182                ) == -1)
 183                bb_simple_perror_msg_and_die(*argv);
 184
 185        /* Fetch it into register(s) */
 186        u64 = ioctl_val_on_stack.u64;
 187
 188        if (flags & FL_SCALE512)
 189                u64 >>= 9;
 190
 191        /* Zero- or one-extend the value if needed, then print */
 192        switch (flags & (ARG_MASK+FL_NORESULT)) {
 193        case ARG_INT:
 194                /* Smaller code when we use long long
 195                 * (gcc tail-merges printf call)
 196                 */
 197                printf("%lld\n", (long long)(int)u64);
 198                break;
 199        case ARG_ULONG:
 200                u64 = (unsigned long)u64;
 201                /* FALLTHROUGH */
 202        case ARG_U64:
 203                printf("%llu\n", (unsigned long long)u64);
 204                break;
 205        }
 206
 207        if (ENABLE_FEATURE_CLEAN_UP)
 208                close(fd);
 209        return EXIT_SUCCESS;
 210}
 211