blob: 3b550220a124c12d0f8abf32d2e0f5fc5898618e [file] [log] [blame]
/*
* blockdev implementation for busybox
*
* Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
//config:config BLOCKDEV
//config: bool "blockdev (2.3 kb)"
//config: default y
//config: help
//config: Performs some ioctls with block devices.
//applet:IF_BLOCKDEV(APPLET_NOEXEC(blockdev, blockdev, BB_DIR_SBIN, BB_SUID_DROP, blockdev))
//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
//usage:#define blockdev_trivial_usage
//usage: "OPTION BLOCKDEV"
//usage:#define blockdev_full_usage "\n\n"
//usage: " --setro Set ro"
//usage: "\n --setrw Set rw"
//usage: "\n --getro Get ro"
//usage: "\n --getss Get sector size"
//usage: "\n --getbsz Get block size"
//usage: "\n --setbsz BYTES Set block size"
//usage: "\n --getsz Get device size in 512-byte sectors"
///////: "\n --getsize Get device size in sectors (deprecated)"
///////^^^^^ supported, but not shown in help ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//usage: "\n --getsize64 Get device size in bytes"
//usage: "\n --getra Get readahead in 512-byte sectors"
//usage: "\n --setra SECTORS Set readahead"
//usage: "\n --flushbufs Flush buffers"
//usage: "\n --rereadpt Reread partition table"
// util-linux 2.31 also has:
// --getdiscardzeroes BLKDISCARDZEROES Get discard zeroes support status
// --getpbsz BLKPBSZGET Get physical block (sector) size
// --getiomin BLKIOMIN Get minimum I/O size
// --getioopt BLKIOOPT Get optimal I/O size
// --getalignoff BLKALIGNOFF Get alignment offset in bytes
// --getmaxsect BLKSECTGET Get max sectors per request
// --setra SECTORS BLKRASET Set readahead
// --getra BLKRAGET Get readahead
// --setfra SECTORS BLKFRASET Set filesystem readahead
// --getfra BLKFRAGET Get filesystem readahead
#include "libbb.h"
#include <linux/fs.h>
/* Takes less space is separate arrays than one array of struct */
static const char bdcmd_names[] ALIGN1 =
"setro" "\0"
#define CMD_SETRO 0
"setrw" "\0"
"getro" "\0"
"getss" "\0"
"getbsz" "\0"
"setbsz" "\0"
#define CMD_SETBSZ 5
"getsz" "\0"
"getsize" "\0"
"getsize64" "\0"
"getra" "\0"
"setra" "\0"
#define CMD_SETRA 10
"flushbufs" "\0"
"rereadpt" "\0"
;
static const uint32_t bdcmd_ioctl[] ALIGN4 = {
BLKROSET, //setro
BLKROSET, //setrw
BLKROGET, //getro
BLKSSZGET, //getss
BLKBSZGET, //getbsz
BLKBSZSET, //setbsz
BLKGETSIZE64, //getsz
BLKGETSIZE, //getsize
BLKGETSIZE64, //getsize64
BLKRAGET, //getra
BLKRASET, //setra
BLKFLSBUF, //flushbufs
BLKRRPART, //rereadpt
};
enum {
ARG_NONE = 0,
ARG_INT = 1,
ARG_ULONG = 2,
/* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
ARG_U64 = 3,
ARG_MASK = 3,
FL_USRARG = 4, /* argument is provided by user */
FL_NORESULT = 8,
FL_SCALE512 = 16,
};
static const uint8_t bdcmd_flags[] ALIGN1 = {
ARG_INT + FL_NORESULT, //setro
ARG_INT + FL_NORESULT, //setrw
ARG_INT, //getro
ARG_INT, //getss
ARG_INT, //getbsz
ARG_INT + FL_NORESULT + FL_USRARG, //setbsz
ARG_U64 + FL_SCALE512, //getsz
ARG_ULONG, //getsize
ARG_U64, //getsize64
ARG_ULONG, //getra
ARG_ULONG + FL_NORESULT, //setra
ARG_NONE + FL_NORESULT, //flushbufs
ARG_NONE + FL_NORESULT, //rereadpt
};
static unsigned find_cmd(const char *s)
{
if (s[0] == '-' && s[1] == '-') {
int n = index_in_strings(bdcmd_names, s + 2);
if (n >= 0)
return n;
}
bb_show_usage();
}
int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int blockdev_main(int argc UNUSED_PARAM, char **argv)
{
unsigned bdcmd;
unsigned flags;
int fd;
uint64_t u64;
union {
int i;
unsigned long lu;
uint64_t u64;
} ioctl_val_on_stack;
argv++;
if (!argv[0] || !argv[1]) /* must have at least 2 args */
bb_show_usage();
bdcmd = find_cmd(*argv);
/* setrw translates to BLKROSET(0), most other ioctls don't care... */
/* ...setro will do BLKROSET(1) */
u64 = (bdcmd == CMD_SETRO);
if (bdcmd == CMD_SETBSZ || bdcmd == CMD_SETRA) {
/* ...setbsz is BLKBSZSET(bytes) */
/* ...setra is BLKRASET(512 bytes) */
u64 = xatoi_positive(*++argv);
}
argv++;
if (!argv[0] || argv[1])
bb_show_usage();
fd = xopen(argv[0], O_RDONLY);
ioctl_val_on_stack.u64 = u64;
flags = bdcmd_flags[bdcmd];
#if BB_BIG_ENDIAN
/* Store data properly wrt data size.
* (1) It's no-op for little-endian.
* (2) it's no-op for 0 and -1.
* --setro uses arg != 0 and != -1, and it is ARG_INT
* --setbsz USER_VAL is also ARG_INT
* --setra USER_VAL is ARG_ULONG, but it is passed by value,
* not reference (see below in ioctl call).
* Thus, we don't need to handle ARG_ULONG.
*/
switch (flags & ARG_MASK) {
case ARG_INT:
ioctl_val_on_stack.i = (int)u64;
break;
# if 0 /* unused */
case ARG_ULONG:
ioctl_val_on_stack.lu = (unsigned long)u64;
break;
# endif
}
#endif
if (ioctl(fd, bdcmd_ioctl[bdcmd],
bdcmd == CMD_SETRA
? (void*)(uintptr_t)u64 /* BLKRASET passes _value_, not pointer to it */
: &ioctl_val_on_stack.u64
) == -1)
bb_simple_perror_msg_and_die(*argv);
/* Fetch it into register(s) */
u64 = ioctl_val_on_stack.u64;
if (flags & FL_SCALE512)
u64 >>= 9;
/* Zero- or one-extend the value if needed, then print */
switch (flags & (ARG_MASK+FL_NORESULT)) {
case ARG_INT:
/* Smaller code when we use long long
* (gcc tail-merges printf call)
*/
printf("%lld\n", (long long)(int)u64);
break;
case ARG_ULONG:
u64 = (unsigned long)u64;
/* FALLTHROUGH */
case ARG_U64:
printf("%llu\n", (unsigned long long)u64);
break;
}
if (ENABLE_FEATURE_CLEAN_UP)
close(fd);
return EXIT_SUCCESS;
}