]> err.no Git - util-linux/commitdiff
libblkid: add exFAT file system detection support
authorAndrew Nayenko <resver@gmail.com>
Fri, 28 May 2010 19:22:00 +0000 (23:22 +0400)
committerKarel Zak <kzak@redhat.com>
Tue, 1 Jun 2010 09:59:31 +0000 (11:59 +0200)
Add exFAT file system <http://en.wikipedia.org/wiki/ExFAT> detection support.

Signed-off-by: Andrew Nayenko <resver@gmail.com>
shlibs/blkid/src/superblocks/Makefile.am
shlibs/blkid/src/superblocks/exfat.c [new file with mode: 0644]
shlibs/blkid/src/superblocks/superblocks.c
shlibs/blkid/src/superblocks/superblocks.h
tests/expected/blkid/low-probe-exfat [new file with mode: 0644]
tests/ts/blkid/images-fs/exfat.img.bz2 [new file with mode: 0644]

index f5b88b8e9d4c785f039aa7b4a477e5ec47b1eb36..39b074bad0d02aafdb4eb32942e1298afe395333 100644 (file)
@@ -46,4 +46,5 @@ libblkid_superblocks_la_SOURCES = \
                        bfs.c \
                        drbd.c \
                        vmfs.c \
-                       befs.c
+                       befs.c \
+                       exfat.c
diff --git a/shlibs/blkid/src/superblocks/exfat.c b/shlibs/blkid/src/superblocks/exfat.c
new file mode 100644 (file)
index 0000000..bada3a8
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "superblocks.h"
+
+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 minor;
+               uint8_t major;
+       } 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
+
+static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
+               blkid_loff_t block)
+{
+       return (blkid_loff_t) block << sb->block_bits;
+}
+
+static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
+               uint32_t cluster)
+{
+       return le32_to_cpu(sb->cluster_block_start) +
+                       ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
+                                       << sb->bpc_bits);
+}
+
+static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb,
+               uint32_t cluster)
+{
+       return block_to_offset(sb, cluster_to_block(sb, cluster));
+}
+
+static uint32_t next_cluster(blkid_probe pr,
+               const struct exfat_super_block *sb, uint32_t cluster)
+{
+       uint32_t *next;
+       blkid_loff_t fat_offset;
+
+       fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
+               + (blkid_loff_t) cluster * sizeof(cluster);
+       next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
+                       sizeof(uint32_t));
+       if (!next)
+               return 0;
+       return le32_to_cpu(*next);
+}
+
+static struct exfat_entry_label *find_label(blkid_probe pr,
+               const struct exfat_super_block *sb)
+{
+       uint32_t cluster = le32_to_cpu(sb->rootdir_cluster);
+       blkid_loff_t offset = cluster_to_offset(sb, cluster);
+       uint8_t *entry;
+
+       for (;;) {
+               entry = (uint8_t *) blkid_probe_get_buffer(pr, 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 = next_cluster(pr, sb, cluster);
+                       if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+                               return NULL;
+                       if (cluster > EXFAT_LAST_DATA_CLUSTER)
+                               return NULL;
+                       offset = cluster_to_offset(sb, cluster);
+               }
+       }
+}
+
+static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       struct exfat_super_block *sb;
+       struct exfat_entry_label *label;
+
+       sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
+       if (!sb)
+               return -1;
+
+       label = find_label(pr, sb);
+       if (label)
+               blkid_probe_set_utf8label(pr, label->name,
+                               min(label->length * 2, 30), BLKID_ENC_UTF16LE);
+
+       blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4,
+                       "%02hhX%02hhX-%02hhX%02hhX",
+                       sb->volume_serial[3], sb->volume_serial[2],
+                       sb->volume_serial[1], sb->volume_serial[0]);
+
+       blkid_probe_sprintf_version(pr, "%hu.%hu",
+                       sb->version.major, sb->version.minor);
+
+       return 0;
+}
+
+const struct blkid_idinfo exfat_idinfo =
+{
+       .name           = "exfat",
+       .usage          = BLKID_USAGE_FILESYSTEM,
+       .probefunc      = probe_exfat,
+       .magics         =
+       {
+               { .magic = "EXFAT   ", .len = 8, .sboff = 3 },
+               { NULL }
+       }
+};
index caa169fae561b84b856b0e7b3ffcea76d6e6af66..b80c10b795141a6b441f3ca4190f2c98dbc4e457 100644 (file)
@@ -139,7 +139,8 @@ static const struct blkid_idinfo *idinfos[] =
        &ubifs_idinfo,
        &bfs_idinfo,
        &vmfs_fs_idinfo,
-       &befs_idinfo
+       &befs_idinfo,
+       &exfat_idinfo
 };
 
 /*
index b1fa49d502461f73b31f5a1c934f06c98131bfa4..74cb974321d59b61305f982f2307bc03bb2b4510 100644 (file)
@@ -65,6 +65,7 @@ extern const struct blkid_idinfo vmfs_volume_idinfo;
 extern const struct blkid_idinfo vmfs_fs_idinfo;
 extern const struct blkid_idinfo drbd_idinfo;
 extern const struct blkid_idinfo befs_idinfo;
+extern const struct blkid_idinfo exfat_idinfo;
 
 /*
  * superblock functions
diff --git a/tests/expected/blkid/low-probe-exfat b/tests/expected/blkid/low-probe-exfat
new file mode 100644 (file)
index 0000000..b9defbd
--- /dev/null
@@ -0,0 +1,7 @@
+ID_FS_LABEL=Новый_том
+ID_FS_LABEL_ENC=Новый\x20том
+ID_FS_TYPE=exfat
+ID_FS_USAGE=filesystem
+ID_FS_UUID=9C23-8877
+ID_FS_UUID_ENC=9C23-8877
+ID_FS_VERSION=1.0
diff --git a/tests/ts/blkid/images-fs/exfat.img.bz2 b/tests/ts/blkid/images-fs/exfat.img.bz2
new file mode 100644 (file)
index 0000000..f96c3f0
Binary files /dev/null and b/tests/ts/blkid/images-fs/exfat.img.bz2 differ