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
{
38 } __attribute__((__packed__
));
40 #define BLOCK_SIZE(sb) (1u << (sb)->block_bits)
41 #define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
42 #define EXFAT_FIRST_DATA_CLUSTER 2
43 #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
44 #define EXFAT_ENTRY_SIZE 32
46 #define EXFAT_ENTRY_EOD 0x00
47 #define EXFAT_ENTRY_LABEL 0x83
49 static uint64_t block_to_offset(const struct exfat_super_block
*sb
,
52 return block
<< sb
->block_bits
;
55 static uint64_t cluster_to_block(const struct exfat_super_block
*sb
,
58 return le32_to_cpu(sb
->cluster_block_start
) +
59 ((uint64_t) (cluster
- EXFAT_FIRST_DATA_CLUSTER
)
63 static uint64_t cluster_to_offset(const struct exfat_super_block
*sb
,
66 return block_to_offset(sb
, cluster_to_block(sb
, cluster
));
69 static uint32_t next_cluster(blkid_probe pr
,
70 const struct exfat_super_block
*sb
, uint32_t cluster
)
75 fat_offset
= block_to_offset(sb
, le32_to_cpu(sb
->fat_block_start
))
76 + (uint64_t) cluster
* sizeof(cluster
);
77 next
= (uint32_t *) blkid_probe_get_buffer(pr
, fat_offset
,
81 return le32_to_cpu(*next
);
84 static struct exfat_entry_label
*find_label(blkid_probe pr
,
85 const struct exfat_super_block
*sb
)
87 uint32_t cluster
= le32_to_cpu(sb
->rootdir_cluster
);
88 uint64_t offset
= cluster_to_offset(sb
, cluster
);
90 const size_t max_iter
= 10000;
93 for (; i
< max_iter
; i
++) {
94 entry
= (uint8_t *) blkid_probe_get_buffer(pr
, offset
,
98 if (entry
[0] == EXFAT_ENTRY_EOD
)
100 if (entry
[0] == EXFAT_ENTRY_LABEL
)
101 return (struct exfat_entry_label
*) entry
;
103 offset
+= EXFAT_ENTRY_SIZE
;
104 if (offset
% CLUSTER_SIZE(sb
) == 0) {
105 cluster
= next_cluster(pr
, sb
, cluster
);
106 if (cluster
< EXFAT_FIRST_DATA_CLUSTER
)
108 if (cluster
> EXFAT_LAST_DATA_CLUSTER
)
110 offset
= cluster_to_offset(sb
, cluster
);
117 static int probe_exfat(blkid_probe pr
, const struct blkid_idmag
*mag
)
119 struct exfat_super_block
*sb
;
120 struct exfat_entry_label
*label
;
122 sb
= blkid_probe_get_sb(pr
, mag
, struct exfat_super_block
);
123 if (!sb
|| !CLUSTER_SIZE(sb
))
124 return errno
? -errno
: BLKID_PROBE_NONE
;
126 label
= find_label(pr
, sb
);
128 blkid_probe_set_utf8label(pr
, label
->name
,
129 min((size_t) label
->length
* 2, sizeof(label
->name
)),
134 blkid_probe_sprintf_uuid(pr
, sb
->volume_serial
, 4,
135 "%02hhX%02hhX-%02hhX%02hhX",
136 sb
->volume_serial
[3], sb
->volume_serial
[2],
137 sb
->volume_serial
[1], sb
->volume_serial
[0]);
139 blkid_probe_sprintf_version(pr
, "%u.%u",
140 sb
->version
.vermaj
, sb
->version
.vermin
);
142 blkid_probe_set_fsblocksize(pr
, BLOCK_SIZE(sb
));
143 blkid_probe_set_block_size(pr
, BLOCK_SIZE(sb
));
145 return BLKID_PROBE_OK
;
148 const struct blkid_idinfo exfat_idinfo
=
151 .usage
= BLKID_USAGE_FILESYSTEM
,
152 .probefunc
= probe_exfat
,
155 { .magic
= "EXFAT ", .len
= 8, .sboff
= 3 },