mdev: add support to run as daemon
[oweals/busybox.git] / util-linux / blockdev.c
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 /*//usage:     "\n      --getsize       Get device size in sectors (deprecated)"*/
29 //usage:     "\n        --getsize64     Get device size in bytes"
30 //usage:     "\n        --flushbufs     Flush buffers"
31 //usage:     "\n        --rereadpt      Reread partition table"
32
33
34 #include "libbb.h"
35 #include <linux/fs.h>
36
37 /* Takes less space is separate arrays than one array of struct */
38 static const char bdcmd_names[] ALIGN1 =
39         "setro"     "\0"
40 #define CMD_SETRO 0
41         "setrw"     "\0"
42         "getro"     "\0"
43         "getss"     "\0"
44         "getbsz"    "\0"
45         "setbsz"    "\0"
46 #define CMD_SETBSZ 5
47         "getsz"     "\0"
48         "getsize"   "\0"
49         "getsize64" "\0"
50         "flushbufs" "\0"
51         "rereadpt"  "\0"
52 ;
53 static const uint32_t bdcmd_ioctl[] = {
54         BLKROSET,       //setro
55         BLKROSET,       //setrw
56         BLKROGET,       //getro
57         BLKSSZGET,      //getss
58         BLKBSZGET,      //getbsz
59         BLKBSZSET,      //setbsz
60         BLKGETSIZE64,   //getsz
61         BLKGETSIZE,     //getsize
62         BLKGETSIZE64,   //getsize64
63         BLKFLSBUF,      //flushbufs
64         BLKRRPART,      //rereadpt
65 };
66 enum {
67         ARG_NONE   = 0,
68         ARG_INT    = 1,
69         ARG_ULONG  = 2,
70         /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
71         ARG_U64    = 3,
72         ARG_MASK   = 3,
73
74         FL_USRARG   = 4, /* argument is provided by user */
75         FL_NORESULT = 8,
76         FL_SCALE512 = 16,
77 };
78 static const uint8_t bdcmd_flags[] ALIGN1 = {
79         ARG_INT + FL_NORESULT,             //setro
80         ARG_INT + FL_NORESULT,             //setrw
81         ARG_INT,                           //getro
82         ARG_INT,                           //getss
83         ARG_INT,                           //getbsz
84         ARG_INT + FL_NORESULT + FL_USRARG, //setbsz
85         ARG_U64 + FL_SCALE512,             //getsz
86         ARG_ULONG,                         //getsize
87         ARG_U64,                           //getsize64
88         ARG_NONE + FL_NORESULT,            //flushbufs
89         ARG_NONE + FL_NORESULT,            //rereadpt
90 };
91
92 static unsigned find_cmd(const char *s)
93 {
94         if (s[0] == '-' && s[1] == '-') {
95                 int n = index_in_strings(bdcmd_names, s + 2);
96                 if (n >= 0)
97                         return n;
98         }
99         bb_show_usage();
100 }
101
102 int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
103 int blockdev_main(int argc UNUSED_PARAM, char **argv)
104 {
105         unsigned bdcmd;
106         unsigned flags;
107         int fd;
108         uint64_t u64;
109         union {
110                 int i;
111                 unsigned long lu;
112                 uint64_t u64;
113         } ioctl_val_on_stack;
114
115         argv++;
116         if (!argv[0] || !argv[1]) /* must have at least 2 args */
117                 bb_show_usage();
118
119         bdcmd = find_cmd(*argv);
120         /* setrw translates to BLKROSET(0), most other ioctls don't care... */
121         /* ...setro will do BLKROSET(1) */
122         u64 = (bdcmd == CMD_SETRO);
123         if (bdcmd == CMD_SETBSZ) {
124                 /* ...setbsz is BLKBSZSET(bytes) */
125                 u64 = xatoi_positive(*++argv);
126         }
127
128         argv++;
129         if (!argv[0] || argv[1])
130                 bb_show_usage();
131         fd = xopen(argv[0], O_RDONLY);
132
133         ioctl_val_on_stack.u64 = u64;
134         flags = bdcmd_flags[bdcmd];
135 #if BB_BIG_ENDIAN
136         /* Store data properly wrt data size.
137          * (1) It's no-op for little-endian.
138          * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
139          * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
140          * Thus, we don't need to handle ARG_ULONG.
141          */
142         switch (flags & ARG_MASK) {
143         case ARG_INT:
144                 ioctl_val_on_stack.i = (int)u64;
145                 break;
146 # if 0 /* unused */
147         case ARG_ULONG:
148                 ioctl_val_on_stack.lu = (unsigned long)u64;
149                 break;
150 # endif
151         }
152 #endif
153
154         if (ioctl(fd, bdcmd_ioctl[bdcmd], &ioctl_val_on_stack.u64) == -1)
155                 bb_simple_perror_msg_and_die(*argv);
156
157         /* Fetch it into register(s) */
158         u64 = ioctl_val_on_stack.u64;
159
160         if (flags & FL_SCALE512)
161                 u64 >>= 9;
162
163         /* Zero- or one-extend the value if needed, then print */
164         switch (flags & (ARG_MASK+FL_NORESULT)) {
165         case ARG_INT:
166                 /* Smaller code when we use long long
167                  * (gcc tail-merges printf call)
168                  */
169                 printf("%lld\n", (long long)(int)u64);
170                 break;
171         case ARG_ULONG:
172                 u64 = (unsigned long)u64;
173                 /* FALLTHROUGH */
174         case ARG_U64:
175                 printf("%llu\n", (unsigned long long)u64);
176                 break;
177         }
178
179         if (ENABLE_FEATURE_CLEAN_UP)
180                 close(fd);
181         return EXIT_SUCCESS;
182 }