/* * blockdev implementation for busybox * * Copyright (C) 2010 Sergey Naumov * * 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 /* 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[] ALIGN4 = { 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; }