[LEDE-DEV] [LEDE-DEV,1/3] fstools: add exfat filesystem support
rosysong at rosinson.com
rosysong at rosinson.com
Thu Mar 29 01:23:35 PDT 2018
Add exfat filesystem support for fstools so that the block can detect and show the information
of stroage in exfat format.
Signed-off-by: Rosy Song <rosysong at rosinson.com>
---
CMakeLists.txt | 1 +
libblkid-tiny/blkidP.h | 12 ++++
libblkid-tiny/exfat.c | 155 ++++++++++++++++++++++++++++++++++++++++++
libblkid-tiny/libblkid-tiny.c | 87 ++++++++++++++++++++++++
libblkid-tiny/superblocks.h | 6 ++
5 files changed, 261 insertions(+)
create mode 100644 libblkid-tiny/exfat.c
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e855bd..8efa56a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,6 +24,7 @@ ADD_LIBRARY(blkid-tiny SHARED
libblkid-tiny/ext.c
libblkid-tiny/jffs2.c
libblkid-tiny/vfat.c
+ libblkid-tiny/exfat.c
libblkid-tiny/ntfs.c
libblkid-tiny/hfs.c
libblkid-tiny/swap.c
diff --git a/libblkid-tiny/blkidP.h b/libblkid-tiny/blkidP.h
index 1ba7673..a573166 100644
--- a/libblkid-tiny/blkidP.h
+++ b/libblkid-tiny/blkidP.h
@@ -557,4 +557,16 @@ extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
#define BLKID_ENC_UTF16BE 0
#define BLKID_ENC_UTF16LE 1
+enum uuid_format {
+ UUID_DOS = 0, /* 4 bytes */
+ UUID_NTFS = 1, /* 8 bytes */
+ UUID_DCE = 2, /* 16 bytes */
+ UUID_DCE_STRING = 3, /* 36 bytes (VOLUME_ID_UUID_SIZE) */
+};
+
+enum endian {
+ LE = 0,
+ BE = 1
+};
+
#endif /* _BLKID_BLKIDP_H */
diff --git a/libblkid-tiny/exfat.c b/libblkid-tiny/exfat.c
new file mode 100644
index 0000000..8e69350
--- /dev/null
+++ b/libblkid-tiny/exfat.c
@@ -0,0 +1,155 @@
+ /*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak at redhat.com>
+ * Copyright (C) 2012 S-G Bergh <sgb at systemasis.org>
+ * Copyright (C) 2018 rosysong <rosysong at rosinson.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#if 0
+#include "pt-mbr.h"
+#endif
+
+#include "superblocks.h"
+
+#define EXFAT_SB_OFFSET 0
+#define EXFAT_DIR_ENTRY_SZ 32
+#define EXFAT_MAX_DIR_ENTRIES 100
+
+struct exfat_super_block {
+/* 0x00 */ uint8_t boot_jump[3];
+/* 0x03 */ uint8_t fs_name[8];
+/* 0x0B */ uint8_t must_be_zero[53];
+/* 0x40 */ uint64_t partition_offset;
+/* 0x48 */ uint64_t volume_length;
+/* 0x50 */ uint32_t fat_offset; // Sector address of 1st FAT
+/* 0x54 */ uint32_t fat_size; // In sectors
+/* 0x58 */ uint32_t cluster_heap_offset; // Sector address of Data Region
+/* 0x5C */ uint32_t cluster_count;
+/* 0x60 */ uint32_t root_dir; // Cluster address of Root Directory
+/* 0x64 */ uint8_t vol_serial_nr[4]; // Volume ID
+/* 0x68 */ uint16_t fs_revision; // VV.MM
+/* 0x6A */ uint16_t vol_flags;
+/* 0x6C */ uint8_t bytes_per_sector; // Power of 2: 9 => 512, 12 => 4096
+/* 0x6D */ uint8_t sectors_per_cluster; // Power of 2
+/* 0x6E */ uint8_t nr_of_fats; // 2 for TexFAT
+/* 0x6F */ // ...
+} __attribute__((packed));
+
+struct exfat_dir_entry {
+/* 0x00 */ uint8_t entry_type;
+ union {
+ struct volume_label {
+/* 0x01 */ uint8_t char_count; // Length of label
+/* 0x02 */ uint16_t vol_label[11]; // UTF16 string without null termination
+/* 0x18 */ uint8_t reserved[8];
+/* 0x20 */ } __attribute__((packed)) label;
+ struct volume_guid {
+/* 0x01 */ uint8_t sec_count;
+/* 0x02 */ uint16_t set_checksum;
+/* 0x04 */ uint16_t flags;
+/* 0x06 */ uint8_t vol_guid[16];
+/* 0x16 */ uint8_t reserved[10];
+/* 0x20 */ } __attribute__((packed)) guid;
+ } __attribute__((packed)) type;
+} __attribute__((packed));
+
+static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct exfat_super_block *sb;
+ struct exfat_dir_entry *de;
+ unsigned char *vol_label = 0;
+ unsigned char *vol_serno = NULL;
+ unsigned sector_sz;
+ unsigned cluster_sz;
+ uint64_t root_dir_off;
+ unsigned count;
+ unsigned need_lbl_guid;
+ const char *version = "EXFAT";
+
+ // Primary super block
+ DBG(LOWPROBE, ul_debug("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET));
+ sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
+ if (!sb)
+ return errno ? -errno : 1;
+
+ if (memcmp(sb->fs_name, "EXFAT ", 8) != 0)
+ return -1;
+
+ sector_sz = 1 << sb->bytes_per_sector;
+ cluster_sz = sector_sz << sb->sectors_per_cluster;
+ // There are no clusters 0 and 1, so the first cluster is 2.
+ root_dir_off = (uint64_t)EXFAT_SB_OFFSET +
+ // Hmm... should we cast sector_sz/cluster_sz to uint64_t?
+ (le32_to_cpu(sb->cluster_heap_offset)) * sector_sz +
+ (le32_to_cpu(sb->root_dir) - 2) * cluster_sz;
+ DBG(LOWPROBE, ul_debug("exFAT: sector size 0x%x bytes", sector_sz));
+ DBG(LOWPROBE, ul_debug("exFAT: cluster size 0x%x bytes", cluster_sz));
+ DBG(LOWPROBE, ul_debug("exFAT: root dir is at 0x%llx", (long long)root_dir_off));
+
+ // Use DOS uuid(UUID_DOS) as fallback, if no GUID set
+ vol_serno = sb->vol_serial_nr;
+ blkid_probe_sprintf_uuid_exfat(pr, vol_serno, UUID_DOS);
+
+ // EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
+ // The Root Directory may hold an unlimited number of entries,
+ // so we do not want to check all. Usually label and GUID
+ // are in the beginning, but there are no guarantees.
+ need_lbl_guid = (1 << 0) | (1 << 1);
+ for (count = 0; count < EXFAT_MAX_DIR_ENTRIES; count++) {
+ de = (struct exfat_dir_entry *)
+ blkid_probe_get_buffer(pr, root_dir_off + (count * EXFAT_DIR_ENTRY_SZ), EXFAT_DIR_ENTRY_SZ);
+ if (de == NULL)
+ break;
+ if (de->entry_type == 0x00) {
+ // End of Directory Marker
+ DBG(LOWPROBE, ul_debug("exFAT: End of root directory reached after %u entries", count));
+ break;
+ }
+ if (de->entry_type == 0x83) {
+ // Volume Label Directory Entry
+ vol_label = (unsigned char *)de->type.label.vol_label;
+ blkid_probe_set_unicode16label(pr, vol_label, LE, 2 * de->type.label.char_count);
+ need_lbl_guid &= ~(1 << 0);
+ }
+ if (de->entry_type == 0xA0) {
+ // Volume GUID Directory Entry (UUID_DCE)
+ vol_serno = de->type.guid.vol_guid;
+ blkid_probe_sprintf_uuid_exfat(pr, vol_serno, UUID_DCE);
+ need_lbl_guid &= ~(1 << 1);
+ }
+ if (!need_lbl_guid)
+ break;
+ }
+
+ if (version)
+ blkid_probe_set_version(pr, version);
+
+ return 0;
+}
+
+const struct blkid_idinfo exfat_idinfo =
+{
+ .name = "exfat",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_exfat,
+ .magics =
+ {
+ { .magic = "EXFAT ", .len = 8, .sboff = 0x03 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid-tiny/libblkid-tiny.c b/libblkid-tiny/libblkid-tiny.c
index f020e23..ccfaf63 100644
--- a/libblkid-tiny/libblkid-tiny.c
+++ b/libblkid-tiny/libblkid-tiny.c
@@ -121,6 +121,51 @@ int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len)
return 0;
}
+int blkid_probe_set_unicode16(char *str, size_t len, const unsigned char *buf, enum endian endianess, size_t count)
+{
+ unsigned i, j;
+ unsigned c;
+
+ j = 0;
+ for (i = 0; i + 2 <= count; i += 2) {
+ if (endianess == LE)
+ c = (buf[i+1] << 8) | buf[i];
+ else
+ c = (buf[i] << 8) | buf[i+1];
+ if (c == 0)
+ break;
+ if (j+1 >= len)
+ break;
+ if (c < 0x80) {
+ /* 0xxxxxxx */
+ } else {
+ unsigned char topbits = 0xc0;
+ if (j+2 >= len)
+ break;
+ if (c < 0x800) {
+ /* 110yyyxx 10xxxxxx */
+ } else {
+ if (j+3 >= len)
+ break;
+ /* 1110yyyy 10yyyyxx 10xxxxxx */
+ str[j++] = (unsigned char) (0xe0 | (c >> 12));
+ topbits = 0x80;
+ }
+ str[j++] = (unsigned char) (topbits | ((c >> 6) & 0x3f));
+ c = 0x80 | (c & 0x3f);
+ }
+ str[j++] = (unsigned char) c;
+ }
+ str[j] = '\0';
+
+ return j;
+}
+
+int blkid_probe_set_unicode16label(blkid_probe pr, unsigned char *label, enum endian endianess, size_t len)
+{
+ return blkid_probe_set_unicode16(pr->label, sizeof(pr->label), label, endianess, len);
+}
+
int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name)
{
short unsigned int*u = (short unsigned int*) uuid;
@@ -135,6 +180,47 @@ int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *nam
return 0;
}
+int blkid_probe_sprintf_uuid_exfat(blkid_probe pr, unsigned char *buf, enum uuid_format format)
+{
+ unsigned i;
+ unsigned count = 4 << format;
+
+ /* if set, create string in the same format, the native platform uses */
+ for (i = 0; i < count; i++)
+ if (buf[i] != 0)
+ goto set;
+
+ /* all bytes are zero, leave it empty ("") */
+ return 0;
+
+set:
+ switch (format) {
+ case UUID_DOS:
+ blkid_probe_sprintf_uuid(pr, buf, count, "%02X%02X-%02X%02X",
+ buf[3], buf[2], buf[1], buf[0]);
+ break;
+ case UUID_NTFS:
+ blkid_probe_sprintf_uuid(pr, buf, count, "%02X%02X%02X%02X%02X%02X%02X%02X",
+ buf[7], buf[6], buf[5], buf[4],
+ buf[3], buf[2], buf[1], buf[0]);
+ break;
+ case UUID_DCE:
+ blkid_probe_sprintf_uuid(pr, buf, count,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15]);
+ break;
+ case UUID_DCE_STRING:
+ memcpy(pr->uuid, buf, count);
+ pr->uuid[count] = '\0';
+ break;
+ }
+
+ return count;
+}
+
int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid)
{
return blkid_probe_set_uuid_as(pr, uuid, NULL);
@@ -155,6 +241,7 @@ int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
static const struct blkid_idinfo *idinfos[] =
{
&vfat_idinfo,
+ &exfat_idinfo,
&swsuspend_idinfo,
&swap_idinfo,
&ext4dev_idinfo,
diff --git a/libblkid-tiny/superblocks.h b/libblkid-tiny/superblocks.h
index cde8a40..85c61f7 100644
--- a/libblkid-tiny/superblocks.h
+++ b/libblkid-tiny/superblocks.h
@@ -88,9 +88,15 @@ extern int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
extern int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len);
extern int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
size_t len, int enc);
+extern int blkid_probe_set_unicode16(char *str, size_t len, const unsigned char *buf,
+ enum endian endianess, size_t count);
+extern int blkid_probe_set_unicode16label(blkid_probe pr, unsigned char *label,
+ enum endian endianess, size_t len);
extern int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
size_t len, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 4, 5)));
+extern int blkid_probe_sprintf_uuid_exfat(blkid_probe pr, unsigned char *buf,
+ enum uuid_format format);
extern int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len);
extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid);
--
2.13.3
Lede-dev mailing list
Lede-dev at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/lede-dev
More information about the Lede-dev
mailing list