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