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