]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
Rename from bcache-tools to bcachefs-tools
[bcachefs-tools-debian] / tools-util.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <linux/fs.h>
7 #include <math.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <blkid.h>
17 #include <uuid/uuid.h>
18
19 #include "ccan/crc/crc.h"
20
21 #include "bcachefs_ioctl.h"
22 #include "linux/sort.h"
23 #include "tools-util.h"
24 #include "util.h"
25
26 /* Integer stuff: */
27
28 struct units_buf __pr_units(u64 v, enum units units)
29 {
30         struct units_buf ret;
31
32         switch (units) {
33         case BYTES:
34                 snprintf(ret.b, sizeof(ret.b), "%llu", v << 9);
35                 break;
36         case SECTORS:
37                 snprintf(ret.b, sizeof(ret.b), "%llu", v);
38                 break;
39         case HUMAN_READABLE:
40                 v <<= 9;
41
42                 if (v >= 1024) {
43                         int exp = log(v) / log(1024);
44                         snprintf(ret.b, sizeof(ret.b), "%.1f%c",
45                                  v / pow(1024, exp),
46                                  "KMGTPE"[exp-1]);
47                 } else {
48                         snprintf(ret.b, sizeof(ret.b), "%llu", v);
49                 }
50
51                 break;
52         }
53
54         return ret;
55 }
56
57 /* Argument parsing stuff: */
58
59 /* File parsing (i.e. sysfs) */
60
61 char *read_file_str(int dirfd, const char *path)
62 {
63         int fd = xopenat(dirfd, path, O_RDONLY);
64         ssize_t len = xfstat(fd).st_size;
65
66         char *buf = xmalloc(len + 1);
67
68         len = read(fd, buf, len);
69         if (len < 0)
70                 die("read error: %s", strerror(errno));
71
72         buf[len] = '\0';
73         if (len && buf[len - 1] == '\n')
74                 buf[len - 1] = '\0';
75
76         close(fd);
77
78         return buf;
79 }
80
81 u64 read_file_u64(int dirfd, const char *path)
82 {
83         char *buf = read_file_str(dirfd, path);
84         u64 v;
85         if (kstrtou64(buf, 10, &v))
86                 die("read_file_u64: error parsing %s (got %s)", path, buf);
87         free(buf);
88         return v;
89 }
90
91 /* String list options: */
92
93 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
94                                 const char *msg)
95 {
96         ssize_t v = bch2_read_string_list(opt, list);
97         if (v < 0)
98                 die("Bad %s %s", msg, opt);
99
100         return v;
101 }
102
103 /* Returns size of file or block device: */
104 u64 get_size(const char *path, int fd)
105 {
106         struct stat statbuf = xfstat(fd);
107
108         if (!S_ISBLK(statbuf.st_mode))
109                 return statbuf.st_size;
110
111         u64 ret;
112         xioctl(fd, BLKGETSIZE64, &ret);
113         return ret;
114 }
115
116 /* Returns blocksize in units of 512 byte sectors: */
117 unsigned get_blocksize(const char *path, int fd)
118 {
119         struct stat statbuf = xfstat(fd);
120
121         if (!S_ISBLK(statbuf.st_mode))
122                 return statbuf.st_blksize >> 9;
123
124         unsigned ret;
125         xioctl(fd, BLKPBSZGET, &ret);
126         return ret >> 9;
127 }
128
129 /* Open a block device, do magic blkid stuff to probe for existing filesystems: */
130 int open_for_format(const char *dev, bool force)
131 {
132         blkid_probe pr;
133         const char *fs_type = NULL, *fs_label = NULL;
134         size_t fs_type_len, fs_label_len;
135
136         int fd = xopen(dev, O_RDWR|O_EXCL);
137
138         if (force)
139                 return fd;
140
141         if (!(pr = blkid_new_probe()))
142                 die("blkid error 1");
143         if (blkid_probe_set_device(pr, fd, 0, 0))
144                 die("blkid error 2");
145         if (blkid_probe_enable_partitions(pr, true))
146                 die("blkid error 3");
147         if (blkid_do_fullprobe(pr) < 0)
148                 die("blkid error 4");
149
150         blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len);
151         blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len);
152
153         if (fs_type) {
154                 if (fs_label)
155                         printf("%s contains a %s filesystem labelled '%s'\n",
156                                dev, fs_type, fs_label);
157                 else
158                         printf("%s contains a %s filesystem\n",
159                                dev, fs_type);
160                 fputs("Proceed anyway?", stdout);
161                 if (!ask_yn())
162                         exit(EXIT_FAILURE);
163         }
164
165         blkid_free_probe(pr);
166         return fd;
167 }
168
169 /* Global control device: */
170 int bcachectl_open(void)
171 {
172         return xopen("/dev/bcachefs-ctl", O_RDWR);
173 }
174
175 /* Filesystem handles (ioctl, sysfs dir): */
176
177 #define SYSFS_BASE "/sys/fs/bcachefs/"
178
179 struct bcache_handle bcache_fs_open(const char *path)
180 {
181         struct bcache_handle ret;
182         uuid_t tmp;
183
184         if (!uuid_parse(path, tmp)) {
185                 /* It's a UUID, look it up in sysfs: */
186                 char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
187                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
188
189                 char *minor = read_file_str(ret.sysfs_fd, "minor");
190                 char *ctl = mprintf("/dev/bcachefs%s-ctl", minor);
191                 ret.ioctl_fd = xopen(ctl, O_RDWR);
192
193                 free(sysfs);
194                 free(minor);
195                 free(ctl);
196         } else {
197                 /* It's a path: */
198                 ret.ioctl_fd = xopen(path, O_RDONLY);
199
200                 struct bch_ioctl_query_uuid uuid;
201                 xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid);
202
203                 char uuid_str[40];
204                 uuid_unparse(uuid.uuid.b, uuid_str);
205
206                 char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
207                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
208                 free(sysfs);
209         }
210
211         return ret;
212 }
213
214 bool ask_yn(void)
215 {
216         const char *short_yes = "yY";
217         char *buf = NULL;
218         size_t buflen = 0;
219         bool ret;
220
221         fputs(" (y,n) ", stdout);
222         fflush(stdout);
223
224         if (getline(&buf, &buflen, stdin) < 0)
225                 die("error reading from standard input");
226
227         ret = strchr(short_yes, buf[0]);
228         free(buf);
229         return ret;
230 }
231
232 static int range_cmp(const void *_l, const void *_r)
233 {
234         const struct range *l = _l, *r = _r;
235
236         if (l->start < r->start)
237                 return -1;
238         if (l->start > r->start)
239                 return  1;
240         return 0;
241 }
242
243 void ranges_sort_merge(ranges *r)
244 {
245         struct range *t, *i;
246         ranges tmp = { NULL };
247
248         sort(&darray_item(*r, 0), darray_size(*r),
249              sizeof(darray_item(*r, 0)), range_cmp, NULL);
250
251         /* Merge contiguous ranges: */
252         darray_foreach(i, *r) {
253                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
254
255                 if (t && t->end >= i->start)
256                         t->end = max(t->end, i->end);
257                 else
258                         darray_append(tmp, *i);
259         }
260
261         darray_free(*r);
262         *r = tmp;
263 }
264
265 void ranges_roundup(ranges *r, unsigned block_size)
266 {
267         struct range *i;
268
269         darray_foreach(i, *r) {
270                 i->start = round_down(i->start, block_size);
271                 i->end  = round_up(i->end, block_size);
272         }
273 }
274
275 void ranges_rounddown(ranges *r, unsigned block_size)
276 {
277         struct range *i;
278
279         darray_foreach(i, *r) {
280                 i->start = round_up(i->start, block_size);
281                 i->end  = round_down(i->end, block_size);
282                 i->end  = max(i->end, i->start);
283         }
284 }
285
286 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
287 {
288         struct fiemap_extent e;
289
290         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
291
292         if (iter->idx == iter->f.fm_mapped_extents) {
293                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
294
295                 if (!iter->f.fm_mapped_extents)
296                         return (struct fiemap_extent) { .fe_length = 0 };
297
298                 iter->idx = 0;
299         }
300
301         e = iter->f.fm_extents[iter->idx++];
302         BUG_ON(!e.fe_length);
303
304         iter->f.fm_start = e.fe_logical + e.fe_length;
305
306         return e;
307 }
308
309 const char *strcmp_prefix(const char *a, const char *a_prefix)
310 {
311         while (*a_prefix && *a == *a_prefix) {
312                 a++;
313                 a_prefix++;
314         }
315         return *a_prefix ? NULL : a;
316 }
317
318 unsigned hatoi_validate(const char *s, const char *msg)
319 {
320         u64 v;
321
322         if (bch2_strtoull_h(s, &v))
323                 die("bad %s %s", msg, s);
324
325         if (v & (v - 1))
326                 die("%s must be a power of two", msg);
327
328         v /= 512;
329
330         if (v > USHRT_MAX)
331                 die("%s too large\n", msg);
332
333         if (!v)
334                 die("%s too small\n", msg);
335
336         return v;
337 }