]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
fix sync writes - don't use O_EXCL
[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 void die(const char *fmt, ...)
27 {
28         va_list args;
29
30         va_start(args, fmt);
31         vfprintf(stderr, fmt, args);
32         va_end(args);
33         fputc('\n', stderr);
34
35         exit(EXIT_FAILURE);
36 }
37
38 char *mprintf(const char *fmt, ...)
39 {
40         va_list args;
41         char *str;
42         int ret;
43
44         va_start(args, fmt);
45         ret = vasprintf(&str, fmt, args);
46         va_end(args);
47
48         if (ret < 0)
49                 die("insufficient memory");
50
51         return str;
52 }
53
54 void *xcalloc(size_t count, size_t size)
55 {
56         void *p = calloc(count, size);
57
58         if (!p)
59                 die("insufficient memory");
60
61         return p;
62 }
63
64 void *xmalloc(size_t size)
65 {
66         void *p = malloc(size);
67
68         if (!p)
69                 die("insufficient memory");
70
71         memset(p, 0, size);
72         return p;
73 }
74
75 void xpread(int fd, void *buf, size_t count, off_t offset)
76 {
77         ssize_t r = pread(fd, buf, count, offset);
78
79         if (r != count)
80                 die("read error (ret %zi)", r);
81 }
82
83 void xpwrite(int fd, const void *buf, size_t count, off_t offset)
84 {
85         ssize_t r = pwrite(fd, buf, count, offset);
86
87         if (r != count)
88                 die("write error (ret %zi err %m)", r);
89 }
90
91 struct stat xfstatat(int dirfd, const char *path, int flags)
92 {
93         struct stat stat;
94         if (fstatat(dirfd, path, &stat, flags))
95                 die("stat error: %m");
96         return stat;
97 }
98
99 struct stat xfstat(int fd)
100 {
101         struct stat stat;
102         if (fstat(fd, &stat))
103                 die("stat error: %m");
104         return stat;
105 }
106
107 /* Integer stuff: */
108
109 struct units_buf __pr_units(u64 v, enum units units)
110 {
111         struct units_buf ret;
112
113         switch (units) {
114         case BYTES:
115                 snprintf(ret.b, sizeof(ret.b), "%llu", v << 9);
116                 break;
117         case SECTORS:
118                 snprintf(ret.b, sizeof(ret.b), "%llu", v);
119                 break;
120         case HUMAN_READABLE:
121                 v <<= 9;
122
123                 if (v >= 1024) {
124                         int exp = log(v) / log(1024);
125                         snprintf(ret.b, sizeof(ret.b), "%.1f%c",
126                                  v / pow(1024, exp),
127                                  "KMGTPE"[exp-1]);
128                 } else {
129                         snprintf(ret.b, sizeof(ret.b), "%llu", v);
130                 }
131
132                 break;
133         }
134
135         return ret;
136 }
137
138 /* Argument parsing stuff: */
139
140 /* File parsing (i.e. sysfs) */
141
142 char *read_file_str(int dirfd, const char *path)
143 {
144         int fd = xopenat(dirfd, path, O_RDONLY);
145         ssize_t len = xfstat(fd).st_size;
146
147         char *buf = xmalloc(len + 1);
148
149         len = read(fd, buf, len);
150         if (len < 0)
151                 die("read error: %m");
152
153         buf[len] = '\0';
154         if (len && buf[len - 1] == '\n')
155                 buf[len - 1] = '\0';
156
157         close(fd);
158
159         return buf;
160 }
161
162 u64 read_file_u64(int dirfd, const char *path)
163 {
164         char *buf = read_file_str(dirfd, path);
165         u64 v;
166         if (kstrtou64(buf, 10, &v))
167                 die("read_file_u64: error parsing %s (got %s)", path, buf);
168         free(buf);
169         return v;
170 }
171
172 /* String list options: */
173
174 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
175                                 const char *msg)
176 {
177         ssize_t v = bch2_read_string_list(opt, list);
178         if (v < 0)
179                 die("Bad %s %s", msg, opt);
180
181         return v;
182 }
183
184 /* Returns size of file or block device: */
185 u64 get_size(const char *path, int fd)
186 {
187         struct stat statbuf = xfstat(fd);
188
189         if (!S_ISBLK(statbuf.st_mode))
190                 return statbuf.st_size;
191
192         u64 ret;
193         xioctl(fd, BLKGETSIZE64, &ret);
194         return ret;
195 }
196
197 /* Returns blocksize in units of 512 byte sectors: */
198 unsigned get_blocksize(const char *path, int fd)
199 {
200         struct stat statbuf = xfstat(fd);
201
202         if (!S_ISBLK(statbuf.st_mode))
203                 return statbuf.st_blksize >> 9;
204
205         unsigned ret;
206         xioctl(fd, BLKPBSZGET, &ret);
207         return ret >> 9;
208 }
209
210 /* Open a block device, do magic blkid stuff to probe for existing filesystems: */
211 int open_for_format(const char *dev, bool force)
212 {
213         blkid_probe pr;
214         const char *fs_type = NULL, *fs_label = NULL;
215         size_t fs_type_len, fs_label_len;
216
217         int fd = xopen(dev, O_RDWR|O_EXCL);
218
219         if (force)
220                 return fd;
221
222         if (!(pr = blkid_new_probe()))
223                 die("blkid error 1");
224         if (blkid_probe_set_device(pr, fd, 0, 0))
225                 die("blkid error 2");
226         if (blkid_probe_enable_partitions(pr, true))
227                 die("blkid error 3");
228         if (blkid_do_fullprobe(pr) < 0)
229                 die("blkid error 4");
230
231         blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len);
232         blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len);
233
234         if (fs_type) {
235                 if (fs_label)
236                         printf("%s contains a %s filesystem labelled '%s'\n",
237                                dev, fs_type, fs_label);
238                 else
239                         printf("%s contains a %s filesystem\n",
240                                dev, fs_type);
241                 fputs("Proceed anyway?", stdout);
242                 if (!ask_yn())
243                         exit(EXIT_FAILURE);
244         }
245
246         blkid_free_probe(pr);
247         return fd;
248 }
249
250 /* Global control device: */
251 int bcachectl_open(void)
252 {
253         return xopen("/dev/bcachefs-ctl", O_RDWR);
254 }
255
256 /* Filesystem handles (ioctl, sysfs dir): */
257
258 #define SYSFS_BASE "/sys/fs/bcachefs/"
259
260 struct bcache_handle bcache_fs_open(const char *path)
261 {
262         struct bcache_handle ret;
263         uuid_t tmp;
264
265         if (!uuid_parse(path, tmp)) {
266                 /* It's a UUID, look it up in sysfs: */
267                 char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
268                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
269
270                 char *minor = read_file_str(ret.sysfs_fd, "minor");
271                 char *ctl = mprintf("/dev/bcachefs%s-ctl", minor);
272                 ret.ioctl_fd = xopen(ctl, O_RDWR);
273
274                 free(sysfs);
275                 free(minor);
276                 free(ctl);
277         } else {
278                 /* It's a path: */
279                 ret.ioctl_fd = xopen(path, O_RDONLY);
280
281                 struct bch_ioctl_query_uuid uuid;
282                 xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid);
283
284                 char uuid_str[40];
285                 uuid_unparse(uuid.uuid.b, uuid_str);
286
287                 char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
288                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
289                 free(sysfs);
290         }
291
292         return ret;
293 }
294
295 bool ask_yn(void)
296 {
297         const char *short_yes = "yY";
298         char *buf = NULL;
299         size_t buflen = 0;
300         bool ret;
301
302         fputs(" (y,n) ", stdout);
303         fflush(stdout);
304
305         if (getline(&buf, &buflen, stdin) < 0)
306                 die("error reading from standard input");
307
308         ret = strchr(short_yes, buf[0]);
309         free(buf);
310         return ret;
311 }
312
313 static int range_cmp(const void *_l, const void *_r)
314 {
315         const struct range *l = _l, *r = _r;
316
317         if (l->start < r->start)
318                 return -1;
319         if (l->start > r->start)
320                 return  1;
321         return 0;
322 }
323
324 void ranges_sort_merge(ranges *r)
325 {
326         struct range *t, *i;
327         ranges tmp = { NULL };
328
329         sort(&darray_item(*r, 0), darray_size(*r),
330              sizeof(darray_item(*r, 0)), range_cmp, NULL);
331
332         /* Merge contiguous ranges: */
333         darray_foreach(i, *r) {
334                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
335
336                 if (t && t->end >= i->start)
337                         t->end = max(t->end, i->end);
338                 else
339                         darray_append(tmp, *i);
340         }
341
342         darray_free(*r);
343         *r = tmp;
344 }
345
346 void ranges_roundup(ranges *r, unsigned block_size)
347 {
348         struct range *i;
349
350         darray_foreach(i, *r) {
351                 i->start = round_down(i->start, block_size);
352                 i->end  = round_up(i->end, block_size);
353         }
354 }
355
356 void ranges_rounddown(ranges *r, unsigned block_size)
357 {
358         struct range *i;
359
360         darray_foreach(i, *r) {
361                 i->start = round_up(i->start, block_size);
362                 i->end  = round_down(i->end, block_size);
363                 i->end  = max(i->end, i->start);
364         }
365 }
366
367 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
368 {
369         struct fiemap_extent e;
370
371         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
372
373         if (iter->idx == iter->f.fm_mapped_extents) {
374                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
375
376                 if (!iter->f.fm_mapped_extents)
377                         return (struct fiemap_extent) { .fe_length = 0 };
378
379                 iter->idx = 0;
380         }
381
382         e = iter->f.fm_extents[iter->idx++];
383         BUG_ON(!e.fe_length);
384
385         iter->f.fm_start = e.fe_logical + e.fe_length;
386
387         return e;
388 }
389
390 const char *strcmp_prefix(const char *a, const char *a_prefix)
391 {
392         while (*a_prefix && *a == *a_prefix) {
393                 a++;
394                 a_prefix++;
395         }
396         return *a_prefix ? NULL : a;
397 }
398
399 unsigned hatoi_validate(const char *s, const char *msg)
400 {
401         u64 v;
402
403         if (bch2_strtoull_h(s, &v))
404                 die("bad %s %s", msg, s);
405
406         if (v & (v - 1))
407                 die("%s must be a power of two", msg);
408
409         v /= 512;
410
411         if (v > USHRT_MAX)
412                 die("%s too large\n", msg);
413
414         if (!v)
415                 die("%s too small\n", msg);
416
417         return v;
418 }