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