blob: bf4d49792eaa3184ebce3e4a268b98b5ef9efa33
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" |
10 | //config: default y |
11 | //config: help |
12 | //config: Performs some ioctls with block devices. |
13 | |
14 | //applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP)) |
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 | enum { |
38 | ARG_NONE = 0, |
39 | ARG_INT = 1, |
40 | ARG_ULONG = 2, |
41 | /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */ |
42 | ARG_U64 = 3, |
43 | ARG_MASK = 3, |
44 | |
45 | FL_USRARG = 4, /* argument is provided by user */ |
46 | FL_NORESULT = 8, |
47 | FL_SCALE512 = 16, |
48 | }; |
49 | |
50 | struct bdc { |
51 | uint32_t ioc; /* ioctl code */ |
52 | const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */ |
53 | uint8_t flags; |
54 | int8_t argval; /* default argument value */ |
55 | }; |
56 | |
57 | static const struct bdc bdcommands[] = { |
58 | { |
59 | .ioc = BLKROSET, |
60 | .name = "setro", |
61 | .flags = ARG_INT + FL_NORESULT, |
62 | .argval = 1, |
63 | },{ |
64 | .ioc = BLKROSET, |
65 | .name = "setrw", |
66 | .flags = ARG_INT + FL_NORESULT, |
67 | .argval = 0, |
68 | },{ |
69 | .ioc = BLKROGET, |
70 | .name = "getro", |
71 | .flags = ARG_INT, |
72 | .argval = -1, |
73 | },{ |
74 | .ioc = BLKSSZGET, |
75 | .name = "getss", |
76 | .flags = ARG_INT, |
77 | .argval = -1, |
78 | },{ |
79 | .ioc = BLKBSZGET, |
80 | .name = "getbsz", |
81 | .flags = ARG_INT, |
82 | .argval = -1, |
83 | },{ |
84 | .ioc = BLKBSZSET, |
85 | .name = "setbsz", |
86 | .flags = ARG_INT + FL_NORESULT + FL_USRARG, |
87 | .argval = 0, |
88 | },{ |
89 | .ioc = BLKGETSIZE64, |
90 | .name = "getsz", |
91 | .flags = ARG_U64 + FL_SCALE512, |
92 | .argval = -1, |
93 | },{ |
94 | .ioc = BLKGETSIZE, |
95 | .name = "getsize", |
96 | .flags = ARG_ULONG, |
97 | .argval = -1, |
98 | },{ |
99 | .ioc = BLKGETSIZE64, |
100 | .name = "getsize64", |
101 | .flags = ARG_U64, |
102 | .argval = -1, |
103 | },{ |
104 | .ioc = BLKFLSBUF, |
105 | .name = "flushbufs", |
106 | .flags = ARG_NONE + FL_NORESULT, |
107 | .argval = 0, |
108 | },{ |
109 | .ioc = BLKRRPART, |
110 | .name = "rereadpt", |
111 | .flags = ARG_NONE + FL_NORESULT, |
112 | .argval = 0, |
113 | } |
114 | }; |
115 | |
116 | static const struct bdc *find_cmd(const char *s) |
117 | { |
118 | const struct bdc *bdcmd = bdcommands; |
119 | if (s[0] == '-' && s[1] == '-') { |
120 | s += 2; |
121 | do { |
122 | if (strcmp(s, bdcmd->name) == 0) |
123 | return bdcmd; |
124 | bdcmd++; |
125 | } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands)); |
126 | } |
127 | bb_show_usage(); |
128 | } |
129 | |
130 | int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
131 | int blockdev_main(int argc UNUSED_PARAM, char **argv) |
132 | { |
133 | const struct bdc *bdcmd; |
134 | int fd; |
135 | uint64_t u64; |
136 | union { |
137 | int i; |
138 | unsigned long lu; |
139 | uint64_t u64; |
140 | } ioctl_val_on_stack; |
141 | |
142 | argv++; |
143 | if (!argv[0] || !argv[1]) /* must have at least 2 args */ |
144 | bb_show_usage(); |
145 | |
146 | bdcmd = find_cmd(*argv); |
147 | |
148 | u64 = (int)bdcmd->argval; |
149 | if (bdcmd->flags & FL_USRARG) |
150 | u64 = xatoi_positive(*++argv); |
151 | |
152 | argv++; |
153 | if (!argv[0] || argv[1]) |
154 | bb_show_usage(); |
155 | fd = xopen(argv[0], O_RDONLY); |
156 | |
157 | ioctl_val_on_stack.u64 = u64; |
158 | #if BB_BIG_ENDIAN |
159 | /* Store data properly wrt data size. |
160 | * (1) It's no-op for little-endian. |
161 | * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1, |
162 | * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT. |
163 | * Thus, we don't need to handle ARG_ULONG. |
164 | */ |
165 | switch (bdcmd->flags & ARG_MASK) { |
166 | case ARG_INT: |
167 | ioctl_val_on_stack.i = (int)u64; |
168 | break; |
169 | # if 0 /* unused */ |
170 | case ARG_ULONG: |
171 | ioctl_val_on_stack.lu = (unsigned long)u64; |
172 | break; |
173 | # endif |
174 | } |
175 | #endif |
176 | |
177 | if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1) |
178 | bb_simple_perror_msg_and_die(*argv); |
179 | |
180 | /* Fetch it into register(s) */ |
181 | u64 = ioctl_val_on_stack.u64; |
182 | |
183 | if (bdcmd->flags & FL_SCALE512) |
184 | u64 >>= 9; |
185 | |
186 | /* Zero- or one-extend the value if needed, then print */ |
187 | switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) { |
188 | case ARG_INT: |
189 | /* Smaller code when we use long long |
190 | * (gcc tail-merges printf call) |
191 | */ |
192 | printf("%lld\n", (long long)(int)u64); |
193 | break; |
194 | case ARG_ULONG: |
195 | u64 = (unsigned long)u64; |
196 | /* FALLTHROUGH */ |
197 | case ARG_U64: |
198 | printf("%llu\n", (unsigned long long)u64); |
199 | break; |
200 | } |
201 | |
202 | if (ENABLE_FEATURE_CLEAN_UP) |
203 | close(fd); |
204 | return EXIT_SUCCESS; |
205 | } |
206 |