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
{
11 uint8_t FileSystemName
[8];
12 uint8_t MustBeZero
[53];
13 uint64_t PartitionOffset
;
14 uint64_t VolumeLength
;
17 uint32_t ClusterHeapOffset
;
18 uint32_t ClusterCount
;
19 uint32_t FirstClusterOfRootDirectory
;
20 uint8_t VolumeSerialNumber
[4];
26 uint8_t BytesPerSectorShift
;
27 uint8_t SectorsPerClusterShift
;
32 uint8_t BootCode
[390];
33 uint16_t BootSignature
;
34 } __attribute__((__packed__
));
36 struct exfat_entry_label
{
41 } __attribute__((__packed__
));
43 #define BLOCK_SIZE(sb) ((sb)->BytesPerSectorShift < 32 ? (1u << (sb)->BytesPerSectorShift) : 0)
44 #define CLUSTER_SIZE(sb) ((sb)->SectorsPerClusterShift < 32 ? (BLOCK_SIZE(sb) << (sb)->SectorsPerClusterShift) : 0)
45 #define EXFAT_FIRST_DATA_CLUSTER 2
46 #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
47 #define EXFAT_ENTRY_SIZE 32
49 #define EXFAT_ENTRY_EOD 0x00
50 #define EXFAT_ENTRY_LABEL 0x83
52 static uint64_t block_to_offset(const struct exfat_super_block
*sb
,
55 return block
<< sb
->BytesPerSectorShift
;
58 static uint64_t cluster_to_block(const struct exfat_super_block
*sb
,
61 return le32_to_cpu(sb
->ClusterHeapOffset
) +
62 ((uint64_t) (cluster
- EXFAT_FIRST_DATA_CLUSTER
)
63 << sb
->SectorsPerClusterShift
);
66 static uint64_t cluster_to_offset(const struct exfat_super_block
*sb
,
69 return block_to_offset(sb
, cluster_to_block(sb
, cluster
));
72 static uint32_t next_cluster(blkid_probe pr
,
73 const struct exfat_super_block
*sb
, uint32_t cluster
)
75 uint32_t *nextp
, next
;
78 fat_offset
= block_to_offset(sb
, le32_to_cpu(sb
->FatOffset
))
79 + (uint64_t) cluster
* sizeof(cluster
);
80 nextp
= (uint32_t *) blkid_probe_get_buffer(pr
, fat_offset
,
84 memcpy(&next
, nextp
, sizeof(next
));
85 return le32_to_cpu(next
);
88 static struct exfat_entry_label
*find_label(blkid_probe pr
,
89 const struct exfat_super_block
*sb
)
91 uint32_t cluster
= le32_to_cpu(sb
->FirstClusterOfRootDirectory
);
92 uint64_t offset
= cluster_to_offset(sb
, cluster
);
94 const size_t max_iter
= 10000;
97 for (; i
< max_iter
; i
++) {
98 entry
= (uint8_t *) blkid_probe_get_buffer(pr
, offset
,
102 if (entry
[0] == EXFAT_ENTRY_EOD
)
104 if (entry
[0] == EXFAT_ENTRY_LABEL
)
105 return (struct exfat_entry_label
*) entry
;
107 offset
+= EXFAT_ENTRY_SIZE
;
108 if (CLUSTER_SIZE(sb
) && (offset
% CLUSTER_SIZE(sb
)) == 0) {
109 cluster
= next_cluster(pr
, sb
, cluster
);
110 if (cluster
< EXFAT_FIRST_DATA_CLUSTER
)
112 if (cluster
> EXFAT_LAST_DATA_CLUSTER
)
114 offset
= cluster_to_offset(sb
, cluster
);
121 static int probe_exfat(blkid_probe pr
, const struct blkid_idmag
*mag
)
123 struct exfat_super_block
*sb
;
124 struct exfat_entry_label
*label
;
126 sb
= blkid_probe_get_sb(pr
, mag
, struct exfat_super_block
);
127 if (!sb
|| !CLUSTER_SIZE(sb
))
128 return errno
? -errno
: BLKID_PROBE_NONE
;
130 if (le16_to_cpu(sb
->BootSignature
) != 0xAA55)
131 return BLKID_PROBE_NONE
;
133 if (memcmp(sb
->JumpBoot
, "\xEB\x76\x90", 3) != 0)
134 return BLKID_PROBE_NONE
;
136 for (size_t i
= 0; i
< sizeof(sb
->MustBeZero
); i
++)
137 if (sb
->MustBeZero
[i
] != 0x00)
138 return BLKID_PROBE_NONE
;
140 label
= find_label(pr
, sb
);
142 blkid_probe_set_utf8label(pr
, label
->name
,
143 min((size_t) label
->length
* 2, sizeof(label
->name
)),
148 blkid_probe_sprintf_uuid(pr
, sb
->VolumeSerialNumber
, 4,
149 "%02hhX%02hhX-%02hhX%02hhX",
150 sb
->VolumeSerialNumber
[3], sb
->VolumeSerialNumber
[2],
151 sb
->VolumeSerialNumber
[1], sb
->VolumeSerialNumber
[0]);
153 blkid_probe_sprintf_version(pr
, "%u.%u",
154 sb
->FileSystemRevision
.vermaj
, sb
->FileSystemRevision
.vermin
);
157 blkid_probe_set_fsblocksize(pr
, BLOCK_SIZE(sb
));
158 blkid_probe_set_block_size(pr
, BLOCK_SIZE(sb
));
159 blkid_probe_set_fssize(pr
, BLOCK_SIZE(sb
) * le64_to_cpu(sb
->VolumeLength
));
162 return BLKID_PROBE_OK
;
165 const struct blkid_idinfo exfat_idinfo
=
168 .usage
= BLKID_USAGE_FILESYSTEM
,
169 .probefunc
= probe_exfat
,
172 { .magic
= "EXFAT ", .len
= 8, .sboff
= 3 },