master xplshn/aruu / shared / libutil / diskutil.c
  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}