1/* See LICENSE file for copyright and license details. */
2#include "../diskutil.h"
3#include "../util.h"
4
5#include <errno.h>
6#include <fcntl.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <unistd.h>
13
14#if defined(__linux__)
15#include <linux/fs.h>
16#include <sys/ioctl.h>
17#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Apple__)
18#include <sys/disk.h>
19#include <sys/ioctl.h>
20#if defined(__OpenBSD__) || defined(__NetBSD__)
21#include <sys/disklabel.h>
22#include <sys/dkio.h>
23#include <sys/sysctl.h>
24#elif defined(__FreeBSD__)
25#include <dirent.h>
26#include <sys/disk.h>
27#include <sys/sysctl.h>
28#endif
29#endif
30
31int
32blockdev_open(struct BlockDev *dev, const char *path, int writable)
33{
34 struct stat st;
35 int fd, flags;
36
37 flags = writable ? O_RDWR : O_RDONLY;
38 fd = open(path, flags);
39 if (fd < 0)
40 return -1;
41
42 if (fstat(fd, &st) < 0) {
43 close(fd);
44 return -1;
45 }
46
47 dev->fd = fd;
48 dev->size = st.st_size;
49 dev->sec_size = 512;
50
51 if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
52#if defined(__linux__)
53 uint64_t size_bytes = 0;
54 int sec_size = 0;
55#pragma GCC diagnostic push
56#pragma GCC diagnostic ignored "-Woverflow"
57 if (ioctl(fd, BLKGETSIZE64, &size_bytes) >= 0)
58 dev->size = size_bytes;
59 if (ioctl(fd, BLKSSZGET, &sec_size) >= 0)
60 dev->sec_size = sec_size;
61#pragma GCC diagnostic pop
62#elif defined(__FreeBSD__)
63 off_t size_bytes = 0;
64 unsigned int sec_size = 0;
65 if (ioctl(fd, DIOCGMEDIASIZE, &size_bytes) >= 0)
66 dev->size = size_bytes;
67 if (ioctl(fd, DIOCGSECTORSIZE, &sec_size) >= 0)
68 dev->sec_size = sec_size;
69#elif defined(__OpenBSD__) || defined(__NetBSD__)
70 struct disklabel dl;
71 if (ioctl(fd, DIOCGDINFO, &dl) >= 0) {
72 dev->size = (uint64_t)dl.d_secsize * DL_GETDSIZE(&dl);
73 dev->sec_size = dl.d_secsize;
74 }
75#endif
76 }
77
78 return 0;
79}
80
81int
82blockdev_read(struct BlockDev *dev, uint64_t sector, void *buf, size_t sector_count)
83{
84 off_t offset = (off_t)sector * dev->sec_size;
85 size_t size = sector_count * dev->sec_size;
86 ssize_t n;
87
88 if (lseek(dev->fd, offset, SEEK_SET) == (off_t)-1)
89 return -1;
90
91 n = read(dev->fd, buf, size);
92 if (n < 0 || (size_t)n != size)
93 return -1;
94
95 return 0;
96}
97
98int
99blockdev_write(struct BlockDev *dev, uint64_t sector, const void *buf, size_t sector_count)
100{
101 off_t offset = (off_t)sector * dev->sec_size;
102 size_t size = sector_count * dev->sec_size;
103 ssize_t n;
104
105 if (lseek(dev->fd, offset, SEEK_SET) == (off_t)-1)
106 return -1;
107
108 n = write(dev->fd, buf, size);
109 if (n < 0 || (size_t)n != size)
110 return -1;
111
112 return 0;
113}
114
115void
116blockdev_close(struct BlockDev *dev)
117{
118 if (dev->fd >= 0) {
119 close(dev->fd);
120 dev->fd = -1;
121 }
122}
123
124struct BlockDevInfo *
125blockdev_get_list(void)
126{
127#if defined(__linux__)
128 FILE *fp;
129 FILE *mfile;
130 char line[256];
131 char mline[512];
132 char devpath[128];
133 char mnt[256];
134 char fs[64];
135 struct BlockDevInfo *head = NULL;
136 struct BlockDevInfo *tail = NULL;
137 struct BlockDevInfo *info;
138 struct BlockDevInfo *curr;
139 unsigned int major, minor;
140 unsigned long long blocks;
141 char name[64];
142 int is_part;
143
144 fp = fopen(ARUU_LINUX_PATH_PROC_PARTITIONS, "r");
145 if (!fp)
146 return NULL;
147
148 while (fgets(line, sizeof(line), fp)) {
149 if (sscanf(line, " %u %u %llu %63s", &major, &minor, &blocks, name) != 4)
150 continue;
151
152 info = ecalloc(1, sizeof(*info));
153
154 strlcpy(info->name, name, sizeof(info->name));
155 snprintf(info->path, sizeof(info->path), "%s/%s", ARUU_PATH_DEV, name);
156 info->size = blocks * 1024;
157 info->major = major;
158 info->minor = minor;
159
160 /* search tree topology to link partition node under proper
161 * target parent disk */
162 is_part = 0;
163 for (curr = head; curr; curr = curr->next) {
164 size_t len = strlen(curr->name);
165 if (strncmp(name, curr->name, len) == 0) {
166 char c = name[len];
167 if (curr->name[len - 1] >= '0' && curr->name[len - 1] <= '9') {
168 if (c == 'p' && name[len + 1] >= '0' && name[len + 1] <= '9')
169 is_part = 1;
170 } else {
171 if (c >= '0' && c <= '9')
172 is_part = 1;
173 }
174 if (is_part) {
175 strlcpy(info->type, "part", sizeof(info->type));
176 struct BlockDevInfo *p = curr->parts;
177 if (!p) {
178 curr->parts = info;
179 } else {
180 while (p->next)
181 p = p->next;
182 p->next = info;
183 }
184 break;
185 }
186 }
187 }
188
189 if (!is_part) {
190 strlcpy(info->type, "disk", sizeof(info->type));
191 if (!head) {
192 head = info;
193 } else {
194 tail->next = info;
195 }
196 tail = info;
197 }
198 }
199 fclose(fp);
200
201 mfile = fopen(ARUU_LINUX_PATH_PROC_MOUNTS, "r");
202 if (mfile) {
203 while (fgets(mline, sizeof(mline), mfile)) {
204 if (sscanf(mline, "%127s %255s %63s", devpath, mnt, fs) == 3) {
205 char *devname = devpath;
206 size_t dev_len = strlen(ARUU_PATH_DEV);
207 if (strncmp(devpath, ARUU_PATH_DEV, dev_len) == 0 && devpath[dev_len] == '/')
208 devname = devpath + dev_len + 1;
209 struct BlockDevInfo *curr;
210 for (curr = head; curr; curr = curr->next) {
211 if (strcmp(curr->name, devname) == 0) {
212 strlcpy(curr->mountpoint, mnt, sizeof(curr->mountpoint));
213 strlcpy(curr->fstype, fs, sizeof(curr->fstype));
214 }
215 struct BlockDevInfo *part;
216 for (part = curr->parts; part; part = part->next) {
217 if (strcmp(part->name, devname) == 0) {
218 strlcpy(part->mountpoint, mnt, sizeof(part->mountpoint));
219 strlcpy(part->fstype, fs, sizeof(part->fstype));
220 }
221 }
222 }
223 }
224 }
225 fclose(mfile);
226 }
227
228 return head;
229
230#elif defined(__FreeBSD__)
231 char disks[1024];
232 char devpath[128];
233 char *tok;
234 char *ptr;
235 struct BlockDevInfo *head = NULL;
236 struct BlockDevInfo *tail = NULL;
237 struct BlockDevInfo *info;
238 struct BlockDevInfo *part;
239 struct BlockDevInfo *p;
240 struct BlockDev dev;
241 DIR *dir;
242 struct dirent *de;
243 size_t disk_len;
244 size_t len;
245 struct statfs *mntbuf;
246 int mntsize;
247 int i;
248
249 len = sizeof(disks);
250 if (sysctlbyname("kern.disks", disks, &len, NULL, 0) < 0)
251 return NULL;
252
253 ptr = disks;
254 while ((tok = strsep(&ptr, " \t")) != NULL) {
255 if (!*tok)
256 continue;
257
258 info = ecalloc(1, sizeof(*info));
259
260 strlcpy(info->name, tok, sizeof(info->name));
261 snprintf(info->path, sizeof(info->path), "%s/%s", ARUU_PATH_DEV, tok);
262 strlcpy(info->type, "disk", sizeof(info->type));
263
264 snprintf(devpath, sizeof(devpath), "%s/%s", ARUU_PATH_DEV, tok);
265 if (blockdev_open(&dev, devpath, 0) == 0) {
266 info->size = dev.size;
267 blockdev_close(&dev);
268 }
269
270 dir = opendir(ARUU_PATH_DEV);
271 if (dir) {
272 disk_len = strlen(tok);
273 while ((de = readdir(dir)) != NULL) {
274 if (strncmp(de->d_name, tok, disk_len) == 0) {
275 char c = de->d_name[disk_len];
276 if (c == 's' || c == 'p') {
277 part = ecalloc(1, sizeof(*part));
278 strlcpy(part->name, de->d_name, sizeof(part->name));
279 snprintf(part->path, sizeof(part->path), "%s/%s", ARUU_PATH_DEV, de->d_name);
280 strlcpy(part->type, "part", sizeof(part->type));
281 snprintf(devpath, sizeof(devpath), "%s/%s", ARUU_PATH_DEV, de->d_name);
282 if (blockdev_open(&dev, devpath, 0) == 0) {
283 part->size = dev.size;
284 blockdev_close(&dev);
285 }
286 p = info->parts;
287 if (!p) {
288 info->parts = part;
289 } else {
290 while (p->next)
291 p = p->next;
292 p->next = part;
293 }
294 }
295 }
296 }
297 closedir(dir);
298 }
299
300 if (!head) {
301 head = info;
302 } else {
303 tail->next = info;
304 }
305 tail = info;
306 }
307
308 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
309 for (i = 0; i < mntsize; i++) {
310 char *frompath = mntbuf[i].f_mntfromname;
311 char *devname = frompath;
312 size_t dev_len = strlen(ARUU_PATH_DEV);
313 if (strncmp(frompath, ARUU_PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
314 devname = frompath + dev_len + 1;
315 struct BlockDevInfo *curr;
316 for (curr = head; curr; curr = curr->next) {
317 if (strcmp(curr->name, devname) == 0) {
318 strlcpy(curr->mountpoint, mntbuf[i].f_mntonname, sizeof(curr->mountpoint));
319 strlcpy(curr->fstype, mntbuf[i].f_fstypename, sizeof(curr->fstype));
320 }
321 struct BlockDevInfo *part;
322 for (part = curr->parts; part; part = part->next) {
323 if (strcmp(part->name, devname) == 0) {
324 strlcpy(part->mountpoint, mntbuf[i].f_mntonname, sizeof(part->mountpoint));
325 strlcpy(part->fstype, mntbuf[i].f_fstypename, sizeof(part->fstype));
326 }
327 }
328 }
329 }
330
331 return head;
332
333#elif defined(__OpenBSD__)
334 char disknames[1024];
335 char devpath[128];
336 char *tok;
337 char *ptr;
338 char *colon;
339 struct BlockDevInfo *head = NULL;
340 struct BlockDevInfo *tail = NULL;
341 struct BlockDevInfo *info;
342 struct BlockDevInfo *part;
343 struct BlockDevInfo *p;
344 struct BlockDev dev;
345 size_t len;
346 int mib[2];
347 char c;
348 struct statfs *mntbuf;
349 int mntsize;
350 int i;
351
352 mib[0] = CTL_HW;
353 mib[1] = HW_DISKNAMES;
354 len = sizeof(disknames);
355 if (sysctl(mib, 2, disknames, &len, NULL, 0) < 0)
356 return NULL;
357
358 ptr = disknames;
359 while ((tok = strsep(&ptr, ",")) != NULL) {
360 colon = strchr(tok, ':');
361 if (colon)
362 *colon = '\0';
363 if (!*tok)
364 continue;
365
366 info = ecalloc(1, sizeof(*info));
367
368 strlcpy(info->name, tok, sizeof(info->name));
369 snprintf(info->path, sizeof(info->path), "%s/r%sc", ARUU_PATH_DEV, tok);
370 strlcpy(info->type, "disk", sizeof(info->type));
371
372 snprintf(devpath, sizeof(devpath), "%s/r%sc", ARUU_PATH_DEV, tok);
373 if (blockdev_open(&dev, devpath, 0) == 0) {
374 info->size = dev.size;
375 blockdev_close(&dev);
376 }
377
378 for (c = 'a'; c <= 'p'; c++) {
379 if (c == 'c')
380 continue;
381 snprintf(devpath, sizeof(devpath), "%s/r%s%c", ARUU_PATH_DEV, tok, c);
382 if (blockdev_open(&dev, devpath, 0) == 0) {
383 part = ecalloc(1, sizeof(*part));
384 snprintf(part->name, sizeof(part->name), "%s%c", tok, c);
385 snprintf(part->path, sizeof(part->path), "%s/r%s%c", ARUU_PATH_DEV, tok, c);
386 strlcpy(part->type, "part", sizeof(part->type));
387 part->size = dev.size;
388 p = info->parts;
389 if (!p) {
390 info->parts = part;
391 } else {
392 while (p->next)
393 p = p->next;
394 p->next = part;
395 }
396 blockdev_close(&dev);
397 }
398 }
399
400 if (!head) {
401 head = info;
402 } else {
403 tail->next = info;
404 }
405 tail = info;
406 }
407
408 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
409 for (i = 0; i < mntsize; i++) {
410 char *frompath = mntbuf[i].f_mntfromname;
411 char *devname = frompath;
412 size_t dev_len = strlen(ARUU_PATH_DEV);
413 if (strncmp(frompath, ARUU_PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
414 devname = frompath + dev_len + 1;
415 struct BlockDevInfo *curr;
416 for (curr = head; curr; curr = curr->next) {
417 if (strcmp(curr->name, devname) == 0) {
418 strlcpy(curr->mountpoint, mntbuf[i].f_mntonname, sizeof(curr->mountpoint));
419 strlcpy(curr->fstype, mntbuf[i].f_fstypename, sizeof(curr->fstype));
420 }
421 struct BlockDevInfo *part;
422 for (part = curr->parts; part; part = part->next) {
423 if (strcmp(part->name, devname) == 0) {
424 strlcpy(part->mountpoint, mntbuf[i].f_mntonname, sizeof(part->mountpoint));
425 strlcpy(part->fstype, mntbuf[i].f_fstypename, sizeof(part->fstype));
426 }
427 }
428 }
429 }
430
431 return head;
432
433#else
434 struct BlockDevInfo *head = NULL;
435 struct BlockDevInfo *tail = NULL;
436 struct BlockDevInfo *info;
437 struct BlockDev dev;
438 char devname[16];
439 char devpath[128];
440 char c;
441
442 for (c = 'a'; c <= 'z'; c++) {
443 snprintf(devname, sizeof(devname), "sd%c", c);
444 snprintf(devpath, sizeof(devpath), "%s/%s", ARUU_PATH_DEV, devname);
445 if (blockdev_open(&dev, devpath, 0) == 0) {
446 info = ecalloc(1, sizeof(*info));
447 strlcpy(info->name, devname, sizeof(info->name));
448 snprintf(info->path, sizeof(info->path), "%s/%s", ARUU_PATH_DEV, devname);
449 strlcpy(info->type, "disk", sizeof(info->type));
450 info->size = dev.size;
451 if (!head) {
452 head = info;
453 } else {
454 tail->next = info;
455 }
456 tail = info;
457 blockdev_close(&dev);
458 }
459 }
460 return head;
461#endif
462}
463
464void
465blockdev_free_list(struct BlockDevInfo *list)
466{
467 struct BlockDevInfo *curr = list;
468 while (curr) {
469 struct BlockDevInfo *next = curr->next;
470 struct BlockDevInfo *part = curr->parts;
471 while (part) {
472 struct BlockDevInfo *next_part = part->next;
473 free(part);
474 part = next_part;
475 }
476 free(curr);
477 curr = next;
478 }
479}
480
481struct FsType {
482 const char *name;
483 size_t magic_offset;
484 size_t magic_len;
485 uint64_t magic1;
486 uint64_t magic2;
487 size_t uuid_offset;
488 size_t uuid_len;
489 size_t label_offset;
490 size_t label_len;
491};
492
493static const struct FsType fstypes[] = {
494 /* ext2/3/4 */
495 {"ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16},
496 /* vfat / fat32 */
497 {"vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11},
498 /* vfat / fat12/16 */
499 {"vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11},
500 /* ntfs */
501 {"ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0},
502 /* xfs */
503 {"xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12},
504 /* btrfs */
505 {"btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256},
506 /* f2fs */
507 {"f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512},
508 /* ufs1 */
509 {"ufs1", 9564, 4, 0x011954, 0x54190100, 8428, 8, 0, 0},
510 /* ufs2 */
511 {"ufs2", 66908, 4, 0x19540119, 0x19015419, 65772, 8, 66216, 32},
512 /* fossil */
513 {"fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127},
514};
515
516static void
517format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
518{
519 size_t i;
520 char *p;
521
522 p = out;
523 if (strcmp(type, "vfat") == 0) {
524 snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
525 return;
526 }
527
528 if (strcmp(type, "ntfs") == 0) {
529 for (i = 8; i > 0; i--) {
530 snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
531 p += 2;
532 }
533 return;
534 }
535
536 for (i = 0; i < len; i++) {
537 if (i == 4 || i == 6 || i == 8 || i == 10) {
538 *p++ = '-';
539 }
540 snprintf(p, out_len - (p - out), "%02x", buf[i]);
541 p += 2;
542 }
543 *p = '\0';
544}
545
546static int
547zfs_detect(const unsigned char *buf, size_t len)
548{
549 size_t offset;
550 uint64_t magic;
551
552 for (offset = 131072; offset + 8 <= len; offset += 1024) {
553 magic = ((uint64_t)buf[offset + 7] << 56) | ((uint64_t)buf[offset + 6] << 48)
554 | ((uint64_t)buf[offset + 5] << 40) | ((uint64_t)buf[offset + 4] << 32)
555 | ((uint64_t)buf[offset + 3] << 24) | ((uint64_t)buf[offset + 2] << 16)
556 | ((uint64_t)buf[offset + 1] << 8) | (uint64_t)buf[offset];
557
558 if (magic == 0x00bab10c00000000ULL || magic == 0x0cb1ba0000000000ULL || magic == 0x00bab10cULL
559 || magic == 0x0cb1ba00ULL)
560 return 1;
561 }
562 return 0;
563}
564
565struct Plan9FsDef {
566 const char *name;
567 int blocksize;
568 int namelen;
569 int dirblks;
570 int indirblks;
571 int daddrbits;
572};
573
574static const struct Plan9FsDef p9_fs_defs[] = {
575 {"cwfs64x", 16384, 144, 6, 4, 64},
576 {"cwfs64", 16384, 56, 6, 4, 64},
577 {"cwfs", 16384, 28, 6, 2, 32},
578 {"fs64", 8192, 56, 6, 4, 64},
579 {"fs", 4096, 28, 6, 2, 32},
580};
581
582static int
583plan9_magic_check(const unsigned char *p, const char **name)
584{
585 if (memcmp(p, "\x01\x1c\xe5\x0d", 4) == 0 || memcmp(p, "\x0d\xe5\x1c\x01", 4) == 0) {
586 *name = "hjfs";
587 return 1;
588 }
589 if (memcmp(p, "gefs", 4) == 0 || memcmp(p, "sfeg", 4) == 0) {
590 *name = "gefs";
591 return 1;
592 }
593 return 0;
594}
595
596static int
597plan9_detect_other(const unsigned char *buf, size_t len, const char **name)
598{
599 if (len >= 4 && plan9_magic_check(buf, name))
600 return 1;
601 if (len >= 516 && plan9_magic_check(buf + 512, name))
602 return 1;
603 return 0;
604}
605
606static int
607plan9_detect_text(const unsigned char *buf, size_t len, const char **name)
608{
609 int blocksize = 0, namelen = 0, dirblks = 0, indirblks = 0, daddrbits = 0;
610 char line[128];
611 char key[64];
612 int val;
613 size_t i = 0, j;
614
615 if (len >= 256 + 15) {
616 if (memcmp(buf + 256, "kfs wren device", 15) == 0) {
617 *name = "kfs";
618 return 1;
619 }
620 }
621
622 if (len < 512)
623 return 0;
624 i = 512;
625 if (len > 1536)
626 len = 1536;
627
628 while (i < len) {
629 size_t k = 0;
630 while (i < len && buf[i] != '\n' && k < sizeof(line) - 1) {
631 line[k++] = buf[i++];
632 }
633 if (i < len && buf[i] == '\n')
634 i++;
635 line[k] = '\0';
636
637 if (sscanf(line, "%63s %d", key, &val) == 2) {
638 if (strcmp(key, "blocksize") == 0)
639 blocksize = val;
640 else if (strcmp(key, "namelen") == 0)
641 namelen = val;
642 else if (strcmp(key, "dirblks") == 0)
643 dirblks = val;
644 else if (strcmp(key, "indirblks") == 0)
645 indirblks = val;
646 else if (strcmp(key, "daddrbits") == 0)
647 daddrbits = val;
648 }
649 }
650
651 for (j = 0; j < LEN(p9_fs_defs); j++) {
652 if (blocksize == p9_fs_defs[j].blocksize && namelen == p9_fs_defs[j].namelen
653 && dirblks == p9_fs_defs[j].dirblks && indirblks == p9_fs_defs[j].indirblks
654 && daddrbits == p9_fs_defs[j].daddrbits) {
655 *name = p9_fs_defs[j].name;
656 return 1;
657 }
658 }
659
660 return 0;
661}
662
663int
664blockdev_detect_fs(
665 struct BlockDev *dev,
666 char *type,
667 size_t type_sz,
668 char *label,
669 size_t label_sz,
670 char *uuid,
671 size_t uuid_sz
672)
673{
674 unsigned char *buf;
675 size_t read_size = 262144; /* 256kb */
676 size_t sectors_to_read;
677 size_t i, j;
678 int found = 0;
679
680 if (dev->size < read_size)
681 read_size = dev->size;
682
683 buf = emalloc(read_size);
684 sectors_to_read = (read_size + dev->sec_size - 1) / dev->sec_size;
685 if (blockdev_read(dev, 0, buf, sectors_to_read) < 0) {
686 free(buf);
687 return -2;
688 }
689
690 for (i = 0; i < LEN(fstypes); i++) {
691 const struct FsType *fs = &fstypes[i];
692 if (fs->magic_offset + fs->magic_len > read_size)
693 continue;
694
695 uint64_t val = 0;
696 for (j = 0; j < fs->magic_len; j++) {
697 val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
698 }
699
700 if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
701 const char *type_name = fs->name;
702 if (strcmp(fs->name, "ext") == 0) {
703 if (buf[1116] & 4)
704 type_name = "ext3";
705 else if (buf[1120] & 64)
706 type_name = "ext4";
707 else
708 type_name = "ext2";
709 }
710
711 if (type && type_sz)
712 strlcpy(type, type_name, type_sz);
713
714 if (label && label_sz && fs->label_len && fs->label_offset + fs->label_len <= read_size) {
715 snprintf(label, label_sz, "%.*s", (int)fs->label_len, buf + fs->label_offset);
716 /* strip trailing spaces */
717 char *end = label + strlen(label) - 1;
718 while (end >= label && *end == ' ') {
719 *end = '\0';
720 end--;
721 }
722 }
723
724 if (uuid && uuid_sz && fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
725 format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, uuid_sz);
726 }
727
728 found = 1;
729 break;
730 }
731 }
732
733 if (!found && zfs_detect(buf, read_size)) {
734 if (type && type_sz)
735 strlcpy(type, "zfs_member", type_sz);
736 found = 1;
737 }
738
739 if (!found) {
740 const char *p9_name = NULL;
741 if (plan9_detect_other(buf, read_size, &p9_name)
742 || plan9_detect_text(buf, read_size, &p9_name)) {
743 if (type && type_sz)
744 strlcpy(type, p9_name, type_sz);
745 found = 1;
746 }
747 }
748
749 free(buf);
750 return found ? 0 : -1;
751}