]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/exfat.c
libblkid: constify return values of blkid_probe_get_sb
[thirdparty/util-linux.git] / libblkid / src / superblocks / exfat.c
CommitLineData
8604c255
AN
1/*
2 * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7#include "superblocks.h"
8
9struct exfat_super_block {
6c2484cd
TW
10 uint8_t JumpBoot[3];
11 uint8_t FileSystemName[8];
12 uint8_t MustBeZero[53];
13 uint64_t PartitionOffset;
14 uint64_t VolumeLength;
15 uint32_t FatOffset;
16 uint32_t FatLength;
17 uint32_t ClusterHeapOffset;
18 uint32_t ClusterCount;
19 uint32_t FirstClusterOfRootDirectory;
20 uint8_t VolumeSerialNumber[4];
8604c255 21 struct {
8f6a58ef
KZ
22 uint8_t vermin;
23 uint8_t vermaj;
6c2484cd
TW
24 } FileSystemRevision;
25 uint16_t VolumeFlags;
26 uint8_t BytesPerSectorShift;
27 uint8_t SectorsPerClusterShift;
28 uint8_t NumberOfFats;
29 uint8_t DriveSelect;
30 uint8_t PercentInUse;
31 uint8_t Reserved[7];
32 uint8_t BootCode[390];
33 uint16_t BootSignature;
8604c255
AN
34} __attribute__((__packed__));
35
36struct exfat_entry_label {
37 uint8_t type;
38 uint8_t length;
fb4ed8a5
PR
39 uint8_t name[22];
40 uint8_t reserved[8];
8604c255
AN
41} __attribute__((__packed__));
42
6c2484cd
TW
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)
8604c255
AN
45#define EXFAT_FIRST_DATA_CLUSTER 2
46#define EXFAT_LAST_DATA_CLUSTER 0xffffff6
47#define EXFAT_ENTRY_SIZE 32
48
49#define EXFAT_ENTRY_EOD 0x00
50#define EXFAT_ENTRY_LABEL 0x83
51
07b2450d
YM
52#define EXFAT_MAX_DIR_SIZE (256 * 1024 * 1024)
53
f12cd8d1
KZ
54static uint64_t block_to_offset(const struct exfat_super_block *sb,
55 uint64_t block)
8604c255 56{
6c2484cd 57 return block << sb->BytesPerSectorShift;
8604c255
AN
58}
59
f12cd8d1 60static uint64_t cluster_to_block(const struct exfat_super_block *sb,
8604c255
AN
61 uint32_t cluster)
62{
6c2484cd 63 return le32_to_cpu(sb->ClusterHeapOffset) +
f12cd8d1 64 ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
6c2484cd 65 << sb->SectorsPerClusterShift);
8604c255
AN
66}
67
f12cd8d1 68static uint64_t cluster_to_offset(const struct exfat_super_block *sb,
8604c255
AN
69 uint32_t cluster)
70{
71 return block_to_offset(sb, cluster_to_block(sb, cluster));
72}
73
74static uint32_t next_cluster(blkid_probe pr,
75 const struct exfat_super_block *sb, uint32_t cluster)
76{
2a71e291 77 uint32_t *nextp, next;
f12cd8d1 78 uint64_t fat_offset;
8604c255 79
6c2484cd 80 fat_offset = block_to_offset(sb, le32_to_cpu(sb->FatOffset))
f12cd8d1 81 + (uint64_t) cluster * sizeof(cluster);
2a71e291 82 nextp = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
8604c255 83 sizeof(uint32_t));
2a71e291 84 if (!nextp)
8604c255 85 return 0;
2a71e291
MB
86 memcpy(&next, nextp, sizeof(next));
87 return le32_to_cpu(next);
8604c255
AN
88}
89
90static struct exfat_entry_label *find_label(blkid_probe pr,
91 const struct exfat_super_block *sb)
92{
6c2484cd 93 uint32_t cluster = le32_to_cpu(sb->FirstClusterOfRootDirectory);
f12cd8d1 94 uint64_t offset = cluster_to_offset(sb, cluster);
8604c255 95 uint8_t *entry;
07b2450d 96 const size_t max_iter = EXFAT_MAX_DIR_SIZE / EXFAT_ENTRY_SIZE;
f98b5632 97 size_t i = 0;
8604c255 98
f98b5632 99 for (; i < max_iter; i++) {
8604c255
AN
100 entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
101 EXFAT_ENTRY_SIZE);
102 if (!entry)
103 return NULL;
104 if (entry[0] == EXFAT_ENTRY_EOD)
105 return NULL;
106 if (entry[0] == EXFAT_ENTRY_LABEL)
107 return (struct exfat_entry_label *) entry;
f98b5632 108
8604c255 109 offset += EXFAT_ENTRY_SIZE;
48229f8e 110 if (CLUSTER_SIZE(sb) && (offset % CLUSTER_SIZE(sb)) == 0) {
8604c255
AN
111 cluster = next_cluster(pr, sb, cluster);
112 if (cluster < EXFAT_FIRST_DATA_CLUSTER)
113 return NULL;
114 if (cluster > EXFAT_LAST_DATA_CLUSTER)
115 return NULL;
116 offset = cluster_to_offset(sb, cluster);
117 }
118 }
f98b5632
RS
119
120 return NULL;
8604c255
AN
121}
122
81bbdd0b 123/* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */
7eba8f98 124static uint32_t exfat_boot_checksum(const unsigned char *sectors,
81bbdd0b
TW
125 size_t sector_size)
126{
127 uint32_t n_bytes = sector_size * 11;
128 uint32_t checksum = 0;
129
130 for (size_t i = 0; i < n_bytes; i++) {
131 if ((i == 106) || (i == 107) || (i == 112))
132 continue;
133
134 checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1)
135 + (uint32_t) sectors[i];
136 }
137
138 return checksum;
139}
140
141static int exfat_validate_checksum(blkid_probe pr,
142 const struct exfat_super_block *sb)
143{
144 size_t sector_size = BLOCK_SIZE(sb);
145 /* 11 sectors will be checksummed, the 12th contains the expected */
7eba8f98 146 const unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
81bbdd0b
TW
147 if (!data)
148 return 0;
149
150 uint32_t checksum = exfat_boot_checksum(data, sector_size);
151
152 /* The expected checksum is repeated, check all of them */
153 for (size_t i = 0; i < sector_size / sizeof(uint32_t); i++) {
154 size_t offset = sector_size * 11 + i * 4;
155 uint32_t *expected_addr = (uint32_t *) &data[offset];
156 uint32_t expected = le32_to_cpu(*expected_addr);
157 if (!blkid_probe_verify_csum(pr, checksum, expected))
158 return 0;
159 };
160
161 return 1;
162}
163
8a0f04e6 164static int exfat_valid_superblock(blkid_probe pr, const struct exfat_super_block *sb)
8604c255 165{
1a73b23e 166 if (le16_to_cpu(sb->BootSignature) != 0xAA55)
8a0f04e6
TW
167 return 0;
168
169 if (!CLUSTER_SIZE(sb))
170 return 0;
1a73b23e
TW
171
172 if (memcmp(sb->JumpBoot, "\xEB\x76\x90", 3) != 0)
8a0f04e6 173 return 0;
1a73b23e
TW
174
175 for (size_t i = 0; i < sizeof(sb->MustBeZero); i++)
176 if (sb->MustBeZero[i] != 0x00)
8a0f04e6 177 return 0;
1a73b23e 178
81bbdd0b 179 if (!exfat_validate_checksum(pr, sb))
8a0f04e6
TW
180 return 0;
181
182 return 1;
183}
184
185/* function prototype to avoid warnings (duplicate in partitions/dos.c) */
186extern int blkid_probe_is_exfat(blkid_probe pr);
187
188/*
189 * This function is used by MBR partition table parser to avoid
190 * misinterpretation of exFAT filesystem.
191 */
192int blkid_probe_is_exfat(blkid_probe pr)
193{
4583e6b2 194 const struct exfat_super_block *sb;
8a0f04e6
TW
195 const struct blkid_idmag *mag = NULL;
196 int rc;
197
198 rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
199 if (rc < 0)
200 return rc; /* error */
201 if (rc != BLKID_PROBE_OK || !mag)
202 return 0;
203
204 sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
205 if (!sb)
206 return 0;
207
208 if (memcmp(sb->FileSystemName, "EXFAT ", 8) != 0)
209 return 0;
210
211 return exfat_valid_superblock(pr, sb);
212}
213
214static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
215{
4583e6b2 216 const struct exfat_super_block *sb;
8a0f04e6
TW
217 struct exfat_entry_label *label;
218
219 sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
220 if (!sb)
221 return errno ? -errno : BLKID_PROBE_NONE;
222
223 if (!exfat_valid_superblock(pr, sb))
81bbdd0b
TW
224 return BLKID_PROBE_NONE;
225
8604c255
AN
226 label = find_label(pr, sb);
227 if (label)
228 blkid_probe_set_utf8label(pr, label->name,
81826929 229 min((size_t) label->length * 2, sizeof(label->name)),
35c6ed61 230 UL_ENCODE_UTF16LE);
37f40602
HR
231 else if (errno)
232 return -errno;
8604c255 233
6c2484cd 234 blkid_probe_sprintf_uuid(pr, sb->VolumeSerialNumber, 4,
8604c255 235 "%02hhX%02hhX-%02hhX%02hhX",
6c2484cd
TW
236 sb->VolumeSerialNumber[3], sb->VolumeSerialNumber[2],
237 sb->VolumeSerialNumber[1], sb->VolumeSerialNumber[0]);
8604c255 238
07ce7003 239 blkid_probe_sprintf_version(pr, "%u.%u",
6c2484cd 240 sb->FileSystemRevision.vermaj, sb->FileSystemRevision.vermin);
8604c255 241
0f447d49 242 blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb));
cd129b7d 243 blkid_probe_set_block_size(pr, BLOCK_SIZE(sb));
60a5e8d8 244 blkid_probe_set_fssize(pr, BLOCK_SIZE(sb) * le64_to_cpu(sb->VolumeLength));
cd129b7d 245
37f40602 246 return BLKID_PROBE_OK;
8604c255
AN
247}
248
249const struct blkid_idinfo exfat_idinfo =
250{
251 .name = "exfat",
252 .usage = BLKID_USAGE_FILESYSTEM,
253 .probefunc = probe_exfat,
254 .magics =
255 {
256 { .magic = "EXFAT ", .len = 8, .sboff = 3 },
257 { NULL }
258 }
259};