blkid: add support to recognize exfat to blkid.

we can now identify exfat filesystem.

Change-Id: I870e59a14b6bcd8b45562cdd02c2502d60a9eeff
Signed-off-by: liminghao <liminghao@xiaomi.com>
diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c
index 66ecbc4..fae74a7 100644
--- a/lib/blkid/probe.c
+++ b/lib/blkid/probe.c
@@ -106,7 +106,6 @@
 	if (blkid_llseek(fd, offset, 0) < 0 ||
 	    read(fd, buf, 4096) != 4096)
 		return -BLKID_ERR_IO;
-
 	/* Check for magic number */
 	if (memcmp("\251+N\374", buf, 4) && memcmp("\374N+\251", buf, 4))
 		return -BLKID_ERR_PARAM;
@@ -330,7 +329,7 @@
 	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
 		return -BLKID_ERR_PARAM;
 
-	/* 
+	/*
 	 * If the filesystem does not have a journal and ext2 and ext4
 	 * is not present, then force this to be detected as an
 	 * ext4dev filesystem.
@@ -374,7 +373,7 @@
 	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
 		return -BLKID_ERR_PARAM;
 
-	/* 
+	/*
 	 * If the filesystem does not have a journal and ext2 is not
 	 * present, then force this to be detected as an ext2
 	 * filesystem.
@@ -452,7 +451,7 @@
 	     EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
 		return -BLKID_ERR_PARAM;
 
-	/* 
+	/*
 	 * If ext2 is not present, but ext4 or ext4dev are, then
 	 * disclaim we are ext2
 	 */
@@ -1403,6 +1402,99 @@
     return 0;
 }
 
+static uint64_t exfat_block_to_offset(const struct exfat_super_block *sb,
+                                      uint64_t block)
+{
+    return block << sb->block_bits;
+}
+
+static uint64_t exfat_cluster_to_block(const struct exfat_super_block *sb,
+                                       uint32_t cluster)
+{
+    return sb->cluster_block_start +
+            ((uint64_t)(cluster - EXFAT_FIRST_DATA_CLUSTER) << sb->bpc_bits);
+}
+
+static uint64_t exfat_cluster_to_offset(const struct exfat_super_block *sb,
+                                        uint32_t cluster)
+{
+    return exfat_block_to_offset(sb, exfat_cluster_to_block(sb, cluster));
+}
+
+static uint32_t exfat_next_cluster(struct blkid_probe *probe,
+                                   const struct exfat_super_block *sb,
+                                   uint32_t cluster)
+{
+    uint32_t *next;
+    uint64_t offset;
+
+    offset = exfat_block_to_offset(sb, sb->fat_block_start)
+            + (uint64_t) cluster * sizeof (cluster);
+    next = (uint32_t *)get_buffer(probe, offset, sizeof (uint32_t));
+
+    return next ? *next : 0;
+}
+
+static struct exfat_entry_label *find_exfat_entry_label(
+    struct blkid_probe *probe, const struct exfat_super_block *sb)
+{
+    uint32_t cluster = sb->rootdir_cluster;
+    uint64_t offset = exfat_cluster_to_offset(sb, cluster);
+    uint8_t *entry;
+    const size_t max_iter = 10000;
+    size_t i = 0;
+
+    for (; i < max_iter; ++i) {
+        entry = (uint8_t *)get_buffer(probe, offset, EXFAT_ENTRY_SIZE);
+        if (!entry)
+            return NULL;
+        if (entry[0] == EXFAT_ENTRY_EOD)
+            return NULL;
+        if (entry[0] == EXFAT_ENTRY_LABEL)
+            return (struct exfat_entry_label*) entry;
+
+        offset += EXFAT_ENTRY_SIZE;
+        if (offset % CLUSTER_SIZE(sb) == 0) {
+            cluster = exfat_next_cluster(probe, sb, cluster);
+            if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+                return NULL;
+            if (cluster > EXFAT_LAST_DATA_CLUSTER)
+                return NULL;
+            offset = exfat_cluster_to_offset(sb, cluster);
+        }
+    }
+
+    return NULL;
+}
+
+static int probe_exfat(struct blkid_probe *probe, struct blkid_magic *id,
+                       unsigned char *buf)
+{
+    struct exfat_super_block *sb;
+    struct exfat_entry_label *label;
+    uuid_t uuid;
+    sb = (struct exfat_super_block *)buf;
+    if (!sb || !CLUSTER_SIZE(sb)) {
+        DBG(DEBUG_PROBE, printf("bad exfat superblock.\n"));
+        return errno ? - errno : 1;
+    }
+
+    label = find_exfat_entry_label(probe, sb);
+    if (label) {
+        blkid_set_tag(probe->dev, "LABEL", label->name, label->length);
+    } else {
+        blkid_set_tag(probe->dev, "LABEL", "disk", 4);
+    }
+
+    snprintf(uuid, sizeof (uuid), "%02hhX%02hhX-%02hhX%02hhX",
+             sb->volume_serial[3], sb->volume_serial[2],
+             sb->volume_serial[1], sb->volume_serial[0]);
+
+    set_uuid(probe->dev, uuid, 0);
+
+    return 0;
+}
+
 /*
  * Various filesystem magics that we can check for.  Note that kboff and
  * sboff are in kilobytes and bytes respectively.  All magics are in
@@ -1512,6 +1604,7 @@
   { "lvm2pv",	 1,  0x218,  8, "LVM2 001",		probe_lvm2 },
   { "btrfs",	 64,  0x40,  8, "_BHRfS_M",		probe_btrfs },
   { "f2fs",	 1,      0,  4, "\x10\x20\xf5\xf2",	probe_f2fs },
+  { "exfat",     0,      3,  8, "EXFAT   ",             probe_exfat },
   {   NULL,	 0,	 0,  0, NULL,			NULL }
 };
 
diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h
index 9d02695..c46a332 100644
--- a/lib/blkid/probe.h
+++ b/lib/blkid/probe.h
@@ -14,6 +14,8 @@
 #ifndef _BLKID_PROBE_H
 #define _BLKID_PROBE_H
 
+#include <stdint.h>
+
 #include <blkid/blkid_types.h>
 
 struct blkid_magic;
@@ -763,6 +765,46 @@
     __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
 } __attribute__((__packed__));
 
+struct exfat_super_block {
+    uint8_t jump[3];
+    uint8_t oem_name[8];
+    uint8_t __unused1[53];
+    uint64_t block_start;
+    uint64_t block_count;
+    uint32_t fat_block_start;
+    uint32_t fat_block_count;
+    uint32_t cluster_block_start;
+    uint32_t cluster_count;
+    uint32_t rootdir_cluster;
+    uint8_t volume_serial[4];
+    struct {
+        uint8_t vermin;
+        uint8_t vermaj;
+    } version;
+    uint16_t volume_state;
+    uint8_t block_bits;
+    uint8_t bpc_bits;
+    uint8_t fat_count;
+    uint8_t drive_no;
+    uint8_t allocated_percent;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+    uint8_t type;
+    uint8_t length;
+    uint8_t name[30];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb)   (1 << (sb)->block_bits)
+#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
+
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER  0xffffff6
+#define EXFAT_ENTRY_SIZE         32
+
+#define EXFAT_ENTRY_EOD   0x00
+#define EXFAT_ENTRY_LABEL 0x83
+
 /*
  * Byte swap functions
  */