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