]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
Add upstream files
[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)
98 {
99         ssize_t r = pwrite(fd, buf, count, offset);
100
101         if (r != count)
102                 die("write error (ret %zi err %m)", 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 (kstrtou64(buf, 10, &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 units of 512 byte sectors: */
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 >> 9;
252
253         unsigned ret;
254         xioctl(fd, BLKPBSZGET, &ret);
255         return ret >> 9;
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 = xopen(dev, O_RDWR|O_EXCL);
266
267         if (force)
268                 return fd;
269
270         if (!(pr = blkid_new_probe()))
271                 die("blkid error 1");
272         if (blkid_probe_set_device(pr, fd, 0, 0))
273                 die("blkid error 2");
274         if (blkid_probe_enable_partitions(pr, true))
275                 die("blkid error 3");
276         if (blkid_do_fullprobe(pr) < 0)
277                 die("blkid error 4");
278
279         blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len);
280         blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len);
281
282         if (fs_type) {
283                 if (fs_label)
284                         printf("%s contains a %s filesystem labelled '%s'\n",
285                                dev, fs_type, fs_label);
286                 else
287                         printf("%s contains a %s filesystem\n",
288                                dev, fs_type);
289                 fputs("Proceed anyway?", stdout);
290                 if (!ask_yn())
291                         exit(EXIT_FAILURE);
292         }
293
294         blkid_free_probe(pr);
295         return fd;
296 }
297
298 bool ask_yn(void)
299 {
300         const char *short_yes = "yY";
301         char *buf = NULL;
302         size_t buflen = 0;
303         bool ret;
304
305         fputs(" (y,n) ", stdout);
306         fflush(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 }
315
316 static int range_cmp(const void *_l, const void *_r)
317 {
318         const struct range *l = _l, *r = _r;
319
320         if (l->start < r->start)
321                 return -1;
322         if (l->start > r->start)
323                 return  1;
324         return 0;
325 }
326
327 void ranges_sort_merge(ranges *r)
328 {
329         struct range *t, *i;
330         ranges tmp = { NULL };
331
332         sort(&darray_item(*r, 0), darray_size(*r),
333              sizeof(darray_item(*r, 0)), range_cmp, NULL);
334
335         /* Merge contiguous ranges: */
336         darray_foreach(i, *r) {
337                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
338
339                 if (t && t->end >= i->start)
340                         t->end = max(t->end, i->end);
341                 else
342                         darray_append(tmp, *i);
343         }
344
345         darray_free(*r);
346         *r = tmp;
347 }
348
349 void ranges_roundup(ranges *r, unsigned block_size)
350 {
351         struct range *i;
352
353         darray_foreach(i, *r) {
354                 i->start = round_down(i->start, block_size);
355                 i->end  = round_up(i->end, block_size);
356         }
357 }
358
359 void ranges_rounddown(ranges *r, unsigned block_size)
360 {
361         struct range *i;
362
363         darray_foreach(i, *r) {
364                 i->start = round_up(i->start, block_size);
365                 i->end  = round_down(i->end, block_size);
366                 i->end  = max(i->end, i->start);
367         }
368 }
369
370 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
371 {
372         struct fiemap_extent e;
373
374         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
375
376         if (iter->idx == iter->f.fm_mapped_extents) {
377                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
378
379                 if (!iter->f.fm_mapped_extents)
380                         return (struct fiemap_extent) { .fe_length = 0 };
381
382                 iter->idx = 0;
383         }
384
385         e = iter->f.fm_extents[iter->idx++];
386         BUG_ON(!e.fe_length);
387
388         iter->f.fm_start = e.fe_logical + e.fe_length;
389
390         return e;
391 }
392
393 char *strcmp_prefix(char *a, const char *a_prefix)
394 {
395         while (*a_prefix && *a == *a_prefix) {
396                 a++;
397                 a_prefix++;
398         }
399         return *a_prefix ? NULL : a;
400 }
401
402 unsigned hatoi_validate(const char *s, const char *msg)
403 {
404         u64 v;
405
406         if (bch2_strtoull_h(s, &v))
407                 die("bad %s %s", msg, s);
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 }
419
420 /* crc32c */
421
422 static u32 crc32c_default(u32 crc, const void *buf, size_t size)
423 {
424         static const u32 crc32c_tab[] = {
425                 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
426                 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
427                 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
428                 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
429                 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
430                 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
431                 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
432                 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
433                 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
434                 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
435                 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
436                 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
437                 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
438                 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
439                 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
440                 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
441                 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
442                 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
443                 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
444                 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
445                 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
446                 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
447                 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
448                 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
449                 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
450                 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
451                 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
452                 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
453                 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
454                 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
455                 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
456                 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
457                 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
458                 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
459                 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
460                 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
461                 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
462                 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
463                 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
464                 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
465                 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
466                 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
467                 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
468                 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
469                 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
470                 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
471                 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
472                 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
473                 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
474                 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
475                 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
476                 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
477                 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
478                 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
479                 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
480                 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
481                 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
482                 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
483                 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
484                 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
485                 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
486                 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
487                 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
488                 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
489         };
490         const u8 *p = buf;
491
492         while (size--)
493                 crc = crc32c_tab[(crc ^ *p++) & 0xFFL] ^ (crc >> 8);
494
495         return crc;
496 }
497
498 #include <linux/compiler.h>
499
500 #ifdef __x86_64__
501
502 #ifdef CONFIG_X86_64
503 #define REX_PRE "0x48, "
504 #else
505 #define REX_PRE
506 #endif
507
508 static u32 crc32c_sse42(u32 crc, const void *buf, size_t size)
509 {
510         while (size >= sizeof(long)) {
511                 const unsigned long *d = buf;
512
513                 __asm__ __volatile__(
514                         ".byte 0xf2, " REX_PRE "0xf, 0x38, 0xf1, 0xf1;"
515                         :"=S"(crc)
516                         :"0"(crc), "c"(*d)
517                 );
518                 buf     += sizeof(long);
519                 size    -= sizeof(long);
520         }
521
522         while (size) {
523                 const u8 *d = buf;
524
525                 __asm__ __volatile__(
526                         ".byte 0xf2, 0xf, 0x38, 0xf0, 0xf1"
527                         :"=S"(crc)
528                         :"0"(crc), "c"(*d)
529                 );
530                 buf     += 1;
531                 size    -= 1;
532         }
533
534         return crc;
535 }
536
537 #endif
538
539 static void *resolve_crc32c(void)
540 {
541 #ifdef __x86_64__
542         if (__builtin_cpu_supports("sse4.2"))
543                 return crc32c_sse42;
544 #endif
545         return crc32c_default;
546 }
547
548 /*
549  * ifunc is buggy and I don't know what breaks it (LTO?)
550  */
551 #ifdef HAVE_WORKING_IFUNC
552
553 static void *ifunc_resolve_crc32c(void)
554 {
555         __builtin_cpu_init();
556
557         return resolve_crc32c
558 }
559
560 u32 crc32c(u32, const void *, size_t)
561         __attribute__((ifunc("ifunc_resolve_crc32c")));
562
563 #else
564
565 u32 crc32c(u32 crc, const void *buf, size_t size)
566 {
567         static u32 (*real_crc32c)(u32, const void *, size_t);
568
569         if (unlikely(!real_crc32c))
570                 real_crc32c = resolve_crc32c();
571
572         return real_crc32c(crc, buf, size);
573 }
574
575 #endif /* HAVE_WORKING_IFUNC */
576
577 char *dev_to_name(dev_t dev)
578 {
579         char *line = NULL, *name = NULL;
580         size_t n = 0;
581
582         FILE *f = fopen("/proc/partitions", "r");
583         if (!f)
584                 die("error opening /proc/partitions: %m");
585
586         while (getline(&line, &n, f) != -1) {
587                 unsigned ma, mi;
588                 u64 sectors;
589
590                 name = realloc(name, n + 1);
591
592                 if (sscanf(line, " %u %u %llu %s", &ma, &mi, &sectors, name) == 4 &&
593                     ma == major(dev) && mi == minor(dev))
594                         goto found;
595         }
596
597         free(name);
598         name = NULL;
599 found:
600         fclose(f);
601         free(line);
602         return name;
603 }
604
605 char *dev_to_path(dev_t dev)
606 {
607         char *name = dev_to_name(dev);
608         if (!name)
609                 return NULL;
610
611         char *path = mprintf("/dev/%s", name);
612
613         free(name);
614         return path;
615 }
616
617 struct mntent *dev_to_mount(char *dev)
618 {
619         struct mntent *mnt, *ret = NULL;
620         FILE *f = setmntent("/proc/mounts", "r");
621         if (!f)
622                 die("error opening /proc/mounts: %m");
623
624         struct stat d1 = xstat(dev);
625
626         while ((mnt = getmntent(f))) {
627                 char *d, *p = mnt->mnt_fsname;
628
629                 while ((d = strsep(&p, ":"))) {
630                         struct stat d2;
631
632                         if (stat(d, &d2))
633                                 continue;
634
635                         if (S_ISBLK(d1.st_mode) != S_ISBLK(d2.st_mode))
636                                 continue;
637
638                         if (S_ISBLK(d1.st_mode)) {
639                                 if (d1.st_rdev != d2.st_rdev)
640                                         continue;
641                         } else {
642                                 if (d1.st_dev != d2.st_dev ||
643                                     d1.st_ino != d2.st_ino)
644                                         continue;
645                         }
646
647                         ret = mnt;
648                         goto found;
649                 }
650         }
651 found:
652         fclose(f);
653         return ret;
654 }
655
656 int dev_mounted(char *dev)
657 {
658         struct mntent *mnt = dev_to_mount(dev);
659
660         if (!mnt)
661                 return 0;
662         if (hasmntopt(mnt, "ro"))
663                 return 1;
664         return 2;
665 }