]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/vfat.c
libblkid: use internally uint64_t for offsets and sizes
[thirdparty/util-linux.git] / libblkid / src / superblocks / vfat.c
CommitLineData
90438002
KZ
1/*
2 * Copyright (C) 1999 by Andries Brouwer
3 * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
4 * Copyright (C) 2001 by Andreas Dilger
5 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
6 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
7 *
9c863b75
KZ
8 * This file may be redistributed under the terms of the
9 * GNU Lesser General Public License.
90438002
KZ
10 */
11#include <stdio.h>
12#include <stdlib.h>
13#include <unistd.h>
14#include <string.h>
15#include <errno.h>
16#include <ctype.h>
17#include <stdint.h>
18
aaa0ba88 19#include "pt-mbr.h"
1e3cf6a2 20#include "superblocks.h"
90438002 21
973af806
KZ
22/* Yucky misaligned values */
23struct vfat_super_block {
24/* 00*/ unsigned char vs_ignored[3];
25/* 03*/ unsigned char vs_sysid[8];
26/* 0b*/ unsigned char vs_sector_size[2];
27/* 0d*/ uint8_t vs_cluster_size;
28/* 0e*/ uint16_t vs_reserved;
29/* 10*/ uint8_t vs_fats;
30/* 11*/ unsigned char vs_dir_entries[2];
31/* 13*/ unsigned char vs_sectors[2];
32/* 15*/ unsigned char vs_media;
33/* 16*/ uint16_t vs_fat_length;
34/* 18*/ uint16_t vs_secs_track;
35/* 1a*/ uint16_t vs_heads;
36/* 1c*/ uint32_t vs_hidden;
37/* 20*/ uint32_t vs_total_sect;
38/* 24*/ uint32_t vs_fat32_length;
39/* 28*/ uint16_t vs_flags;
40/* 2a*/ uint8_t vs_version[2];
41/* 2c*/ uint32_t vs_root_cluster;
42/* 30*/ uint16_t vs_fsinfo_sector;
43/* 32*/ uint16_t vs_backup_boot;
44/* 34*/ uint16_t vs_reserved2[6];
45/* 40*/ unsigned char vs_unknown[3];
46/* 43*/ unsigned char vs_serno[4];
47/* 47*/ unsigned char vs_label[11];
48/* 52*/ unsigned char vs_magic[8];
49/* 5a*/ unsigned char vs_dummy2[0x1fe - 0x5a];
50/*1fe*/ unsigned char vs_pmagic[2];
51} __attribute__((packed));
52
53/* Yucky misaligned values */
54struct msdos_super_block {
55/* 00*/ unsigned char ms_ignored[3];
56/* 03*/ unsigned char ms_sysid[8];
57/* 0b*/ unsigned char ms_sector_size[2];
58/* 0d*/ uint8_t ms_cluster_size;
59/* 0e*/ uint16_t ms_reserved;
60/* 10*/ uint8_t ms_fats;
61/* 11*/ unsigned char ms_dir_entries[2];
62/* 13*/ unsigned char ms_sectors[2]; /* =0 iff V3 or later */
63/* 15*/ unsigned char ms_media;
64/* 16*/ uint16_t ms_fat_length; /* Sectors per FAT */
65/* 18*/ uint16_t ms_secs_track;
66/* 1a*/ uint16_t ms_heads;
67/* 1c*/ uint32_t ms_hidden;
68/* V3 BPB */
69/* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */
70/* V4 BPB */
71/* 24*/ unsigned char ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */
72/* 27*/ unsigned char ms_serno[4];
73/* 2b*/ unsigned char ms_label[11];
74/* 36*/ unsigned char ms_magic[8];
75/* 3e*/ unsigned char ms_dummy2[0x1fe - 0x3e];
76/*1fe*/ unsigned char ms_pmagic[2];
77} __attribute__((packed));
90438002
KZ
78
79struct vfat_dir_entry {
80 uint8_t name[11];
81 uint8_t attr;
82 uint16_t time_creat;
83 uint16_t date_creat;
84 uint16_t time_acc;
85 uint16_t date_acc;
86 uint16_t cluster_high;
87 uint16_t time_write;
88 uint16_t date_write;
89 uint16_t cluster_low;
90 uint32_t size;
11854e2e 91} __attribute__((packed));
90438002 92
de8e25fa
KZ
93struct fat32_fsinfo {
94 uint8_t signature1[4];
95 uint32_t reserved1[120];
96 uint8_t signature2[4];
97 uint32_t free_clusters;
98 uint32_t next_cluster;
99 uint32_t reserved2[4];
11854e2e 100} __attribute__((packed));
de8e25fa 101
90438002
KZ
102/* maximum number of clusters */
103#define FAT12_MAX 0xFF4
104#define FAT16_MAX 0xFFF4
105#define FAT32_MAX 0x0FFFFFF6
106
107#define FAT_ATTR_VOLUME_ID 0x08
108#define FAT_ATTR_DIR 0x10
109#define FAT_ATTR_LONG_NAME 0x0f
110#define FAT_ATTR_MASK 0x3f
111#define FAT_ENTRY_FREE 0xe5
112
113static const char *no_name = "NO NAME ";
114
973af806
KZ
115#define unaligned_le16(x) \
116 (((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8))
117
a47b2ddd
KZ
118/*
119 * Look for LABEL (name) in the FAT root directory.
120 */
45835b73 121static unsigned char *search_fat_label(blkid_probe pr,
5ec1ee6d 122 uint64_t offset, uint32_t entries)
90438002 123{
a47b2ddd 124 struct vfat_dir_entry *ent, *dir = NULL;
538a2fe9 125 uint32_t i;
90438002 126
c62a6311 127 DBG(LOWPROBE, ul_debug("\tlook for label in root-dir "
0540ea54 128 "(entries: %d, offset: %jd)", entries, offset));
45835b73 129
a47b2ddd
KZ
130 if (!blkid_probe_is_tiny(pr)) {
131 /* large disk, read whole root directory */
45835b73 132 dir = (struct vfat_dir_entry *)
a47b2ddd
KZ
133 blkid_probe_get_buffer(pr,
134 offset,
f12cd8d1 135 (uint64_t) entries *
7e256156 136 sizeof(struct vfat_dir_entry));
a47b2ddd
KZ
137 if (!dir)
138 return NULL;
139 }
140
141 for (i = 0; i < entries; i++) {
142 /*
143 * The root directory could be relatively large (4-16kB).
144 * Fortunately, the LABEL is usually the first entry in the
145 * directory. On tiny disks we call read() per entry.
146 */
147 if (!dir)
148 ent = (struct vfat_dir_entry *)
15a8fb42 149 blkid_probe_get_buffer(pr,
f12cd8d1 150 (uint64_t) offset + (i *
7e256156 151 sizeof(struct vfat_dir_entry)),
45835b73 152 sizeof(struct vfat_dir_entry));
a47b2ddd
KZ
153 else
154 ent = &dir[i];
155
156 if (!ent || ent->name[0] == 0x00)
90438002
KZ
157 break;
158
a47b2ddd
KZ
159 if ((ent->name[0] == FAT_ENTRY_FREE) ||
160 (ent->cluster_high != 0 || ent->cluster_low != 0) ||
161 ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
90438002
KZ
162 continue;
163
a47b2ddd 164 if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
90438002 165 FAT_ATTR_VOLUME_ID) {
c62a6311 166 DBG(LOWPROBE, ul_debug("\tfound fs LABEL at entry %d", i));
a47b2ddd 167 return ent->name;
90438002
KZ
168 }
169 }
a47b2ddd 170 return NULL;
90438002
KZ
171}
172
aaa0ba88
KZ
173static int fat_valid_superblock(blkid_probe pr,
174 const struct blkid_idmag *mag,
973af806
KZ
175 struct msdos_super_block *ms,
176 struct vfat_super_block *vs,
177 uint32_t *cluster_count, uint32_t *fat_size)
90438002 178{
973af806
KZ
179 uint16_t sector_size, dir_entries, reserved;
180 uint32_t sect_count, __fat_size, dir_size, __cluster_count, fat_length;
181 uint32_t max_count;
78419f26 182
973af806
KZ
183 /* extra check for FATs without magic strings */
184 if (mag->len <= 2) {
185 /* Old floppies have a valid MBR signature */
186 if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA)
187 return 0;
78419f26 188
973af806
KZ
189 /*
190 * OS/2 and apparently DFSee will place a FAT12/16-like
191 * pseudo-superblock in the first 512 bytes of non-FAT
192 * filesystems --- at least JFS and HPFS, and possibly others.
193 * So we explicitly check for those filesystems at the
194 * FAT12/16 filesystem magic field identifier, and if they are
195 * present, we rule this out as a FAT filesystem, despite the
196 * FAT-like pseudo-header.
197 */
198 if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) ||
199 (memcmp(ms->ms_magic, "HPFS ", 8) == 0))
200 return 0;
201 }
90438002 202
973af806
KZ
203 /* fat counts(Linux kernel expects at least 1 FAT table) */
204 if (!ms->ms_fats)
205 return 0;
206 if (!ms->ms_reserved)
207 return 0;
208 if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0))
209 return 0;
210 if (!is_power_of_2(ms->ms_cluster_size))
211 return 0;
212
213 sector_size = unaligned_le16(&ms->ms_sector_size);
214 if (!is_power_of_2(sector_size) ||
215 sector_size < 512 || sector_size > 4096)
216 return 0;
217
218 dir_entries = unaligned_le16(&ms->ms_dir_entries);
219 reserved = le16_to_cpu(ms->ms_reserved);
220 sect_count = unaligned_le16(&ms->ms_sectors);
90438002 221
973af806
KZ
222 if (sect_count == 0)
223 sect_count = le32_to_cpu(ms->ms_total_sect);
90438002 224
973af806
KZ
225 fat_length = le16_to_cpu(ms->ms_fat_length);
226 if (fat_length == 0)
227 fat_length = le32_to_cpu(vs->vs_fat32_length);
90438002 228
973af806
KZ
229 __fat_size = fat_length * ms->ms_fats;
230 dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
231 (sector_size-1)) / sector_size;
90438002 232
973af806
KZ
233 __cluster_count = (sect_count - (reserved + __fat_size + dir_size)) /
234 ms->ms_cluster_size;
235 if (!ms->ms_fat_length && vs->vs_fat32_length)
236 max_count = FAT32_MAX;
237 else
238 max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX;
90438002 239
973af806
KZ
240 if (__cluster_count > max_count)
241 return 0;
242
243 if (fat_size)
244 *fat_size = __fat_size;
245 if (cluster_count)
246 *cluster_count = __cluster_count;
247
aaa0ba88
KZ
248 if (blkid_probe_is_wholedisk(pr)) {
249 /* OK, seems like FAT, but it's possible that we found boot
250 * sector with crazy FAT-like stuff (magic strings, media,
251 * etc..) before MBR. Let's make sure that there is no MBR with
252 * usable partition. */
253 unsigned char *buf = (unsigned char *) ms;
254 if (mbr_is_valid_magic(buf)) {
255 struct dos_partition *p0 = mbr_get_partition(buf, 0);
256 if (dos_partition_get_size(p0) != 0 &&
257 (p0->boot_ind == 0 || p0->boot_ind == 0x80))
258 return 0;
259 }
260 }
261
973af806 262 return 1; /* valid */
90438002
KZ
263}
264
973af806
KZ
265/*
266 * This function is used by MBR partition table parser to avoid
267 * misinterpretation of FAT filesystem.
268 */
269int blkid_probe_is_vfat(blkid_probe pr)
90438002
KZ
270{
271 struct vfat_super_block *vs;
272 struct msdos_super_block *ms;
973af806 273 const struct blkid_idmag *mag = NULL;
5b575d43 274 int rc;
de6dd814 275
5b575d43
KZ
276 rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
277 if (rc < 0)
278 return rc; /* error */
279 if (rc != BLKID_PROBE_OK || !mag)
973af806 280 return 0;
90438002 281
973af806
KZ
282 ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
283 if (!ms)
37f40602 284 return errno ? -errno : 0;
90438002
KZ
285 vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
286 if (!vs)
37f40602 287 return errno ? -errno : 0;
973af806 288
aaa0ba88 289 return fat_valid_superblock(pr, mag, ms, vs, NULL, NULL);
973af806
KZ
290}
291
292/* FAT label extraction from the root directory taken from Kay
293 * Sievers's volume_id library */
294static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
295{
296 struct vfat_super_block *vs;
297 struct msdos_super_block *ms;
298 const unsigned char *vol_label = 0;
299 unsigned char *vol_serno = NULL, vol_label_buf[11];
300 uint16_t sector_size = 0, reserved;
301 uint32_t cluster_count, fat_size;
302 const char *version = NULL;
90438002
KZ
303
304 ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
305 if (!ms)
37f40602
HR
306 return errno ? -errno : 1;
307
973af806
KZ
308 vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
309 if (!vs)
37f40602
HR
310 return errno ? -errno : 1;
311
aaa0ba88 312 if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size))
90438002
KZ
313 return 1;
314
973af806 315 sector_size = unaligned_le16(&ms->ms_sector_size);
90438002 316 reserved = le16_to_cpu(ms->ms_reserved);
90438002
KZ
317
318 if (ms->ms_fat_length) {
319 /* the label may be an attribute in the root directory */
973af806
KZ
320 uint32_t root_start = (reserved + fat_size) * sector_size;
321 uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries);
90438002 322
45835b73
KZ
323 vol_label = search_fat_label(pr, root_start, root_dir_entries);
324 if (vol_label) {
325 memcpy(vol_label_buf, vol_label, 11);
326 vol_label = vol_label_buf;
327 }
90438002
KZ
328
329 if (!vol_label || !memcmp(vol_label, no_name, 11))
330 vol_label = ms->ms_label;
331 vol_serno = ms->ms_serno;
332
333 blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
334 sizeof("msdos"));
de8e25fa
KZ
335
336 if (cluster_count < FAT12_MAX)
337 version = "FAT12";
338 else if (cluster_count < FAT16_MAX)
339 version = "FAT16";
973af806
KZ
340
341 } else if (vs->vs_fat32_length) {
90438002 342 unsigned char *buf;
de8e25fa 343 uint16_t fsinfo_sect;
973af806 344 int maxloop = 100;
de8e25fa 345
90438002 346 /* Search the FAT32 root dir for the label attribute */
973af806
KZ
347 uint32_t buf_size = vs->vs_cluster_size * sector_size;
348 uint32_t start_data_sect = reserved + fat_size;
a2e9096f
JL
349 uint32_t entries = le32_to_cpu(vs->vs_fat32_length) *
350 sector_size / sizeof(uint32_t);
973af806 351 uint32_t next = le32_to_cpu(vs->vs_root_cluster);
90438002 352
a2e9096f 353 while (next && next < entries && --maxloop) {
90438002 354 uint32_t next_sect_off;
e1ad8bb3 355 uint64_t next_off, fat_entry_off;
90438002
KZ
356 int count;
357
358 next_sect_off = (next - 2) * vs->vs_cluster_size;
5ec1ee6d 359 next_off = (uint64_t)(start_data_sect + next_sect_off) *
90438002
KZ
360 sector_size;
361
90438002
KZ
362 count = buf_size / sizeof(struct vfat_dir_entry);
363
45835b73
KZ
364 vol_label = search_fat_label(pr, next_off, count);
365 if (vol_label) {
366 memcpy(vol_label_buf, vol_label, 11);
367 vol_label = vol_label_buf;
90438002 368 break;
45835b73 369 }
90438002
KZ
370
371 /* get FAT entry */
2b586af3 372 fat_entry_off = ((uint64_t) reserved * sector_size) +
90438002 373 (next * sizeof(uint32_t));
15a8fb42 374 buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
90438002
KZ
375 if (buf == NULL)
376 break;
377
378 /* set next cluster */
244bf858 379 next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff;
90438002
KZ
380 }
381
973af806
KZ
382 version = "FAT32";
383
90438002
KZ
384 if (!vol_label || !memcmp(vol_label, no_name, 11))
385 vol_label = vs->vs_label;
386 vol_serno = vs->vs_serno;
de8e25fa
KZ
387
388 /*
389 * FAT32 should have a valid signature in the fsinfo block,
390 * but also allow all bytes set to '\0', because some volumes
391 * do not set the signature at all.
392 */
393 fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
394 if (fsinfo_sect) {
395 struct fat32_fsinfo *fsinfo;
396
15a8fb42 397 buf = blkid_probe_get_buffer(pr,
f12cd8d1 398 (uint64_t) fsinfo_sect * sector_size,
de8e25fa
KZ
399 sizeof(struct fat32_fsinfo));
400 if (buf == NULL)
37f40602 401 return errno ? -errno : 1;
de8e25fa
KZ
402
403 fsinfo = (struct fat32_fsinfo *) buf;
404 if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
01802c2e 405 memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 &&
de8e25fa 406 memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
37f40602 407 return 1;
de8e25fa
KZ
408 if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
409 memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
37f40602 410 return 1;
de8e25fa 411 }
90438002
KZ
412 }
413
414 if (vol_label && memcmp(vol_label, no_name, 11))
415 blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
416
417 /* We can't just print them as %04X, because they are unaligned */
973af806
KZ
418 if (vol_serno)
419 blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
420 vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
de8e25fa
KZ
421 if (version)
422 blkid_probe_set_version(pr, version);
423
90438002
KZ
424 return 0;
425}
426
427
161778d7 428const struct blkid_idinfo vfat_idinfo =
90438002
KZ
429{
430 .name = "vfat",
431 .usage = BLKID_USAGE_FILESYSTEM,
432 .probefunc = probe_vfat,
433 .magics =
434 {
435 { .magic = "MSWIN", .len = 5, .sboff = 0x52 },
436 { .magic = "FAT32 ", .len = 8, .sboff = 0x52 },
437 { .magic = "MSDOS", .len = 5, .sboff = 0x36 },
438 { .magic = "FAT16 ", .len = 8, .sboff = 0x36 },
439 { .magic = "FAT12 ", .len = 8, .sboff = 0x36 },
24d1e474 440 { .magic = "FAT ", .len = 8, .sboff = 0x36 },
90438002
KZ
441 { .magic = "\353", .len = 1, },
442 { .magic = "\351", .len = 1, },
443 { .magic = "\125\252", .len = 2, .sboff = 0x1fe },
444 { NULL }
445 }
446};
447