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;
 | 
						|
}
 |