]>
git.ipfire.org Git - thirdparty/systemd.git/blob - extras/volume_id/lib/fat.c
95735c2d2d4f87e31843751d5d0667024caf4673
2 * volume_id - reads filesystem label and uuid
4 * Copyright (C) 2004-2007 Kay Sievers <kay.sievers@vrfy.org>
5 * Copyright (C) 2007 Ryan Lortie <desrt@desrt.ca>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 #include "libvolume_id.h"
33 #include "libvolume_id-private.h"
35 #define FAT12_MAX 0xff5
36 #define FAT16_MAX 0xfff5
37 #define FAT_ATTR_VOLUME_ID 0x08
38 #define FAT_ATTR_DIR 0x10
39 #define FAT_ATTR_LONG_NAME 0x0f
40 #define FAT_ATTR_MASK 0x3f
41 #define FAT_ENTRY_FREE 0xe5
43 #define VFAT_LFN_SEQ_MASK 0x3f
44 #define VFAT_LFN_SEQ_LAST 0x40
45 #define VFAT_LFN_SEQ_MAX 20
46 #define VFAT_LFN_CHARS_PER_ENTRY (5 + 6 + 2)
47 #define VFAT_LOWERCASE_NAME 0x10
48 #define VFAT_LOWERCASE_EXT 0x08
50 struct vfat_super_block
{
54 uint8_t sectors_per_cluster
;
66 struct fat_super_block
{
74 struct fat32_super_block
{
75 uint32_t fat32_length
;
78 uint32_t root_cluster
;
79 uint16_t fsinfo_sector
;
81 uint16_t reserved2
[6];
93 uint8_t signature1
[4];
94 uint32_t reserved1
[120];
95 uint8_t signature2
[4];
96 uint32_t free_clusters
;
97 uint32_t next_cluster
;
98 uint32_t reserved2
[4];
101 struct vfat_dir_entry
{
105 uint8_t fine_time_creat
;
109 uint16_t cluster_high
;
112 uint16_t cluster_low
;
117 struct vfat_lfn_entry
{
128 static uint8_t fat_lfn_checksum(const uint8_t name
[11])
133 /* http://en.wikipedia.org/wiki/File_Allocation_Table */
134 for (i
= 0; i
< 11; i
++)
135 cksum
= ((cksum
& 1) ? 0x80 : 0) + (cksum
>> 1) + name
[i
];
140 static size_t fat_read_lfn(uint8_t *filename
, size_t fnsize
,
141 struct vfat_dir_entry
*dir
,
142 struct vfat_dir_entry
*entry
)
144 uint8_t buffer
[VFAT_LFN_SEQ_MAX
* VFAT_LFN_CHARS_PER_ENTRY
* 2];
145 uint8_t expected_seq
= 1;
150 cksum
= fat_lfn_checksum(entry
->name
);
152 while (--entry
>= dir
) {
153 struct vfat_lfn_entry
*lfn
= (struct vfat_lfn_entry
*) entry
;
155 if (expected_seq
> VFAT_LFN_SEQ_MAX
)
158 if ((lfn
->attr
& FAT_ATTR_MASK
) != FAT_ATTR_LONG_NAME
)
161 if (lfn
->cksum
!= cksum
)
164 if ((lfn
->seq
& VFAT_LFN_SEQ_MASK
) != expected_seq
++)
167 if (lfn
->cluster
!= 0)
170 /* extra paranoia -- should never happen */
171 if (len
+ sizeof(lfn
->name0
) + sizeof(lfn
->name1
) +
172 sizeof(lfn
->name2
) > sizeof(buffer
))
175 memcpy (&buffer
[len
], lfn
->name0
, sizeof(lfn
->name0
));
176 len
+= sizeof(lfn
->name0
);
177 memcpy (&buffer
[len
], lfn
->name1
, sizeof(lfn
->name1
));
178 len
+= sizeof(lfn
->name1
);
179 memcpy (&buffer
[len
], lfn
->name2
, sizeof(lfn
->name2
));
180 len
+= sizeof(lfn
->name2
);
182 if (lfn
->seq
& VFAT_LFN_SEQ_LAST
) {
183 fnlen
= volume_id_set_unicode16(filename
, fnsize
, buffer
, LE
, len
);
191 static size_t fat_read_filename(uint8_t *filename
, size_t fnsize
,
192 struct vfat_dir_entry
*dir
, struct vfat_dir_entry
*entry
)
197 /* check if maybe we have LFN entries */
198 len
= fat_read_lfn(filename
, fnsize
, dir
, entry
);
202 /* else, read the normal 8.3 name */
203 for (i
= 0; i
< 11; i
++) {
204 if (entry
->lowercase
& ((i
< 8) ? VFAT_LOWERCASE_NAME
: VFAT_LOWERCASE_EXT
))
205 filename
[i
] = tolower(entry
->name
[i
]);
207 filename
[i
] = entry
->name
[i
];
212 filename
[len
] = '\0';
216 /* fills filename, returns string length */
217 static size_t get_fat_attr_volume_id(uint8_t *filename
, size_t fnsize
,
218 struct vfat_dir_entry
*dir
, unsigned int count
)
222 for (i
= 0; i
< count
; i
++) {
224 if (dir
[i
].name
[0] == 0x00) {
230 if (dir
[i
].name
[0] == FAT_ENTRY_FREE
)
234 if ((dir
[i
].attr
& FAT_ATTR_MASK
) == FAT_ATTR_LONG_NAME
)
237 if ((dir
[i
].attr
& (FAT_ATTR_VOLUME_ID
| FAT_ATTR_DIR
)) == FAT_ATTR_VOLUME_ID
) {
238 /* labels do not have file data */
239 if (dir
[i
].cluster_high
!= 0 || dir
[i
].cluster_low
!= 0)
242 dbg("found ATTR_VOLUME_ID id in root dir\n");
243 return fat_read_filename(filename
, fnsize
, dir
, &dir
[i
]);
246 dbg("skip dir entry\n");
252 int volume_id_probe_vfat(struct volume_id
*id
, uint64_t off
, uint64_t size
)
254 uint8_t filename
[255 * 3];
255 struct vfat_super_block
*vs
;
256 struct vfat_dir_entry
*dir
;
257 struct fat32_fsinfo
*fsinfo
;
258 uint16_t sector_size
;
259 uint16_t dir_entries
;
263 uint32_t root_cluster
;
265 uint32_t cluster_count
;
267 uint32_t fat32_length
;
269 uint32_t start_data_sect
;
270 uint16_t root_dir_entries
;
271 uint16_t fsinfo_sect
;
278 info("probing at offset 0x%" PRIx64
"\n", off
);
280 buf
= volume_id_get_buffer(id
, off
, 0x400);
284 /* check signature */
285 if (buf
[510] != 0x55 || buf
[511] != 0xaa)
288 vs
= (struct vfat_super_block
*) buf
;
289 if (memcmp(vs
->sysid
, "NTFS", 4) == 0)
292 /* believe only that's fat, don't trust the version */
293 if (memcmp(vs
->type
.fat32
.magic
, "MSWIN", 5) == 0)
296 if (memcmp(vs
->type
.fat32
.magic
, "FAT32 ", 8) == 0)
299 if (memcmp(vs
->type
.fat
.magic
, "FAT16 ", 8) == 0)
302 if (memcmp(vs
->type
.fat
.magic
, "MSDOS", 5) == 0)
305 if (memcmp(vs
->type
.fat
.magic
, "FAT12 ", 8) == 0)
308 /* some old floppies don't have a magic, expect the boot jump address to match */
309 if ((vs
->boot_jump
[0] != 0xeb || vs
->boot_jump
[2] != 0x90) &&
310 vs
->boot_jump
[0] != 0xe9)
314 info("magic found\n");
315 /* reserverd sector count */
324 if (vs
->media
< 0xf8 && vs
->media
!= 0xf0)
327 /* cluster size check */
328 if (vs
->sectors_per_cluster
== 0 ||
329 (vs
->sectors_per_cluster
& (vs
->sectors_per_cluster
-1)))
332 /* sector size check */
333 sector_size
= le16_to_cpu(vs
->sector_size
);
334 if (sector_size
== 0 || ((sector_size
& (sector_size
-1)) != 0))
337 info("checks passed\n");
338 dbg("sector_size 0x%x\n", sector_size
);
339 dbg("sectors_per_cluster 0x%x\n", vs
->sectors_per_cluster
);
341 dir_entries
= le16_to_cpu(vs
->dir_entries
);
342 reserved
= le16_to_cpu(vs
->reserved
);
343 dbg("reserved 0x%x\n", reserved
);
345 sect_count
= le16_to_cpu(vs
->sectors
);
347 sect_count
= le32_to_cpu(vs
->total_sect
);
348 dbg("sect_count 0x%x\n", sect_count
);
350 fat_length
= le16_to_cpu(vs
->fat_length
);
351 info("fat_length 0x%x\n", fat_length
);
352 fat32_length
= le32_to_cpu(vs
->type
.fat32
.fat32_length
);
353 info("fat32_length 0x%x\n", fat32_length
);
356 fat_size
= fat_length
* vs
->fats
;
357 else if (fat32_length
)
358 fat_size
= fat32_length
* vs
->fats
;
361 info("fat_size 0x%x\n", fat_size
);
363 dir_size
= ((dir_entries
* sizeof(struct vfat_dir_entry
)) +
364 (sector_size
-1)) / sector_size
;
365 info("dir_size 0x%x\n", dir_size
);
367 cluster_count
= sect_count
- (reserved
+ fat_size
+ dir_size
);
368 cluster_count
/= vs
->sectors_per_cluster
;
369 info("cluster_count 0x%x\n", cluster_count
);
372 if (!fat_length
&& fat32_length
)
375 /* cluster_count tells us the format */
376 if (cluster_count
< FAT12_MAX
)
377 strcpy(id
->type_version
, "FAT12");
378 else if (cluster_count
< FAT16_MAX
)
379 strcpy(id
->type_version
, "FAT16");
383 /* the label may be an attribute in the root directory */
384 root_start
= (reserved
+ fat_size
) * sector_size
;
385 dbg("root dir start 0x%" PRIx64
"\n", root_start
);
386 root_dir_entries
= le16_to_cpu(vs
->dir_entries
);
387 dbg("expected entries 0x%x\n", root_dir_entries
);
389 buf_size
= root_dir_entries
* sizeof(struct vfat_dir_entry
);
390 buf
= volume_id_get_buffer(id
, off
+ root_start
, buf_size
);
394 dir
= (struct vfat_dir_entry
*) buf
;
396 fnlen
= get_fat_attr_volume_id(filename
, sizeof(filename
), dir
, root_dir_entries
);
398 vs
= (struct vfat_super_block
*) volume_id_get_buffer(id
, off
, 0x200);
402 if (fnlen
> 0 && memcmp(filename
, "NO NAME ", 11) != 0) {
403 volume_id_set_label_raw(id
, filename
, fnlen
);
404 volume_id_set_label_string(id
, filename
, fnlen
);
405 } else if (memcmp(vs
->type
.fat
.label
, "NO NAME ", 11) != 0) {
406 volume_id_set_label_raw(id
, vs
->type
.fat
.label
, 11);
407 volume_id_set_label_string(id
, vs
->type
.fat
.label
, 11);
409 volume_id_set_uuid(id
, vs
->type
.fat
.serno
, 0, UUID_DOS
);
413 info("looking for FAT32\n");
414 /* FAT32 should have a valid signature in the fsinfo block */
415 fsinfo_sect
= le16_to_cpu(vs
->type
.fat32
.fsinfo_sector
);
416 buf
= volume_id_get_buffer(id
, off
+ (fsinfo_sect
* sector_size
), 0x200);
419 fsinfo
= (struct fat32_fsinfo
*) buf
;
420 info("signature1: 0x%02x%02x%02x%02x\n",
421 fsinfo
->signature1
[0], fsinfo
->signature1
[1],
422 fsinfo
->signature1
[2], fsinfo
->signature1
[3]);
423 info("signature2: 0x%02x%02x%02x%02x\n",
424 fsinfo
->signature2
[0], fsinfo
->signature2
[1],
425 fsinfo
->signature2
[2], fsinfo
->signature2
[3]);
426 if (memcmp(fsinfo
->signature1
, "\x52\x52\x61\x41", 4) != 0)
428 if (memcmp(fsinfo
->signature2
, "\x72\x72\x41\x61", 4) != 0)
430 info("FAT32 signatures match\n");
432 vs
= (struct vfat_super_block
*) volume_id_get_buffer(id
, off
, 0x200);
436 strcpy(id
->type_version
, "FAT32");
438 /* FAT32 root dir is a cluster chain like any other directory */
439 buf_size
= vs
->sectors_per_cluster
* sector_size
;
440 root_cluster
= le32_to_cpu(vs
->type
.fat32
.root_cluster
);
441 dbg("root dir cluster %u\n", root_cluster
);
442 start_data_sect
= reserved
+ fat_size
;
447 uint32_t next_sect_off
;
449 uint64_t fat_entry_off
;
452 dbg("next cluster %u\n", next
);
453 next_sect_off
= (next
- 2) * vs
->sectors_per_cluster
;
454 next_off
= (start_data_sect
+ next_sect_off
) * sector_size
;
455 dbg("cluster offset 0x%" PRIx64
"\n", next_off
);
458 buf
= volume_id_get_buffer(id
, off
+ next_off
, buf_size
);
462 dir
= (struct vfat_dir_entry
*) buf
;
463 count
= buf_size
/ sizeof(struct vfat_dir_entry
);
464 dbg("expected entries 0x%x\n", count
);
466 fnlen
= get_fat_attr_volume_id(filename
, sizeof(filename
), dir
, count
);
471 fat_entry_off
= (reserved
* sector_size
) + (next
* sizeof(uint32_t));
472 buf
= volume_id_get_buffer(id
, off
+ fat_entry_off
, buf_size
);
476 /* set next cluster */
477 next
= le32_to_cpu(*((uint32_t *) buf
)) & 0x0fffffff;
478 if (next
< 2 || next
>= 0x0ffffff0)
482 dbg("reached maximum follow count of root cluster chain, give up\n");
484 vs
= (struct vfat_super_block
*) volume_id_get_buffer(id
, off
, 0x200);
488 if (fnlen
> 0 && memcmp(filename
, "NO NAME ", 11) != 0) {
489 volume_id_set_label_raw(id
, filename
, fnlen
);
490 volume_id_set_label_string(id
, filename
, fnlen
);
491 } else if (memcmp(vs
->type
.fat32
.label
, "NO NAME ", 11) != 0) {
492 volume_id_set_label_raw(id
, vs
->type
.fat32
.label
, 11);
493 volume_id_set_label_string(id
, vs
->type
.fat32
.label
, 11);
495 volume_id_set_uuid(id
, vs
->type
.fat32
.serno
, 0, UUID_DOS
);
498 volume_id_set_usage(id
, VOLUME_ID_FILESYSTEM
);