023ae75fc6
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
193 lines
5.1 KiB
C
193 lines
5.1 KiB
C
/*
|
|
* 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"
|
|
/*//usage: "\n --getsize Get device size in sectors (deprecated)"*/
|
|
//usage: "\n --getsize64 Get device size in bytes"
|
|
//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"
|
|
"flushbufs" "\0"
|
|
"rereadpt" "\0"
|
|
;
|
|
static const uint32_t bdcmd_ioctl[] = {
|
|
BLKROSET, //setro
|
|
BLKROSET, //setrw
|
|
BLKROGET, //getro
|
|
BLKSSZGET, //getss
|
|
BLKBSZGET, //getbsz
|
|
BLKBSZSET, //setbsz
|
|
BLKGETSIZE64, //getsz
|
|
BLKGETSIZE, //getsize
|
|
BLKGETSIZE64, //getsize64
|
|
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_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) {
|
|
/* ...setbsz is BLKBSZSET(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. Only --setro uses arg != 0 and != -1,
|
|
* and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
|
|
* 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], &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;
|
|
}
|