]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs.c
1481ef382583ab0e899e1f726234742ff4a57e8c
[bcachefs-tools-debian] / libbcachefs.c
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdbool.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <time.h>
12 #include <unistd.h>
13
14 #include <uuid/uuid.h>
15
16 #include "libbcachefs/bcachefs_format.h"
17 #include "libbcachefs/checksum.h"
18 #include "crypto.h"
19 #include "libbcachefs.h"
20 #include "libbcachefs/opts.h"
21 #include "libbcachefs/super-io.h"
22
23 #define NSEC_PER_SEC    1000000000L
24
25 #define BCH_MIN_NR_NBUCKETS     (1 << 10)
26
27 /* minimum size filesystem we can create, given a bucket size: */
28 static u64 min_size(unsigned bucket_size)
29 {
30         return BCH_MIN_NR_NBUCKETS * bucket_size;
31 }
32
33 static void init_layout(struct bch_sb_layout *l, unsigned block_size,
34                         u64 start, u64 end)
35 {
36         unsigned sb_size;
37         u64 backup; /* offset of 2nd sb */
38
39         memset(l, 0, sizeof(*l));
40
41         if (start != BCH_SB_SECTOR)
42                 start = round_up(start, block_size);
43         end = round_down(end, block_size);
44
45         if (start >= end)
46                 die("insufficient space for superblocks");
47
48         /*
49          * Create two superblocks in the allowed range: reserve a maximum of 64k
50          */
51         sb_size = min_t(u64, 128, end - start / 2);
52
53         backup = start + sb_size;
54         backup = round_up(backup, block_size);
55
56         backup = min(backup, end);
57
58         sb_size = min(end - backup, backup- start);
59         sb_size = rounddown_pow_of_two(sb_size);
60
61         if (sb_size < 8)
62                 die("insufficient space for superblocks");
63
64         l->magic                = BCACHE_MAGIC;
65         l->layout_type          = 0;
66         l->nr_superblocks       = 2;
67         l->sb_max_size_bits     = ilog2(sb_size);
68         l->sb_offset[0]         = cpu_to_le64(start);
69         l->sb_offset[1]         = cpu_to_le64(backup);
70 }
71
72 void bch2_pick_bucket_size(struct format_opts opts, struct dev_opts *dev)
73 {
74         if (!dev->sb_offset) {
75                 dev->sb_offset  = BCH_SB_SECTOR;
76                 dev->sb_end     = BCH_SB_SECTOR + 256;
77         }
78
79         if (!dev->size)
80                 dev->size = get_size(dev->path, dev->fd) >> 9;
81
82         if (!dev->bucket_size) {
83                 if (dev->size < min_size(opts.block_size))
84                         die("cannot format %s, too small (%llu sectors, min %llu)",
85                             dev->path, dev->size, min_size(opts.block_size));
86
87                 /* Bucket size must be >= block size: */
88                 dev->bucket_size = opts.block_size;
89
90                 /* Bucket size must be >= btree node size: */
91                 dev->bucket_size = max(dev->bucket_size, opts.btree_node_size);
92
93                 /* Want a bucket size of at least 128k, if possible: */
94                 dev->bucket_size = max(dev->bucket_size, 256U);
95
96                 if (dev->size >= min_size(dev->bucket_size)) {
97                         unsigned scale = max(1,
98                                              ilog2(dev->size / min_size(dev->bucket_size)) / 4);
99
100                         scale = rounddown_pow_of_two(scale);
101
102                         /* max bucket size 1 mb */
103                         dev->bucket_size = min(dev->bucket_size * scale, 1U << 11);
104                 } else {
105                         do {
106                                 dev->bucket_size /= 2;
107                         } while (dev->size < min_size(dev->bucket_size));
108                 }
109         }
110
111         dev->nbuckets   = dev->size / dev->bucket_size;
112
113         if (dev->bucket_size < opts.block_size)
114                 die("Bucket size cannot be smaller than block size");
115
116         if (dev->bucket_size < opts.btree_node_size)
117                 die("Bucket size cannot be smaller than btree node size");
118
119         if (dev->nbuckets < BCH_MIN_NR_NBUCKETS)
120                 die("Not enough buckets: %llu, need %u (bucket size %u)",
121                     dev->nbuckets, BCH_MIN_NR_NBUCKETS, dev->bucket_size);
122
123 }
124
125 struct bch_sb *bch2_format(struct format_opts opts,
126                            struct dev_opts *devs, size_t nr_devs)
127 {
128         struct bch_sb *sb;
129         struct dev_opts *i;
130         struct bch_sb_field_members *mi;
131         unsigned u64s;
132
133         /* calculate block size: */
134         if (!opts.block_size)
135                 for (i = devs; i < devs + nr_devs; i++)
136                         opts.block_size = max(opts.block_size,
137                                               get_blocksize(i->path, i->fd));
138
139         /* calculate bucket sizes: */
140         for (i = devs; i < devs + nr_devs; i++)
141                 bch2_pick_bucket_size(opts, i);
142
143         /* calculate btree node size: */
144         if (!opts.btree_node_size) {
145                 /* 256k default btree node size */
146                 opts.btree_node_size = 512;
147
148                 for (i = devs; i < devs + nr_devs; i++)
149                         opts.btree_node_size =
150                                 min(opts.btree_node_size, i->bucket_size);
151         }
152
153         if (!is_power_of_2(opts.block_size))
154                 die("block size must be power of 2");
155
156         if (!is_power_of_2(opts.btree_node_size))
157                 die("btree node size must be power of 2");
158
159         if (uuid_is_null(opts.uuid.b))
160                 uuid_generate(opts.uuid.b);
161
162         sb = calloc(1, sizeof(*sb) +
163                     sizeof(struct bch_sb_field_members) +
164                     sizeof(struct bch_member) * nr_devs +
165                     sizeof(struct bch_sb_field_crypt));
166
167         sb->version     = cpu_to_le64(BCH_SB_VERSION_MAX);
168         sb->magic       = BCACHE_MAGIC;
169         sb->block_size  = cpu_to_le16(opts.block_size);
170         sb->user_uuid   = opts.uuid;
171         sb->nr_devices  = nr_devs;
172
173         uuid_generate(sb->uuid.b);
174
175         if (opts.label)
176                 strncpy((char *) sb->label, opts.label, sizeof(sb->label));
177
178         SET_BCH_SB_CSUM_TYPE(sb,                opts.meta_csum_type);
179         SET_BCH_SB_META_CSUM_TYPE(sb,           opts.meta_csum_type);
180         SET_BCH_SB_DATA_CSUM_TYPE(sb,           opts.data_csum_type);
181         SET_BCH_SB_COMPRESSION_TYPE(sb,         opts.compression_type);
182
183         SET_BCH_SB_BTREE_NODE_SIZE(sb,          opts.btree_node_size);
184         SET_BCH_SB_GC_RESERVE(sb,               8);
185         SET_BCH_SB_META_REPLICAS_WANT(sb,       opts.meta_replicas);
186         SET_BCH_SB_META_REPLICAS_REQ(sb,        opts.meta_replicas_required);
187         SET_BCH_SB_DATA_REPLICAS_WANT(sb,       opts.data_replicas);
188         SET_BCH_SB_DATA_REPLICAS_REQ(sb,        opts.data_replicas_required);
189         SET_BCH_SB_ERROR_ACTION(sb,             opts.on_error_action);
190         SET_BCH_SB_STR_HASH_TYPE(sb,            BCH_STR_HASH_SIPHASH);
191         SET_BCH_SB_ENCODED_EXTENT_MAX_BITS(sb,  ilog2(opts.encoded_extent_max));
192
193         SET_BCH_SB_POSIX_ACL(sb,                1);
194
195         struct timespec now;
196         if (clock_gettime(CLOCK_REALTIME, &now))
197                 die("error getting current time: %m");
198
199         sb->time_base_lo        = cpu_to_le64(now.tv_sec * NSEC_PER_SEC + now.tv_nsec);
200         sb->time_precision      = cpu_to_le32(1);
201
202         if (opts.encrypted) {
203                 struct bch_sb_field_crypt *crypt = vstruct_end(sb);
204
205                 u64s = sizeof(struct bch_sb_field_crypt) / sizeof(u64);
206
207                 le32_add_cpu(&sb->u64s, u64s);
208                 crypt->field.u64s = cpu_to_le32(u64s);
209                 crypt->field.type = BCH_SB_FIELD_crypt;
210
211                 bch_sb_crypt_init(sb, crypt, opts.passphrase);
212                 SET_BCH_SB_ENCRYPTION_TYPE(sb, 1);
213         }
214
215         mi = vstruct_end(sb);
216         u64s = (sizeof(struct bch_sb_field_members) +
217                 sizeof(struct bch_member) * nr_devs) / sizeof(u64);
218
219         le32_add_cpu(&sb->u64s, u64s);
220         mi->field.u64s = cpu_to_le32(u64s);
221         mi->field.type = BCH_SB_FIELD_members;
222
223         for (i = devs; i < devs + nr_devs; i++) {
224                 struct bch_member *m = mi->members + (i - devs);
225
226                 uuid_generate(m->uuid.b);
227                 m->nbuckets     = cpu_to_le64(i->nbuckets);
228                 m->first_bucket = 0;
229                 m->bucket_size  = cpu_to_le16(i->bucket_size);
230
231                 SET_BCH_MEMBER_TIER(m,          i->tier);
232                 SET_BCH_MEMBER_REPLACEMENT(m,   CACHE_REPLACEMENT_LRU);
233                 SET_BCH_MEMBER_DISCARD(m,       i->discard);
234                 SET_BCH_MEMBER_DATA_ALLOWED(m,  i->data_allowed);
235         }
236
237         for (i = devs; i < devs + nr_devs; i++) {
238                 sb->dev_idx = i - devs;
239
240                 init_layout(&sb->layout, opts.block_size,
241                             i->sb_offset, i->sb_end);
242
243                 if (i->sb_offset == BCH_SB_SECTOR) {
244                         /* Zero start of disk */
245                         static const char zeroes[BCH_SB_SECTOR << 9];
246
247                         xpwrite(i->fd, zeroes, BCH_SB_SECTOR << 9, 0);
248                 }
249
250                 bch2_super_write(i->fd, sb);
251                 close(i->fd);
252         }
253
254         return sb;
255 }
256
257 void bch2_super_write(int fd, struct bch_sb *sb)
258 {
259         struct nonce nonce = { 0 };
260
261         unsigned i;
262         for (i = 0; i < sb->layout.nr_superblocks; i++) {
263                 sb->offset = sb->layout.sb_offset[i];
264
265                 if (sb->offset == BCH_SB_SECTOR) {
266                         /* Write backup layout */
267                         xpwrite(fd, &sb->layout, sizeof(sb->layout),
268                                 BCH_SB_LAYOUT_SECTOR << 9);
269                 }
270
271                 sb->csum = csum_vstruct(NULL, BCH_SB_CSUM_TYPE(sb), nonce, sb);
272                 xpwrite(fd, sb, vstruct_bytes(sb),
273                         le64_to_cpu(sb->offset) << 9);
274         }
275
276         fsync(fd);
277 }
278
279 struct bch_sb *__bch2_super_read(int fd, u64 sector)
280 {
281         struct bch_sb sb, *ret;
282
283         xpread(fd, &sb, sizeof(sb), sector << 9);
284
285         if (memcmp(&sb.magic, &BCACHE_MAGIC, sizeof(sb.magic)))
286                 die("not a bcachefs superblock");
287
288         size_t bytes = vstruct_bytes(&sb);
289
290         ret = malloc(bytes);
291
292         xpread(fd, ret, bytes, sector << 9);
293
294         return ret;
295 }
296
297 static unsigned get_dev_has_data(struct bch_sb *sb, unsigned dev)
298 {
299         struct bch_sb_field_replicas *replicas;
300         struct bch_replicas_entry *r;
301         unsigned i, data_has = 0;
302
303         replicas = bch2_sb_get_replicas(sb);
304
305         if (replicas)
306                 for_each_replicas_entry(replicas, r)
307                         for (i = 0; i < r->nr; i++)
308                                 if (r->devs[i] == dev)
309                                         data_has |= 1 << r->data_type;
310
311         return data_has;
312 }
313
314 /* superblock printing: */
315
316 static void bch2_sb_print_layout(struct bch_sb *sb, enum units units)
317 {
318         struct bch_sb_layout *l = &sb->layout;
319         unsigned i;
320
321         printf("  type:                         %u\n"
322                "  superblock max size:          %s\n"
323                "  nr superblocks:               %u\n"
324                "  Offsets:                      ",
325                l->layout_type,
326                pr_units(1 << l->sb_max_size_bits, units),
327                l->nr_superblocks);
328
329         for (i = 0; i < l->nr_superblocks; i++) {
330                 if (i)
331                         printf(", ");
332                 printf("%llu", le64_to_cpu(l->sb_offset[i]));
333         }
334         putchar('\n');
335 }
336
337 static void bch2_sb_print_journal(struct bch_sb *sb, struct bch_sb_field *f,
338                                   enum units units)
339 {
340         struct bch_sb_field_journal *journal = field_to_type(f, journal);
341         unsigned i, nr = bch2_nr_journal_buckets(journal);
342
343         printf("  Buckets:                      ");
344         for (i = 0; i < nr; i++) {
345                 if (i)
346                         putchar(' ');
347                 printf("%llu", le64_to_cpu(journal->buckets[i]));
348         }
349         putchar('\n');
350 }
351
352 static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
353                                   enum units units)
354 {
355         struct bch_sb_field_members *mi = field_to_type(f, members);
356         unsigned i;
357
358         for (i = 0; i < sb->nr_devices; i++) {
359                 struct bch_member *m = mi->members + i;
360                 time_t last_mount = le64_to_cpu(m->last_mount);
361                 char member_uuid_str[40];
362                 char data_allowed_str[100];
363                 char data_has_str[100];
364
365                 if (!bch2_member_exists(m))
366                         continue;
367
368                 uuid_unparse(m->uuid.b, member_uuid_str);
369                 bch2_scnprint_flag_list(data_allowed_str,
370                                         sizeof(data_allowed_str),
371                                         bch2_data_types,
372                                         BCH_MEMBER_DATA_ALLOWED(m));
373                 if (!data_allowed_str[0])
374                         strcpy(data_allowed_str, "(none)");
375
376                 bch2_scnprint_flag_list(data_has_str,
377                                         sizeof(data_has_str),
378                                         bch2_data_types,
379                                         get_dev_has_data(sb, i));
380                 if (!data_has_str[0])
381                         strcpy(data_has_str, "(none)");
382
383                 printf("  Device %u:\n"
384                        "    UUID:                       %s\n"
385                        "    Size:                       %s\n"
386                        "    Bucket size:                %s\n"
387                        "    First bucket:               %u\n"
388                        "    Buckets:                    %llu\n"
389                        "    Last mount:                 %s\n"
390                        "    State:                      %s\n"
391                        "    Tier:                       %llu\n"
392                        "    Data allowed:               %s\n"
393
394                        "    Has data:                   %s\n"
395
396                        "    Replacement policy:         %s\n"
397                        "    Discard:                    %llu\n",
398                        i, member_uuid_str,
399                        pr_units(le16_to_cpu(m->bucket_size) *
400                                 le64_to_cpu(m->nbuckets), units),
401                        pr_units(le16_to_cpu(m->bucket_size), units),
402                        le16_to_cpu(m->first_bucket),
403                        le64_to_cpu(m->nbuckets),
404                        last_mount ? ctime(&last_mount) : "(never)",
405
406                        BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR
407                        ? bch2_dev_state[BCH_MEMBER_STATE(m)]
408                        : "unknown",
409
410                        BCH_MEMBER_TIER(m),
411                        data_allowed_str,
412                        data_has_str,
413
414                        BCH_MEMBER_REPLACEMENT(m) < CACHE_REPLACEMENT_NR
415                        ? bch2_cache_replacement_policies[BCH_MEMBER_REPLACEMENT(m)]
416                        : "unknown",
417
418                        BCH_MEMBER_DISCARD(m));
419         }
420 }
421
422 static void bch2_sb_print_crypt(struct bch_sb *sb, struct bch_sb_field *f,
423                                 enum units units)
424 {
425         struct bch_sb_field_crypt *crypt = field_to_type(f, crypt);
426
427         printf("  KFD:                  %llu\n"
428                "  scrypt n:             %llu\n"
429                "  scrypt r:             %llu\n"
430                "  scrypt p:             %llu\n",
431                BCH_CRYPT_KDF_TYPE(crypt),
432                BCH_KDF_SCRYPT_N(crypt),
433                BCH_KDF_SCRYPT_R(crypt),
434                BCH_KDF_SCRYPT_P(crypt));
435 }
436
437 static void bch2_sb_print_replicas(struct bch_sb *sb, struct bch_sb_field *f,
438                                    enum units units)
439 {
440         struct bch_sb_field_replicas *replicas = field_to_type(f, replicas);
441         struct bch_replicas_entry *e;
442         unsigned i;
443
444         for_each_replicas_entry(replicas, e) {
445                 printf_pad(32, "  %s:", bch2_data_types[e->data_type]);
446
447                 putchar('[');
448                 for (i = 0; i < e->nr; i++) {
449                         if (i)
450                                 putchar(' ');
451                         printf("%u", e->devs[i]);
452                 }
453                 printf("]\n");
454         }
455 }
456
457 typedef void (*sb_field_print_fn)(struct bch_sb *, struct bch_sb_field *, enum units);
458
459 struct bch_sb_field_ops {
460         sb_field_print_fn       print;
461 };
462
463 static const struct bch_sb_field_ops bch2_sb_field_ops[] = {
464 #define x(f, nr)                                        \
465         [BCH_SB_FIELD_##f] = {                          \
466                 .print  = bch2_sb_print_##f,            \
467         },
468         BCH_SB_FIELDS()
469 #undef x
470 };
471
472 static inline void bch2_sb_field_print(struct bch_sb *sb,
473                                        struct bch_sb_field *f,
474                                        enum units units)
475 {
476         unsigned type = le32_to_cpu(f->type);
477
478         if (type < BCH_SB_FIELD_NR)
479                 bch2_sb_field_ops[type].print(sb, f, units);
480         else
481                 printf("(unknown field %u)\n", type);
482 }
483
484 void bch2_sb_print(struct bch_sb *sb, bool print_layout,
485                    unsigned fields, enum units units)
486 {
487         struct bch_sb_field_members *mi;
488         char user_uuid_str[40], internal_uuid_str[40];
489         char fields_have_str[200];
490         char label[BCH_SB_LABEL_SIZE + 1];
491         struct bch_sb_field *f;
492         u64 fields_have = 0;
493         unsigned nr_devices = 0;
494
495         memset(label, 0, sizeof(label));
496         memcpy(label, sb->label, sizeof(sb->label));
497         uuid_unparse(sb->user_uuid.b, user_uuid_str);
498         uuid_unparse(sb->uuid.b, internal_uuid_str);
499
500         mi = bch2_sb_get_members(sb);
501         if (mi) {
502                 struct bch_member *m;
503
504                 for (m = mi->members;
505                      m < mi->members + sb->nr_devices;
506                      m++)
507                         nr_devices += bch2_member_exists(m);
508         }
509
510         vstruct_for_each(sb, f)
511                 fields_have |= 1 << le32_to_cpu(f->type);
512         bch2_scnprint_flag_list(fields_have_str, sizeof(fields_have_str),
513                                 bch2_sb_fields, fields_have);
514
515         printf("External UUID:                  %s\n"
516                "Internal UUID:                  %s\n"
517                "Label:                          %s\n"
518                "Version:                        %llu\n"
519                "Block_size:                     %s\n"
520                "Btree node size:                %s\n"
521                "Error action:                   %s\n"
522                "Clean:                          %llu\n"
523
524                "Metadata replicas:              %llu\n"
525                "Data replicas:                  %llu\n"
526
527                "Metadata checksum type:         %s (%llu)\n"
528                "Data checksum type:             %s (%llu)\n"
529                "Compression type:               %s (%llu)\n"
530
531                "String hash type:               %s (%llu)\n"
532                "32 bit inodes:                  %llu\n"
533                "GC reserve percentage:          %llu%%\n"
534                "Root reserve percentage:        %llu%%\n"
535
536                "Devices:                        %u live, %u total\n"
537                "Sections:                       %s\n"
538                "Superblock size:                %llu\n",
539                user_uuid_str,
540                internal_uuid_str,
541                label,
542                le64_to_cpu(sb->version),
543                pr_units(le16_to_cpu(sb->block_size), units),
544                pr_units(BCH_SB_BTREE_NODE_SIZE(sb), units),
545
546                BCH_SB_ERROR_ACTION(sb) < BCH_NR_ERROR_ACTIONS
547                ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)]
548                : "unknown",
549
550                BCH_SB_CLEAN(sb),
551
552                BCH_SB_META_REPLICAS_WANT(sb),
553                BCH_SB_DATA_REPLICAS_WANT(sb),
554
555                BCH_SB_META_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
556                ? bch2_csum_types[BCH_SB_META_CSUM_TYPE(sb)]
557                : "unknown",
558                BCH_SB_META_CSUM_TYPE(sb),
559
560                BCH_SB_DATA_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
561                ? bch2_csum_types[BCH_SB_DATA_CSUM_TYPE(sb)]
562                : "unknown",
563                BCH_SB_DATA_CSUM_TYPE(sb),
564
565                BCH_SB_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_OPT_NR
566                ? bch2_compression_types[BCH_SB_COMPRESSION_TYPE(sb)]
567                : "unknown",
568                BCH_SB_COMPRESSION_TYPE(sb),
569
570                BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
571                ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)]
572                : "unknown",
573                BCH_SB_STR_HASH_TYPE(sb),
574
575                BCH_SB_INODE_32BIT(sb),
576                BCH_SB_GC_RESERVE(sb),
577                BCH_SB_ROOT_RESERVE(sb),
578
579                nr_devices, sb->nr_devices,
580                fields_have_str,
581                vstruct_bytes(sb));
582
583         if (print_layout) {
584                 printf("\n"
585                        "Layout:\n");
586                 bch2_sb_print_layout(sb, units);
587         }
588
589         vstruct_for_each(sb, f) {
590                 unsigned type = le32_to_cpu(f->type);
591                 char name[60];
592
593                 if (!(fields & (1 << type)))
594                         continue;
595
596                 if (type < BCH_SB_FIELD_NR) {
597                         scnprintf(name, sizeof(name), "%s", bch2_sb_fields[type]);
598                         name[0] = toupper(name[0]);
599                 } else {
600                         scnprintf(name, sizeof(name), "(unknown field %u)", type);
601                 }
602
603                 printf("\n%s (size %llu):\n", name, vstruct_bytes(f));
604                 if (type < BCH_SB_FIELD_NR)
605                         bch2_sb_field_print(sb, f, units);
606         }
607 }
608
609 /* ioctl interface: */
610
611 /* Global control device: */
612 int bcachectl_open(void)
613 {
614         return xopen("/dev/bcachefs-ctl", O_RDWR);
615 }
616
617 /* Filesystem handles (ioctl, sysfs dir): */
618
619 #define SYSFS_BASE "/sys/fs/bcachefs/"
620
621 void bcache_fs_close(struct bchfs_handle fs)
622 {
623         close(fs.ioctl_fd);
624         close(fs.sysfs_fd);
625 }
626
627 struct bchfs_handle bcache_fs_open(const char *path)
628 {
629         struct bchfs_handle ret;
630
631         if (!uuid_parse(path, ret.uuid.b)) {
632                 /* It's a UUID, look it up in sysfs: */
633                 char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
634                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
635
636                 char *minor = read_file_str(ret.sysfs_fd, "minor");
637                 char *ctl = mprintf("/dev/bcachefs%s-ctl", minor);
638                 ret.ioctl_fd = xopen(ctl, O_RDWR);
639
640                 free(sysfs);
641                 free(minor);
642                 free(ctl);
643         } else {
644                 /* It's a path: */
645                 ret.ioctl_fd = xopen(path, O_RDONLY);
646
647                 struct bch_ioctl_query_uuid uuid;
648                 xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid);
649
650                 ret.uuid = uuid.uuid;
651
652                 char uuid_str[40];
653                 uuid_unparse(uuid.uuid.b, uuid_str);
654
655                 char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
656                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
657                 free(sysfs);
658         }
659
660         return ret;
661 }