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    /* iso9660: "CD001" at the primary volume descriptor, LBA 16 */
503    {"iso9660", 32769, 5, 0x3130304443ULL, 0, 0, 0, 32808, 32},
504    /* xfs */
505    {"xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12},
506    /* btrfs */
507    {"btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256},
508    /* f2fs */
509    {"f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512},
510    /* ufs1 */
511    {"ufs1", 9564, 4, 0x011954, 0x54190100, 8428, 8, 0, 0},
512    /* ufs2 */
513    {"ufs2", 66908, 4, 0x19540119, 0x19015419, 65772, 8, 66216, 32},
514    /* fossil */
515    {"fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127},
516};
517
518static void
519format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
520{
521  size_t i;
522  char  *p;
523
524  p = out;
525  if (strcmp(type, "vfat") == 0) {
526    snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
527    return;
528  }
529
530  if (strcmp(type, "ntfs") == 0) {
531    for (i = 8; i > 0; i--) {
532      snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
533      p += 2;
534    }
535    return;
536  }
537
538  for (i = 0; i < len; i++) {
539    if (i == 4 || i == 6 || i == 8 || i == 10) {
540      *p++ = '-';
541    }
542    snprintf(p, out_len - (p - out), "%02x", buf[i]);
543    p += 2;
544  }
545  *p = '\0';
546}
547
548static int
549zfs_detect(const unsigned char *buf, size_t len)
550{
551  size_t   offset;
552  uint64_t magic;
553
554  for (offset = 131072; offset + 8 <= len; offset += 1024) {
555    magic = ((uint64_t)buf[offset + 7] << 56) | ((uint64_t)buf[offset + 6] << 48)
556            | ((uint64_t)buf[offset + 5] << 40) | ((uint64_t)buf[offset + 4] << 32)
557            | ((uint64_t)buf[offset + 3] << 24) | ((uint64_t)buf[offset + 2] << 16)
558            | ((uint64_t)buf[offset + 1] << 8) | (uint64_t)buf[offset];
559
560    if (magic == 0x00bab10c00000000ULL || magic == 0x0cb1ba0000000000ULL || magic == 0x00bab10cULL
561        || magic == 0x0cb1ba00ULL)
562      return 1;
563  }
564  return 0;
565}
566
567struct Plan9FsDef {
568  const char *name;
569  int         blocksize;
570  int         namelen;
571  int         dirblks;
572  int         indirblks;
573  int         daddrbits;
574};
575
576static const struct Plan9FsDef p9_fs_defs[] = {
577    {"cwfs64x", 16384, 144, 6, 4, 64},
578    {"cwfs64", 16384, 56, 6, 4, 64},
579    {"cwfs", 16384, 28, 6, 2, 32},
580    {"fs64", 8192, 56, 6, 4, 64},
581    {"fs", 4096, 28, 6, 2, 32},
582};
583
584static int
585plan9_magic_check(const unsigned char *p, const char **name)
586{
587  if (memcmp(p, "\x01\x1c\xe5\x0d", 4) == 0 || memcmp(p, "\x0d\xe5\x1c\x01", 4) == 0) {
588    *name = "hjfs";
589    return 1;
590  }
591  if (memcmp(p, "gefs", 4) == 0 || memcmp(p, "sfeg", 4) == 0) {
592    *name = "gefs";
593    return 1;
594  }
595  return 0;
596}
597
598static int
599plan9_detect_other(const unsigned char *buf, size_t len, const char **name)
600{
601  if (len >= 4 && plan9_magic_check(buf, name))
602    return 1;
603  if (len >= 516 && plan9_magic_check(buf + 512, name))
604    return 1;
605  return 0;
606}
607
608static int
609plan9_detect_text(const unsigned char *buf, size_t len, const char **name)
610{
611  int    blocksize = 0, namelen = 0, dirblks = 0, indirblks = 0, daddrbits = 0;
612  char   line[128];
613  char   key[64];
614  int    val;
615  size_t i = 0, j;
616
617  if (len >= 256 + 15) {
618    if (memcmp(buf + 256, "kfs wren device", 15) == 0) {
619      *name = "kfs";
620      return 1;
621    }
622  }
623
624  if (len < 512)
625    return 0;
626  i = 512;
627  if (len > 1536)
628    len = 1536;
629
630  while (i < len) {
631    size_t k = 0;
632    while (i < len && buf[i] != '\n' && k < sizeof(line) - 1) {
633      line[k++] = buf[i++];
634    }
635    if (i < len && buf[i] == '\n')
636      i++;
637    line[k] = '\0';
638
639    if (sscanf(line, "%63s %d", key, &val) == 2) {
640      if (strcmp(key, "blocksize") == 0)
641        blocksize = val;
642      else if (strcmp(key, "namelen") == 0)
643        namelen = val;
644      else if (strcmp(key, "dirblks") == 0)
645        dirblks = val;
646      else if (strcmp(key, "indirblks") == 0)
647        indirblks = val;
648      else if (strcmp(key, "daddrbits") == 0)
649        daddrbits = val;
650    }
651  }
652
653  for (j = 0; j < LEN(p9_fs_defs); j++) {
654    if (blocksize == p9_fs_defs[j].blocksize && namelen == p9_fs_defs[j].namelen
655        && dirblks == p9_fs_defs[j].dirblks && indirblks == p9_fs_defs[j].indirblks
656        && daddrbits == p9_fs_defs[j].daddrbits) {
657      *name = p9_fs_defs[j].name;
658      return 1;
659    }
660  }
661
662  return 0;
663}
664
665int
666blockdev_detect_fs(
667    struct BlockDev *dev,
668    char            *type,
669    size_t           type_sz,
670    char            *label,
671    size_t           label_sz,
672    char            *uuid,
673    size_t           uuid_sz
674)
675{
676  unsigned char *buf;
677  size_t         read_size = 262144; /* 256kb */
678  size_t         sectors_to_read;
679  size_t         i, j;
680  int            found = 0;
681
682  if (dev->size < read_size)
683    read_size = dev->size;
684
685  buf             = emalloc(read_size);
686  sectors_to_read = (read_size + dev->sec_size - 1) / dev->sec_size;
687  if (blockdev_read(dev, 0, buf, sectors_to_read) < 0) {
688    free(buf);
689    return -2;
690  }
691
692  for (i = 0; i < LEN(fstypes); i++) {
693    const struct FsType *fs = &fstypes[i];
694    if (fs->magic_offset + fs->magic_len > read_size)
695      continue;
696
697    uint64_t val = 0;
698    for (j = 0; j < fs->magic_len; j++) {
699      val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
700    }
701
702    if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
703      const char *type_name = fs->name;
704      if (strcmp(fs->name, "ext") == 0) {
705        if (buf[1116] & 4)
706          type_name = "ext3";
707        else if (buf[1120] & 64)
708          type_name = "ext4";
709        else
710          type_name = "ext2";
711      }
712
713      if (type && type_sz)
714        strlcpy(type, type_name, type_sz);
715
716      if (label && label_sz && fs->label_len && fs->label_offset + fs->label_len <= read_size) {
717        snprintf(label, label_sz, "%.*s", (int)fs->label_len, buf + fs->label_offset);
718        /* strip trailing spaces */
719        char *end = label + strlen(label) - 1;
720        while (end >= label && *end == ' ') {
721          *end = '\0';
722          end--;
723        }
724      }
725
726      if (uuid && uuid_sz && fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
727        format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, uuid_sz);
728      }
729
730      found = 1;
731      break;
732    }
733  }
734
735  if (!found && zfs_detect(buf, read_size)) {
736    if (type && type_sz)
737      strlcpy(type, "zfs_member", type_sz);
738    found = 1;
739  }
740
741  if (!found) {
742    const char *p9_name = NULL;
743    if (plan9_detect_other(buf, read_size, &p9_name)
744        || plan9_detect_text(buf, read_size, &p9_name)) {
745      if (type && type_sz)
746        strlcpy(type, p9_name, type_sz);
747      found = 1;
748    }
749  }
750
751  free(buf);
752  return found ? 0 : -1;
753}