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.
7 #include "superblocks.h"
9 struct exfat_super_block
{
12 uint8_t __unused1
[53];
15 uint32_t fat_block_start
;
16 uint32_t fat_block_count
;
17 uint32_t cluster_block_start
;
18 uint32_t cluster_count
;
19 uint32_t rootdir_cluster
;
20 uint8_t volume_serial
[4];
25 uint16_t volume_state
;
30 uint8_t allocated_percent
;
31 } __attribute__((__packed__
));
33 struct exfat_entry_label
{
37 } __attribute__((__packed__
));
39 #define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
40 #define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
41 #define EXFAT_FIRST_DATA_CLUSTER 2
42 #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
43 #define EXFAT_ENTRY_SIZE 32
45 #define EXFAT_ENTRY_EOD 0x00
46 #define EXFAT_ENTRY_LABEL 0x83
48 static blkid_loff_t
block_to_offset(const struct exfat_super_block
*sb
,
51 return (blkid_loff_t
) block
<< sb
->block_bits
;
54 static blkid_loff_t
cluster_to_block(const struct exfat_super_block
*sb
,
57 return le32_to_cpu(sb
->cluster_block_start
) +
58 ((blkid_loff_t
) (cluster
- EXFAT_FIRST_DATA_CLUSTER
)
62 static blkid_loff_t
cluster_to_offset(const struct exfat_super_block
*sb
,
65 return block_to_offset(sb
, cluster_to_block(sb
, cluster
));
68 static uint32_t next_cluster(blkid_probe pr
,
69 const struct exfat_super_block
*sb
, uint32_t cluster
)
72 blkid_loff_t fat_offset
;
74 fat_offset
= block_to_offset(sb
, le32_to_cpu(sb
->fat_block_start
))
75 + (blkid_loff_t
) cluster
* sizeof(cluster
);
76 next
= (uint32_t *) blkid_probe_get_buffer(pr
, fat_offset
,
80 return le32_to_cpu(*next
);
83 static struct exfat_entry_label
*find_label(blkid_probe pr
,
84 const struct exfat_super_block
*sb
)
86 uint32_t cluster
= le32_to_cpu(sb
->rootdir_cluster
);
87 blkid_loff_t offset
= cluster_to_offset(sb
, cluster
);
91 entry
= (uint8_t *) blkid_probe_get_buffer(pr
, offset
,
95 if (entry
[0] == EXFAT_ENTRY_EOD
)
97 if (entry
[0] == EXFAT_ENTRY_LABEL
)
98 return (struct exfat_entry_label
*) entry
;
99 offset
+= EXFAT_ENTRY_SIZE
;
100 if (offset
% CLUSTER_SIZE(sb
) == 0) {
101 cluster
= next_cluster(pr
, sb
, cluster
);
102 if (cluster
< EXFAT_FIRST_DATA_CLUSTER
)
104 if (cluster
> EXFAT_LAST_DATA_CLUSTER
)
106 offset
= cluster_to_offset(sb
, cluster
);
111 static int probe_exfat(blkid_probe pr
, const struct blkid_idmag
*mag
)
113 struct exfat_super_block
*sb
;
114 struct exfat_entry_label
*label
;
116 sb
= blkid_probe_get_sb(pr
, mag
, struct exfat_super_block
);
120 label
= find_label(pr
, sb
);
122 blkid_probe_set_utf8label(pr
, label
->name
,
123 min(label
->length
* 2, 30), BLKID_ENC_UTF16LE
);
125 blkid_probe_sprintf_uuid(pr
, sb
->volume_serial
, 4,
126 "%02hhX%02hhX-%02hhX%02hhX",
127 sb
->volume_serial
[3], sb
->volume_serial
[2],
128 sb
->volume_serial
[1], sb
->volume_serial
[0]);
130 blkid_probe_sprintf_version(pr
, "%u.%u",
131 sb
->version
.major
, sb
->version
.minor
);
136 const struct blkid_idinfo exfat_idinfo
=
139 .usage
= BLKID_USAGE_FILESYSTEM
,
140 .probefunc
= probe_exfat
,
143 { .magic
= "EXFAT ", .len
= 8, .sboff
= 3 },