]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
bcache in userspace; userspace fsck
[bcachefs-tools-debian] / tools-util.c
1 #include <alloca.h>
2 #include <assert.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <linux/fs.h>
8 #include <math.h>
9 #include <stdbool.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/ioctl.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include <uuid/uuid.h>
18
19 #include "ccan/crc/crc.h"
20
21 #include "tools-util.h"
22
23 /* Integer stuff: */
24
25 struct units_buf pr_units(u64 v, enum units units)
26 {
27         struct units_buf ret;
28
29         switch (units) {
30         case BYTES:
31                 snprintf(ret.b, sizeof(ret.b), "%llu", v << 9);
32                 break;
33         case SECTORS:
34                 snprintf(ret.b, sizeof(ret.b), "%llu", v);
35                 break;
36         case HUMAN_READABLE:
37                 v <<= 9;
38
39                 if (v >= 1024) {
40                         int exp = log(v) / log(1024);
41                         snprintf(ret.b, sizeof(ret.b), "%.1f%c",
42                                  v / pow(1024, exp),
43                                  "KMGTPE"[exp-1]);
44                 } else {
45                         snprintf(ret.b, sizeof(ret.b), "%llu", v);
46                 }
47
48                 break;
49         }
50
51         return ret;
52 }
53
54 /* Argument parsing stuff: */
55
56 long strtoul_or_die(const char *p, size_t max, const char *msg)
57 {
58         errno = 0;
59         long v = strtol(p, NULL, 10);
60         if (errno || v < 0 || v >= max)
61                 die("Invalid %s %zi", msg, v);
62
63         return v;
64 }
65
66 u64 hatoi(const char *s)
67 {
68         char *e;
69         long long i = strtoll(s, &e, 10);
70         switch (*e) {
71                 case 't':
72                 case 'T':
73                         i *= 1024;
74                 case 'g':
75                 case 'G':
76                         i *= 1024;
77                 case 'm':
78                 case 'M':
79                         i *= 1024;
80                 case 'k':
81                 case 'K':
82                         i *= 1024;
83         }
84         return i;
85 }
86
87 unsigned hatoi_validate(const char *s, const char *msg)
88 {
89         u64 v = hatoi(s);
90
91         if (v & (v - 1))
92                 die("%s must be a power of two", msg);
93
94         v /= 512;
95
96         if (v > USHRT_MAX)
97                 die("%s too large\n", msg);
98
99         if (!v)
100                 die("%s too small\n", msg);
101
102         return v;
103 }
104
105 unsigned nr_args(char * const *args)
106 {
107         unsigned i;
108
109         for (i = 0; args[i]; i++)
110                 ;
111
112         return i;
113 }
114
115 /* File parsing (i.e. sysfs) */
116
117 char *read_file_str(int dirfd, const char *path)
118 {
119         int fd = openat(dirfd, path, O_RDONLY);
120
121         if (fd < 0)
122                 die("Unable to open %s\n", path);
123
124         struct stat statbuf;
125         if (fstat(fd, &statbuf) < 0)
126                 die("fstat error\n");
127
128         char *buf = malloc(statbuf.st_size + 1);
129
130         int len = read(fd, buf, statbuf.st_size);
131         if (len < 0)
132                 die("read error while reading from file %s\n", path);
133
134         buf[len] = '\0';
135         if (len && buf[len - 1] == '\n')
136                 buf[len - 1] = '\0';
137
138         close(fd);
139
140         return buf;
141 }
142
143 u64 read_file_u64(int dirfd, const char *path)
144 {
145         char *buf = read_file_str(dirfd, path);
146         u64 ret = strtoll(buf, NULL, 10);
147
148         free(buf);
149         return ret;
150 }
151
152 /* String list options: */
153
154 ssize_t read_string_list(const char *buf, const char * const list[])
155 {
156         size_t i;
157         char *s, *d = strdup(buf);
158         if (!d)
159                 return -ENOMEM;
160
161         s = strim(d);
162
163         for (i = 0; list[i]; i++)
164                 if (!strcmp(list[i], s))
165                         break;
166
167         free(d);
168
169         if (!list[i])
170                 return -EINVAL;
171
172         return i;
173 }
174
175 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
176                                 const char *msg)
177 {
178         ssize_t v = read_string_list(opt, list);
179         if (v < 0)
180                 die("Bad %s %s", msg, opt);
181
182         return v;
183 }
184
185 void print_string_list(const char * const list[], size_t selected)
186 {
187         size_t i;
188
189         for (i = 0; list[i]; i++) {
190                 if (i)
191                         putchar(' ');
192                 printf(i == selected ? "[%s] ": "%s", list[i]);
193         }
194 }
195
196 /* Returns size of file or block device, in units of 512 byte sectors: */
197 u64 get_size(const char *path, int fd)
198 {
199         struct stat statbuf;
200         if (fstat(fd, &statbuf))
201                 die("Error statting %s: %s", path, strerror(errno));
202
203         if (!S_ISBLK(statbuf.st_mode))
204                 return statbuf.st_size >> 9;
205
206         u64 ret;
207         if (ioctl(fd, BLKGETSIZE64, &ret))
208                 die("Error getting block device size on %s: %s\n",
209                     path, strerror(errno));
210
211         return ret >> 9;
212 }
213
214 /* Returns blocksize in units of 512 byte sectors: */
215 unsigned get_blocksize(const char *path, int fd)
216 {
217         struct stat statbuf;
218         if (fstat(fd, &statbuf))
219                 die("Error statting %s: %s", path, strerror(errno));
220
221         if (!S_ISBLK(statbuf.st_mode))
222                 return statbuf.st_blksize >> 9;
223
224         unsigned ret;
225         if (ioctl(fd, BLKPBSZGET, &ret))
226                 die("Error getting blocksize on %s: %s\n",
227                     path, strerror(errno));
228
229         return ret >> 9;
230 }
231
232 /* Global control device: */
233 int bcachectl_open(void)
234 {
235         int fd = open("/dev/bcache-ctl", O_RDWR);
236         if (fd < 0)
237                 die("Can't open bcache device: %s", strerror(errno));
238
239         return fd;
240 }
241
242 /* Filesystem handles (ioctl, sysfs dir): */
243
244 #define SYSFS_BASE "/sys/fs/bcache/"
245
246 struct bcache_handle bcache_fs_open(const char *path)
247 {
248         struct bcache_handle ret;
249         uuid_t tmp;
250
251         if (!uuid_parse(path, tmp)) {
252                 /* It's a UUID, look it up in sysfs: */
253
254                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(path) + 1);
255                 sprintf(sysfs, "%s%s", SYSFS_BASE, path);
256
257                 ret.sysfs_fd = open(sysfs, O_RDONLY);
258                 if (!ret.sysfs_fd)
259                         die("Unable to open %s\n", path);
260
261                 char *minor = read_file_str(ret.sysfs_fd, "minor");
262                 char *ctl = alloca(20 + strlen(minor));
263
264                 sprintf(ctl, "/dev/bcache%s-ctl", minor);
265                 free(minor);
266
267                 ret.ioctl_fd = open(ctl, O_RDWR);
268                 if (ret.ioctl_fd < 0)
269                         die("Error opening control device: %s\n",
270                             strerror(errno));
271         } else {
272                 /* It's a path: */
273
274                 ret.ioctl_fd = open(path, O_RDONLY);
275                 if (ret.ioctl_fd < 0)
276                         die("Error opening %s: %s\n",
277                             path, strerror(errno));
278
279                 struct bch_ioctl_query_uuid uuid;
280                 if (ioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid))
281                         die("ioctl error (not a bcache fs?): %s\n",
282                             strerror(errno));
283
284                 char uuid_str[40];
285                 uuid_unparse(uuid.uuid.b, uuid_str);
286
287                 char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(uuid_str) + 1);
288                 sprintf(sysfs, "%s%s", SYSFS_BASE, uuid_str);
289
290                 ret.sysfs_fd = open(sysfs, O_RDONLY);
291                 if (ret.sysfs_fd < 0)
292                         die("Unable to open sysfs dir %s: %s\n",
293                             sysfs, strerror(errno));
294         }
295
296         return ret;
297 }
298
299 bool ask_proceed(void)
300 {
301         const char *short_yes = "yY";
302         char *buf = NULL;
303         size_t buflen = 0;
304         bool ret;
305
306         fputs("Proceed anyway? (y,n) ", stdout);
307
308         if (getline(&buf, &buflen, stdin) < 0)
309                 die("error reading from standard input");
310
311         ret = strchr(short_yes, buf[0]);
312         free(buf);
313         return ret;
314 }