2 * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
14 #define le32_to_cpu(x) blkid_le32(x)
17 typedef __u16 uint16_t;
18 typedef __u32 uint32_t;
19 typedef __uint64_t uint64_t;
21 typedef struct blkid_probe* blkid_probe;
23 struct exfat_super_block {
29 __u32 fat_block_start;
30 __u32 fat_block_count;
31 __u32 cluster_block_start;
33 __u32 rootdir_cluster;
34 __u8 volume_serial[4];
44 __u8 allocated_percent;
45 } __attribute__((__packed__));
47 struct exfat_entry_label {
51 } __attribute__((__packed__));
53 #define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
54 #define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
55 #define EXFAT_FIRST_DATA_CLUSTER 2
56 #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
57 #define EXFAT_ENTRY_SIZE 32
59 #define EXFAT_ENTRY_EOD 0x00
60 #define EXFAT_ENTRY_LABEL 0x83
62 static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
65 return (blkid_loff_t) block << sb->block_bits;
68 static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
71 return le32_to_cpu(sb->cluster_block_start) +
72 ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
76 static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb,
79 return block_to_offset(sb, cluster_to_block(sb, cluster));
82 extern unsigned char *blkid_probe_get_buffer(struct blkid_probe *pr,
83 blkid_loff_t off, size_t len);
85 static uint32_t next_cluster(blkid_probe pr,
86 const struct exfat_super_block *sb, uint32_t cluster)
89 blkid_loff_t fat_offset;
91 fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
92 + (blkid_loff_t) cluster * sizeof(cluster);
93 next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
97 return le32_to_cpu(*next);
100 static struct exfat_entry_label *find_label(blkid_probe pr,
101 const struct exfat_super_block *sb)
103 uint32_t cluster = le32_to_cpu(sb->rootdir_cluster);
104 blkid_loff_t offset = cluster_to_offset(sb, cluster);
108 entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
112 if (entry[0] == EXFAT_ENTRY_EOD)
114 if (entry[0] == EXFAT_ENTRY_LABEL)
115 return (struct exfat_entry_label *) entry;
116 offset += EXFAT_ENTRY_SIZE;
117 if (offset % CLUSTER_SIZE(sb) == 0) {
118 cluster = next_cluster(pr, sb, cluster);
119 if (cluster < EXFAT_FIRST_DATA_CLUSTER)
121 if (cluster > EXFAT_LAST_DATA_CLUSTER)
123 offset = cluster_to_offset(sb, cluster);
128 static void unicode_16le_to_utf8(unsigned char *str, int out_len,
129 const unsigned char *buf, int in_len)
134 for (i = j = 0; i + 2 <= in_len; i += 2) {
135 c = (buf[i+1] << 8) | buf[i];
139 } else if (c < 0x80) {
142 str[j++] = (unsigned char) c;
143 } else if (c < 0x800) {
146 str[j++] = (unsigned char) (0xc0 | (c >> 6));
147 str[j++] = (unsigned char) (0x80 | (c & 0x3f));
151 str[j++] = (unsigned char) (0xe0 | (c >> 12));
152 str[j++] = (unsigned char) (0x80 | ((c >> 6) & 0x3f));
153 str[j++] = (unsigned char) (0x80 | (c & 0x3f));
159 int probe_exfat(struct blkid_probe *probe,
160 struct blkid_magic *id __BLKID_ATTR((unused)),
163 struct exfat_super_block *sb;
164 struct exfat_entry_label *label;
167 sb = (struct exfat_super_block *) buf;
169 sprintf(serno, "%02X%02X-%02X%02X",
170 sb->volume_serial[3], sb->volume_serial[2],
171 sb->volume_serial[1], sb->volume_serial[0]);
173 blkid_set_tag(probe->dev, "UUID", serno, sizeof(serno)-1);
174 label = find_label(probe, sb);
176 char utf8_label[128];
177 unicode_16le_to_utf8(utf8_label, sizeof(utf8_label), label->name, label->length * 2);
178 blkid_set_tag(probe->dev, "LABEL", utf8_label, 0);