]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
Unit handling cleanups
[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/sysmacros.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include <blkid.h>
18 #include <uuid/uuid.h>
19
20 #include "libbcachefs/bcachefs_ioctl.h"
21 #include "linux/sort.h"
22 #include "tools-util.h"
23 #include "libbcachefs/util.h"
24
25 void die(const char *fmt, ...)
26 {
27         va_list args;
28
29         va_start(args, fmt);
30         vfprintf(stderr, fmt, args);
31         va_end(args);
32         fputc('\n', stderr);
33
34         _exit(EXIT_FAILURE);
35 }
36
37 char *mprintf(const char *fmt, ...)
38 {
39         va_list args;
40         char *str;
41         int ret;
42
43         va_start(args, fmt);
44         ret = vasprintf(&str, fmt, args);
45         va_end(args);
46
47         if (ret < 0)
48                 die("insufficient memory");
49
50         return str;
51 }
52
53 void *xcalloc(size_t count, size_t size)
54 {
55         void *p = calloc(count, size);
56
57         if (!p)
58                 die("insufficient memory");
59
60         return p;
61 }
62
63 void *xmalloc(size_t size)
64 {
65         void *p = malloc(size);
66
67         if (!p)
68                 die("insufficient memory");
69
70         memset(p, 0, size);
71         return p;
72 }
73
74 void *xrealloc(void *p, size_t size)
75 {
76         p = realloc(p, size);
77         if (!p)
78                 die("insufficient memory");
79
80         return p;
81 }
82
83 void xpread(int fd, void *buf, size_t count, off_t offset)
84 {
85         while (count) {
86                 ssize_t r = pread(fd, buf, count, offset);
87
88                 if (r < 0)
89                         die("read error: %m");
90                 if (!r)
91                         die("pread error: unexpected eof");
92                 count   -= r;
93                 offset  += r;
94         }
95 }
96
97 void xpwrite(int fd, const void *buf, size_t count, off_t offset, const char *msg)
98 {
99         ssize_t r = pwrite(fd, buf, count, offset);
100
101         if (r != count)
102                 die("error writing %s (ret %zi err %m)", msg, r);
103 }
104
105 struct stat xfstatat(int dirfd, const char *path, int flags)
106 {
107         struct stat stat;
108         if (fstatat(dirfd, path, &stat, flags))
109                 die("stat error: %m");
110         return stat;
111 }
112
113 struct stat xfstat(int fd)
114 {
115         struct stat stat;
116         if (fstat(fd, &stat))
117                 die("stat error: %m");
118         return stat;
119 }
120
121 struct stat xstat(const char *path)
122 {
123         struct stat statbuf;
124         if (stat(path, &statbuf))
125                 die("stat error statting %s: %m", path);
126         return statbuf;
127 }
128
129 /* Formatting: */
130
131 int printf_pad(unsigned pad, const char * fmt, ...)
132 {
133        va_list args;
134        int ret;
135
136        va_start(args, fmt);
137        ret = vprintf(fmt, args);
138        va_end(args);
139
140        while (ret++ < pad)
141                putchar(' ');
142
143        return ret;
144 }
145
146 struct units_buf __pr_units(s64 _v, enum units units)
147 {
148         struct units_buf ret;
149         char *out = ret.b, *end = out + sizeof(ret.b);
150         u64 v = _v;
151
152         if (_v < 0) {
153                 out += scnprintf(out, end - out, "-");
154                 v = -_v;
155         }
156
157         switch (units) {
158         case BYTES:
159                 snprintf(out, end - out, "%llu", v << 9);
160                 break;
161         case SECTORS:
162                 snprintf(out, end - out, "%llu", v);
163                 break;
164         case HUMAN_READABLE:
165                 v <<= 9;
166
167                 if (v >= 1024) {
168                         int exp = log(v) / log(1024);
169                         snprintf(out, end - out, "%.1f%c",
170                                  v / pow(1024, exp),
171                                  "KMGTPE"[exp-1]);
172                 } else {
173                         snprintf(out, end - out, "%llu", v);
174                 }
175
176                 break;
177         }
178
179         return ret;
180 }
181
182 /* Argument parsing stuff: */
183
184 /* File parsing (i.e. sysfs) */
185
186 char *read_file_str(int dirfd, const char *path)
187 {
188         int fd = xopenat(dirfd, path, O_RDONLY);
189         ssize_t len = xfstat(fd).st_size;
190
191         char *buf = xmalloc(len + 1);
192
193         len = read(fd, buf, len);
194         if (len < 0)
195                 die("read error: %m");
196
197         buf[len] = '\0';
198         if (len && buf[len - 1] == '\n')
199                 buf[len - 1] = '\0';
200         if (!strlen(buf)) {
201                 free(buf);
202                 buf = NULL;
203         }
204
205         close(fd);
206
207         return buf;
208 }
209
210 u64 read_file_u64(int dirfd, const char *path)
211 {
212         char *buf = read_file_str(dirfd, path);
213         u64 v;
214         if (bch2_strtou64_h(buf, &v))
215                 die("read_file_u64: error parsing %s (got %s)", path, buf);
216         free(buf);
217         return v;
218 }
219
220 /* String list options: */
221
222 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
223                                 const char *msg)
224 {
225         ssize_t v = match_string(list, -1, opt);
226         if (v < 0)
227                 die("Bad %s %s", msg, opt);
228
229         return v;
230 }
231
232 /* Returns size of file or block device: */
233 u64 get_size(const char *path, int fd)
234 {
235         struct stat statbuf = xfstat(fd);
236
237         if (!S_ISBLK(statbuf.st_mode))
238                 return statbuf.st_size;
239
240         u64 ret;
241         xioctl(fd, BLKGETSIZE64, &ret);
242         return ret;
243 }
244
245 /* Returns blocksize, in bytes: */
246 unsigned get_blocksize(const char *path, int fd)
247 {
248         struct stat statbuf = xfstat(fd);
249
250         if (!S_ISBLK(statbuf.st_mode))
251                 return statbuf.st_blksize;
252
253         unsigned ret;
254         xioctl(fd, BLKPBSZGET, &ret);
255         return ret;
256 }
257
258 /* Open a block device, do magic blkid stuff to probe for existing filesystems: */
259 int open_for_format(const char *dev, bool force)
260 {
261         blkid_probe pr;
262         const char *fs_type = NULL, *fs_label = NULL;
263         size_t fs_type_len, fs_label_len;
264
265         int fd = open(dev, O_RDWR|O_EXCL);
266         if (fd < 0)
267                 die("Error opening device to format %s: %m", dev);
268
269         if (force)
270                 return fd;
271
272         if (!(pr = blkid_new_probe()))
273                 die("blkid error 1");
274         if (blkid_probe_set_device(pr, fd, 0, 0))
275                 die("blkid error 2");
276         if (blkid_probe_enable_partitions(pr, true))
277                 die("blkid error 3");
278         if (blkid_do_fullprobe(pr) < 0)
279                 die("blkid error 4");
280
281         blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len);
282         blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len);
283
284         if (fs_type) {
285                 if (fs_label)
286                         printf("%s contains a %s filesystem labelled '%s'\n",
287                                dev, fs_type, fs_label);
288                 else
289                         printf("%s contains a %s filesystem\n",
290                                dev, fs_type);
291                 fputs("Proceed anyway?", stdout);
292                 if (!ask_yn())
293                         exit(EXIT_FAILURE);
294                 while (blkid_do_probe(pr) == 0)
295                         blkid_do_wipe(pr, 0);
296         }
297
298         blkid_free_probe(pr);
299         return fd;
300 }
301
302 bool ask_yn(void)
303 {
304         const char *short_yes = "yY";
305         char *buf = NULL;
306         size_t buflen = 0;
307         bool ret;
308
309         fputs(" (y,n) ", stdout);
310         fflush(stdout);
311
312         if (getline(&buf, &buflen, stdin) < 0)
313                 die("error reading from standard input");
314
315         ret = strchr(short_yes, buf[0]);
316         free(buf);
317         return ret;
318 }
319
320 static int range_cmp(const void *_l, const void *_r)
321 {
322         const struct range *l = _l, *r = _r;
323
324         if (l->start < r->start)
325                 return -1;
326         if (l->start > r->start)
327                 return  1;
328         return 0;
329 }
330
331 void ranges_sort_merge(ranges *r)
332 {
333         struct range *t, *i;
334         ranges tmp = { NULL };
335
336         sort(&darray_item(*r, 0), darray_size(*r),
337              sizeof(darray_item(*r, 0)), range_cmp, NULL);
338
339         /* Merge contiguous ranges: */
340         darray_foreach(i, *r) {
341                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
342
343                 if (t && t->end >= i->start)
344                         t->end = max(t->end, i->end);
345                 else
346                         darray_append(tmp, *i);
347         }
348
349         darray_free(*r);
350         *r = tmp;
351 }
352
353 void ranges_roundup(ranges *r, unsigned block_size)
354 {
355         struct range *i;
356
357         darray_foreach(i, *r) {
358                 i->start = round_down(i->start, block_size);
359                 i->end  = round_up(i->end, block_size);
360         }
361 }
362
363 void ranges_rounddown(ranges *r, unsigned block_size)
364 {
365         struct range *i;
366
367         darray_foreach(i, *r) {
368                 i->start = round_up(i->start, block_size);
369                 i->end  = round_down(i->end, block_size);
370                 i->end  = max(i->end, i->start);
371         }
372 }
373
374 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
375 {
376         struct fiemap_extent e;
377
378         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
379
380         if (iter->idx == iter->f.fm_mapped_extents) {
381                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
382
383                 if (!iter->f.fm_mapped_extents)
384                         return (struct fiemap_extent) { .fe_length = 0 };
385
386                 iter->idx = 0;
387         }
388
389         e = iter->f.fm_extents[iter->idx++];
390         BUG_ON(!e.fe_length);
391
392         iter->f.fm_start = e.fe_logical + e.fe_length;
393
394         return e;
395 }
396
397 char *strcmp_prefix(char *a, const char *a_prefix)
398 {
399         while (*a_prefix && *a == *a_prefix) {
400                 a++;
401                 a_prefix++;
402         }
403         return *a_prefix ? NULL : a;
404 }
405
406 /* crc32c */
407
408 static u32 crc32c_default(u32 crc, const void *buf, size_t size)
409 {
410         static const u32 crc32c_tab[] = {
411                 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
412                 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
413                 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
414                 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
415                 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
416                 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
417                 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
418                 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
419                 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
420                 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
421                 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
422                 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
423                 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
424                 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
425                 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
426                 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
427                 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
428                 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
429                 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
430                 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
431                 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
432                 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
433                 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
434                 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
435                 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
436                 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
437                 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
438                 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
439                 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
440                 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
441                 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
442                 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
443                 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
444                 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
445                 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
446                 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
447                 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
448                 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
449                 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
450                 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
451                 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
452                 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
453                 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
454                 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
455                 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
456                 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
457                 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
458                 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
459                 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
460                 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
461                 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
462                 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
463                 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
464                 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
465                 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
466                 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
467                 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
468                 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
469                 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
470                 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
471                 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
472                 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
473                 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
474                 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
475         };
476         const u8 *p = buf;
477
478         while (size--)
479                 crc = crc32c_tab[(crc ^ *p++) & 0xFFL] ^ (crc >> 8);
480
481         return crc;
482 }
483
484 #include <linux/compiler.h>
485
486 #ifdef __x86_64__
487
488 #ifdef CONFIG_X86_64
489 #define REX_PRE "0x48, "
490 #else
491 #define REX_PRE
492 #endif
493
494 static u32 crc32c_sse42(u32 crc, const void *buf, size_t size)
495 {
496         while (size >= sizeof(long)) {
497                 const unsigned long *d = buf;
498
499                 __asm__ __volatile__(
500                         ".byte 0xf2, " REX_PRE "0xf, 0x38, 0xf1, 0xf1;"
501                         :"=S"(crc)
502                         :"0"(crc), "c"(*d)
503                 );
504                 buf     += sizeof(long);
505                 size    -= sizeof(long);
506         }
507
508         while (size) {
509                 const u8 *d = buf;
510
511                 __asm__ __volatile__(
512                         ".byte 0xf2, 0xf, 0x38, 0xf0, 0xf1"
513                         :"=S"(crc)
514                         :"0"(crc), "c"(*d)
515                 );
516                 buf     += 1;
517                 size    -= 1;
518         }
519
520         return crc;
521 }
522
523 #endif
524
525 static void *resolve_crc32c(void)
526 {
527 #ifdef __x86_64__
528         if (__builtin_cpu_supports("sse4.2"))
529                 return crc32c_sse42;
530 #endif
531         return crc32c_default;
532 }
533
534 /*
535  * ifunc is buggy and I don't know what breaks it (LTO?)
536  */
537 #ifdef HAVE_WORKING_IFUNC
538
539 static void *ifunc_resolve_crc32c(void)
540 {
541         __builtin_cpu_init();
542
543         return resolve_crc32c
544 }
545
546 u32 crc32c(u32, const void *, size_t)
547         __attribute__((ifunc("ifunc_resolve_crc32c")));
548
549 #else
550
551 u32 crc32c(u32 crc, const void *buf, size_t size)
552 {
553         static u32 (*real_crc32c)(u32, const void *, size_t);
554
555         if (unlikely(!real_crc32c))
556                 real_crc32c = resolve_crc32c();
557
558         return real_crc32c(crc, buf, size);
559 }
560
561 #endif /* HAVE_WORKING_IFUNC */
562
563 char *dev_to_name(dev_t dev)
564 {
565         char *line = NULL, *name = NULL;
566         size_t n = 0;
567
568         FILE *f = fopen("/proc/partitions", "r");
569         if (!f)
570                 die("error opening /proc/partitions: %m");
571
572         while (getline(&line, &n, f) != -1) {
573                 unsigned ma, mi;
574                 u64 sectors;
575
576                 name = realloc(name, n + 1);
577
578                 if (sscanf(line, " %u %u %llu %s", &ma, &mi, &sectors, name) == 4 &&
579                     ma == major(dev) && mi == minor(dev))
580                         goto found;
581         }
582
583         free(name);
584         name = NULL;
585 found:
586         fclose(f);
587         free(line);
588         return name;
589 }
590
591 char *dev_to_path(dev_t dev)
592 {
593         char *name = dev_to_name(dev);
594         if (!name)
595                 return NULL;
596
597         char *path = mprintf("/dev/%s", name);
598
599         free(name);
600         return path;
601 }
602
603 struct mntent *dev_to_mount(char *dev)
604 {
605         struct mntent *mnt, *ret = NULL;
606         FILE *f = setmntent("/proc/mounts", "r");
607         if (!f)
608                 die("error opening /proc/mounts: %m");
609
610         struct stat d1 = xstat(dev);
611
612         while ((mnt = getmntent(f))) {
613                 char *d, *p = mnt->mnt_fsname;
614
615                 while ((d = strsep(&p, ":"))) {
616                         struct stat d2;
617
618                         if (stat(d, &d2))
619                                 continue;
620
621                         if (S_ISBLK(d1.st_mode) != S_ISBLK(d2.st_mode))
622                                 continue;
623
624                         if (S_ISBLK(d1.st_mode)) {
625                                 if (d1.st_rdev != d2.st_rdev)
626                                         continue;
627                         } else {
628                                 if (d1.st_dev != d2.st_dev ||
629                                     d1.st_ino != d2.st_ino)
630                                         continue;
631                         }
632
633                         ret = mnt;
634                         goto found;
635                 }
636         }
637 found:
638         fclose(f);
639         return ret;
640 }
641
642 int dev_mounted(char *dev)
643 {
644         struct mntent *mnt = dev_to_mount(dev);
645
646         if (!mnt)
647                 return 0;
648         if (hasmntopt(mnt, "ro"))
649                 return 1;
650         return 2;
651 }
652
653 struct bpos bpos_parse(char *buf)
654 {
655         char *s = buf, *field;
656         u64 inode_v = 0, offset_v = 0;
657
658         if (!(field = strsep(&s, ":")) ||
659             kstrtoull(field, 10, &inode_v))
660                 die("invalid bpos %s", buf);
661
662         if ((field = strsep(&s, ":")) &&
663             kstrtoull(field, 10, &offset_v))
664                 die("invalid bpos %s", buf);
665
666         if (s)
667                 die("invalid bpos %s", buf);
668
669         return (struct bpos) { .inode = inode_v, .offset = offset_v };
670 }