1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_fat
);
10 DE_DECLARE_MODULE(de_module_loaddskf
);
11 DE_DECLARE_MODULE(de_module_ea_data
);
13 #define MAX_NESTING_LEVEL 16
22 i64 fn_base_len
, fn_ext_len
;
25 de_ucstring
*short_fn
;
27 struct de_timestamp mod_time
;
36 i64 pending_lfn_bytesused
;
37 #define LFN_CHARS_PER_FRAGMENT 13
38 #define LFN_MAX_FRAGMENTS 20
39 u8 pending_lfn
[LFN_CHARS_PER_FRAGMENT
*2*LFN_MAX_FRAGMENTS
];
42 typedef struct localctx_struct
{
43 de_encoding input_encoding
;
44 int opt_check_rootdir
;
46 // TODO: Decide how to handle different variants of FAT.
47 #define FAT_SUBFMT_UNKNOWN 0
48 #define FAT_SUBFMT_PC 1
49 #define FAT_SUBFMT_ATARIST 2
52 #define FAT_PLATFORM_UNKNOWN 0
53 #define FAT_PLATFORM_PC 1
54 #define FAT_PLATFORM_ATARIST 2
57 u8 num_fat_bits
; // 12, 16, or 32. 0 if unknown.
58 u8 has_atarist_checksum
;
60 i64 sectors_per_cluster
;
61 i64 bytes_per_cluster
;
63 i64 data_region_sector
;
65 i64 num_data_region_clusters
;
68 i64 num_sectors_per_fat
;
69 i64 max_root_dir_entries16
;
71 i64 num_cluster_identifiers
;
72 struct de_strarray
*curpath
;
75 u32
*fat_nextcluster
; // array[num_fat_entries]
76 u8
*cluster_used_flags
; // array[num_fat_entries]
79 static i64
sectornum_to_offset(deark
*c
, lctx
*d
, i64 secnum
)
81 return secnum
* d
->bytes_per_sector
;
84 static int is_good_clusternum(lctx
*d
, i64 cnum
)
87 if(cnum
>= d
->num_cluster_identifiers
) return 0;
91 static i64
clusternum_to_offset(deark
*c
, lctx
*d
, i64 cnum
)
93 return d
->data_region_pos
+ (cnum
-2) * d
->bytes_per_cluster
;
96 static void dbg_timestamp(deark
*c
, struct de_timestamp
*ts
, const char *name
)
98 char timestamp_buf
[64];
100 de_timestamp_to_string(ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
101 de_dbg(c
, "%s: %s", name
, timestamp_buf
);
104 static i64
get_unpadded_len(const u8
*s
, i64 len1
)
109 // Stop at NUL, I guess.
110 for(i
=0; i
<len1
; i
++) {
117 for(i
=len
; i
>0; i
--) {
125 static void do_extract_file(deark
*c
, lctx
*d
, struct member_data
*md
)
129 de_ucstring
*fullfn
= NULL
;
131 i64 nbytes_remaining
;
134 if(md
->filesize
> d
->num_data_region_clusters
* d
->bytes_per_cluster
) {
135 de_err(c
, "%s: Bad file size", ucstring_getpsz_d(md
->short_fn
));
140 fi
= de_finfo_create(c
);
141 fullfn
= ucstring_create(c
);
142 de_strarray_make_path(d
->curpath
, fullfn
, DE_MPFLAG_NOTRAILINGSLASH
);
143 de_finfo_set_name_from_ucstring(c
, fi
, fullfn
, DE_SNFLAG_FULLPATH
);
144 fi
->original_filename_flag
= 1;
146 fi
->is_directory
= 1;
148 if(md
->mod_time
.is_valid
) {
149 fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
] = md
->mod_time
;
152 outf
= dbuf_create_output_file(c
, NULL
, fi
, 0);
154 cur_cluster
= md
->first_cluster
;
156 nbytes_remaining
= 0;
159 nbytes_remaining
= md
->filesize
;
166 if(nbytes_remaining
<= 0) break;
167 if(!is_good_clusternum(d
, cur_cluster
)) break;
168 if(d
->cluster_used_flags
[cur_cluster
]) break;
169 d
->cluster_used_flags
[cur_cluster
] = 1;
170 if(c
->debug_level
>=3) de_dbg3(c
, "cluster: %d", (int)cur_cluster
);
171 dpos
= clusternum_to_offset(c
, d
, cur_cluster
);
172 nbytes_to_copy
= de_min_int(d
->bytes_per_cluster
, nbytes_remaining
);
173 dbuf_copy(c
->infile
, dpos
, nbytes_to_copy
, outf
);
174 nbytes_remaining
-= nbytes_to_copy
;
175 cur_cluster
= (i64
)d
->fat_nextcluster
[cur_cluster
];
178 if(nbytes_remaining
>0) {
179 de_err(c
, "%s: File extraction failed", ucstring_getpsz_d(md
->short_fn
));
185 ucstring_destroy(fullfn
);
186 de_finfo_destroy(c
, fi
);
189 static void do_subdir(deark
*c
, lctx
*d
, struct member_data
*md
, int nesting_level
);
191 static void do_vfat_entry(deark
*c
, lctx
*d
, struct dirctx
*dctx
, i64 pos1
, u8 seq_num_raw
)
195 int is_first_entry
= 0;
198 if(seq_num_raw
==0xe5) {
199 de_dbg(c
, "[deleted VFAT entry]");
204 de_dbg(c
, "seq number: 0x%02x", (UI
)seq_num_raw
);
206 seq_num
= seq_num_raw
& 0xbf;
208 if(seq_num
<1 || seq_num
>LFN_MAX_FRAGMENTS
) {
209 de_warn(c
, "Bad VFAT sequence number (%u)", (UI
)seq_num
);
214 if(seq_num_raw
& 0x40) {
216 de_zeromem(dctx
->pending_lfn
, sizeof(dctx
->pending_lfn
));
217 dctx
->first_seq_num
= seq_num
;
221 if(!dctx
->lfn_valid
|| (seq_num
+1 != dctx
->prev_seq_num
)) {
222 de_dbg(c
, "[stray VFAT entry]");
227 dctx
->prev_seq_num
= seq_num
;
229 startpos_in_lfn
= LFN_CHARS_PER_FRAGMENT
*2*((i64
)seq_num
-1);
231 de_read(&dctx
->pending_lfn
[startpos_in_lfn
+ 0], pos1
+ 1, 10); // 5 chars
232 fn_cksum
= de_getbyte(pos1
+13);
233 de_read(&dctx
->pending_lfn
[startpos_in_lfn
+10], pos1
+14, 12); // 6 more chars
234 de_read(&dctx
->pending_lfn
[startpos_in_lfn
+22], pos1
+28, 4); // 2 more chars
235 de_dbg(c
, "filename checksum (reported): 0x%02x", (UI
)fn_cksum
);
236 if(!is_first_entry
) {
237 if(fn_cksum
!= dctx
->name_cksum
) {
238 de_dbg(c
, "[inconsistent VFAT checksums]");
242 dctx
->name_cksum
= fn_cksum
;
248 static void vfat_cksum_update(const u8
*buf
, size_t buflen
, u8
*cksum
)
252 for(i
=0; i
<buflen
; i
++) {
253 *cksum
= (((*cksum
) & 1) << 7) + ((*cksum
) >> 1) + buf
[i
];
257 // If the long file name seems valid, sets it in md->long_fn for later use.
258 static void handle_vfat_lfn(deark
*c
, lctx
*d
, struct dirctx
*dctx
,
259 struct member_data
*md
)
262 i64 max_len_in_ucs2_chars
;
263 i64 len_in_ucs2_chars
= 0;
266 if(!dctx
->lfn_valid
) goto done
;
267 if(dctx
->prev_seq_num
!= 1) goto done
;
268 if(md
->long_fn
) goto done
;
270 vfat_cksum_update(md
->fn_base
, 8, &cksum_calc
);
271 vfat_cksum_update(md
->fn_ext
, 3, &cksum_calc
);
272 de_dbg(c
, "filename checksum (calculated): 0x%02x", (UI
)cksum_calc
);
273 if(cksum_calc
!= dctx
->name_cksum
) goto done
;
275 max_len_in_ucs2_chars
= LFN_CHARS_PER_FRAGMENT
* (i64
)dctx
->first_seq_num
;
276 if(max_len_in_ucs2_chars
> (i64
)(sizeof(dctx
->pending_lfn
)/2)) goto done
;
277 for(i
=0; i
<max_len_in_ucs2_chars
; i
++) {
278 if(dctx
->pending_lfn
[i
*2]==0x00 && dctx
->pending_lfn
[i
*2+1]==0x00) break;
279 if(dctx
->pending_lfn
[i
*2]==0xff && dctx
->pending_lfn
[i
*2+1]==0xff) break;
283 md
->long_fn
= ucstring_create(c
);
284 ucstring_append_bytes(md
->long_fn
, dctx
->pending_lfn
, len_in_ucs2_chars
*2,
285 0, DE_ENCODING_UTF16LE
);
286 de_dbg(c
, "long filename: \"%s\"", ucstring_getpsz_d(md
->long_fn
));
292 // Reads from md->fn_base* and md->fn_ext*, writes to md->short_fn
293 static void decode_short_filename(deark
*c
, lctx
*d
, struct member_data
*md
)
295 if(md
->fn_base_len
>0) {
296 ucstring_append_bytes(md
->short_fn
, md
->fn_base
, md
->fn_base_len
, 0, d
->input_encoding
);
299 ucstring_append_char(md
->short_fn
, '_');
301 if(md
->fn_ext_len
>0) {
302 ucstring_append_char(md
->short_fn
, '.');
303 ucstring_append_bytes(md
->short_fn
, md
->fn_ext
, md
->fn_ext_len
, 0, d
->input_encoding
);
307 static void decode_volume_label_name(deark
*c
, lctx
*d
, struct member_data
*md
)
309 if(md
->fn_ext_len
>0) {
310 ucstring_append_bytes(md
->short_fn
, md
->fn_base
, 8, 0, d
->input_encoding
);
311 ucstring_append_bytes(md
->short_fn
, md
->fn_ext
, md
->fn_ext_len
, 0, d
->input_encoding
);
314 ucstring_append_bytes(md
->short_fn
, md
->fn_base
, md
->fn_base_len
, 0, d
->input_encoding
);
318 // Returns 0 if this is the end-of-directory marker.
319 static int do_dir_entry(deark
*c
, lctx
*d
, struct dirctx
*dctx
,
320 i64 pos1
, int nesting_level
)
326 int is_volume_label
= 0;
327 int need_curpath_pop
= 0;
328 de_ucstring
*descr
= NULL
;
329 struct member_data
*md
= NULL
;
331 md
= de_malloc(c
, sizeof(struct member_data
));
333 de_dbg(c
, "dir entry at %"I64_FMT
, pos1
);
336 de_read(md
->fn_base
, pos1
+0, 8);
337 de_read(md
->fn_ext
, pos1
+8, 3);
338 firstbyte
= md
->fn_base
[0];
340 if(firstbyte
==0x00) {
341 de_dbg(c
, "[end of dir marker]");
346 md
->attribs
= (UI
)de_getbyte(pos1
+11);
347 descr
= ucstring_create(c
);
348 de_describe_dos_attribs(c
, md
->attribs
, descr
, 0x1);
349 de_dbg(c
, "attribs: 0x%02x (%s)", md
->attribs
, ucstring_getpsz_d(descr
));
350 if((md
->attribs
& 0x3f)==0x0f) {
351 do_vfat_entry(c
, d
, dctx
, pos1
, firstbyte
);
355 if((md
->attribs
& 0x18) == 0x00) {
358 else if((md
->attribs
& 0x18) == 0x08) {
362 else if((md
->attribs
& 0x18) == 0x10) {
366 de_warn(c
, "Invalid directory entry");
372 if(dctx
->lfn_valid
) {
373 handle_vfat_lfn(c
, d
, dctx
, md
);
377 if(firstbyte
==0xe5) {
378 de_dbg(c
, "[deleted]");
380 md
->fn_base
[0] = '?';
382 else if(firstbyte
==0x05) {
383 md
->fn_base
[0] = 0xe5;
386 md
->fn_base_len
= get_unpadded_len(md
->fn_base
, 8);
387 md
->fn_ext_len
= get_unpadded_len(md
->fn_ext
, 3);
389 if(md
->is_subdir
&& md
->fn_base_len
>=1 && md
->fn_base
[0]=='.') {
390 // special "." and ".." dirs
394 md
->short_fn
= ucstring_create(c
);
395 if(is_volume_label
) {
396 decode_volume_label_name(c
, d
, md
);
399 decode_short_filename(c
, d
, md
);
402 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->short_fn
));
404 if(ucstring_isnonempty(md
->long_fn
)) {
405 de_strarray_push(d
->curpath
, md
->long_fn
);
408 de_strarray_push(d
->curpath
, md
->short_fn
);
410 need_curpath_pop
= 1;
412 if(d
->num_fat_bits
<32) {
413 md
->ea_handle
= (UI
)de_getu16le(pos1
+20);
415 de_dbg(c
, "EA handle (if OS/2): %u", md
->ea_handle
);
419 dtime
= de_getu16le(pos1
+22);
420 ddate
= de_getu16le(pos1
+24);
421 de_dos_datetime_to_timestamp(&md
->mod_time
, ddate
, dtime
);
422 dbg_timestamp(c
, &md
->mod_time
, "mod time");
424 // TODO: This is wrong for FAT32.
425 md
->first_cluster
= de_getu16le(pos1
+26);
426 de_dbg(c
, "first cluster: %"I64_FMT
, md
->first_cluster
);
428 md
->filesize
= de_getu32le(pos1
+28);
429 de_dbg(c
, "file size: %"I64_FMT
, md
->filesize
);
431 if(!is_deleted
&& !md
->is_subdir
&& !md
->is_special
) {
432 do_extract_file(c
, d
, md
);
434 else if(!is_deleted
&& md
->is_subdir
&& !md
->is_special
) {
435 do_extract_file(c
, d
, md
);
436 do_subdir(c
, d
, md
, nesting_level
+1);
440 ucstring_destroy(descr
);
442 ucstring_destroy(md
->short_fn
);
443 ucstring_destroy(md
->long_fn
);
445 if(need_curpath_pop
) {
446 de_strarray_pop(d
->curpath
);
448 de_dbg_indent(c
, -1);
452 // Process a contiguous block of directory entries
453 // Returns 0 if an end-of-dir marker was found.
454 static int do_dir_entries(deark
*c
, lctx
*d
, struct dirctx
*dctx
,
455 i64 pos1
, i64 len
, int nesting_level
)
461 num_entries
= len
/32;
462 de_dbg(c
, "num entries: %"I64_FMT
, num_entries
);
464 for(i
=0; i
<num_entries
; i
++) {
465 if(!do_dir_entry(c
, d
, dctx
, pos1
+32*i
, nesting_level
)) {
468 dctx
->dir_entry_count
++;
476 static void destroy_dirctx(deark
*c
, struct dirctx
*dctx
)
482 static void do_subdir(deark
*c
, lctx
*d
, struct member_data
*md
, int nesting_level
)
484 int saved_indent_level
;
487 struct dirctx
*dctx
= NULL
;
489 de_dbg_indent_save(c
, &saved_indent_level
);
491 if(nesting_level
>= MAX_NESTING_LEVEL
) {
492 de_err(c
, "Directories nested too deeply");
496 dctx
= de_malloc(c
, sizeof(struct dirctx
));
498 cur_cluster_num
= md
->first_cluster
;
499 if(!is_good_clusternum(d
, cur_cluster_num
)) {
500 de_err(c
, "Bad subdirectory entry");
503 cur_cluster_pos
= clusternum_to_offset(c
, d
, cur_cluster_num
);
504 de_dbg(c
, "subdir starting at %"I64_FMT
, cur_cluster_pos
);
508 if(!is_good_clusternum(d
, cur_cluster_num
)) {
511 cur_cluster_pos
= clusternum_to_offset(c
, d
, cur_cluster_num
);
512 de_dbg(c
, "[subdir cluster %"I64_FMT
" at %"I64_FMT
"]", cur_cluster_num
, cur_cluster_pos
);
514 if(d
->cluster_used_flags
[cur_cluster_num
]) {
517 d
->cluster_used_flags
[cur_cluster_num
] = 1;
519 if(!do_dir_entries(c
, d
, dctx
, cur_cluster_pos
, d
->bytes_per_cluster
, nesting_level
)) {
523 cur_cluster_num
= d
->fat_nextcluster
[cur_cluster_num
];
527 destroy_dirctx(c
, dctx
);
528 de_dbg_indent_restore(c
, saved_indent_level
);
531 static void do_root_dir(deark
*c
, lctx
*d
)
534 struct dirctx
*dctx
= NULL
;
536 dctx
= de_malloc(c
, sizeof(struct dirctx
));
537 pos1
= sectornum_to_offset(c
, d
, d
->root_dir_sector
);
538 de_dbg(c
, "dir at %"I64_FMT
, pos1
);
540 if(pos1
<d
->bytes_per_sector
) goto done
;
541 (void)do_dir_entries(c
, d
, dctx
, pos1
, d
->max_root_dir_entries16
* 32, 0);
543 destroy_dirctx(c
, dctx
);
544 de_dbg_indent(c
, -1);
547 static int root_dir_seems_valid(deark
*c
, lctx
*d
)
550 i64 max_entries_to_check
;
555 if(d
->num_fat_bits
==32) return 1;
557 if(d
->max_root_dir_entries16
<=0) return 0;
558 pos1
= sectornum_to_offset(c
, d
, d
->root_dir_sector
);
559 if(pos1
+ d
->max_root_dir_entries16
* 32 > c
->infile
->len
) {
563 max_entries_to_check
= de_max_int(d
->max_root_dir_entries16
, 10);
564 for(i
=0; i
<max_entries_to_check
; i
++) {
569 entrypos
= pos1
+ 32*i
;
570 firstbyte
= de_getbyte(entrypos
);
571 if(firstbyte
==0x00) break;
572 if(firstbyte
==0xe5) continue; // Don't validate deleted entries
574 attribs
= de_getbyte(entrypos
+11);
578 else if((attribs
& 0x3f) == 0x0f) {
581 else if((attribs
& 0x18)==0x18) {
582 errcount
++; // dir + vol.label not valid
585 // TODO: It's really lame to only validate the attribs field, when there's
586 // so much more we could be doing. But it's a hard problem. We don't want
587 // to be too sensitive to minor errors.
590 if(errcount
>1 || (errcount
==1 && entrycount
<=1)) {
596 static void do_atarist_boot_checksum(deark
*c
, lctx
*d
, i64 pos1
)
601 for(i
=0; i
<256; i
++) {
602 ck
+= (UI
)de_getu16be(pos1
+i
*2);
606 de_dbg(c
, "Atari ST checksum: 0x%04x", ck
);
608 d
->has_atarist_checksum
= 1;
612 static void do_oem_name(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
614 struct de_stringreaderdata
*srd
;
617 srd
= dbuf_read_string(c
->infile
, pos
, len
, len
, 0, DE_ENCODING_ASCII
);
619 // Require printable ASCII.
620 for(i
=0; i
<len
; i
++) {
621 if(srd
->sz
[i
]<32 || srd
->sz
[i
]>126) {
626 de_dbg(c
, "OEM name: \"%s\"", ucstring_getpsz_d(srd
->str
));
629 de_destroy_stringreaderdata(c
, srd
);
632 static int do_boot_sector(deark
*c
, lctx
*d
, i64 pos1
)
635 i64 num_data_region_sectors
;
636 i64 num_root_dir_sectors
;
637 i64 num_sectors_per_fat16
;
638 i64 num_sectors_per_fat32
= 0;
640 i64 num_sectors32
= 0;
641 i64 num_sectors_per_track
;
648 de_dbg(c
, "boot sector at %"I64_FMT
, pos1
);
651 // BIOS parameter block
652 jmpinstrlen
= (d
->subfmt
==FAT_SUBFMT_ATARIST
)?2:3;
653 de_dbg_hexdump(c
, c
->infile
, pos1
, jmpinstrlen
, jmpinstrlen
, "jump instr", 0);
655 if(d
->subfmt
==FAT_SUBFMT_ATARIST
) {
656 do_oem_name(c
, d
, pos1
+2, 6);
657 de_dbg_hexdump(c
, c
->infile
, pos1
+8, 3, 3, "serial num", 0);
660 do_oem_name(c
, d
, pos1
+3, 8);
664 d
->bytes_per_sector
= de_getu16le_p(&pos
);
665 de_dbg(c
, "bytes per sector: %d", (int)d
->bytes_per_sector
);
666 d
->sectors_per_cluster
= (i64
)de_getbyte_p(&pos
);
667 de_dbg(c
, "sectors per cluster: %d", (int)d
->sectors_per_cluster
);
668 d
->num_rsvd_sectors
= de_getu16le_p(&pos
);
670 de_dbg(c
, "reserved sectors: %d", (int)d
->num_rsvd_sectors
);
671 if(d
->num_rsvd_sectors
==0) {
672 // This happens on some Atari ST disks. Don't know why.
673 d
->num_rsvd_sectors
= 1;
676 d
->num_fats
= (i64
)de_getbyte_p(&pos
);
677 de_dbg(c
, "number of FATs: %d", (int)d
->num_fats
);
679 // This is expected to be 0 for FAT32.
680 d
->max_root_dir_entries16
= de_getu16le_p(&pos
);
681 de_dbg(c
, "max number of root dir entries (if FAT12/16): %d", (int)d
->max_root_dir_entries16
);
683 num_sectors16
= de_getu16le_p(&pos
);
684 de_dbg(c
, "number of sectors (old 16-bit field): %d", (int)num_sectors16
);
685 b
= de_getbyte_p(&pos
);
686 de_dbg(c
, "media descriptor: 0x%02x", (UI
)b
);
687 num_sectors_per_fat16
= de_getu16le_p(&pos
);
688 de_dbg(c
, "sectors per FAT (if FAT12/16): %d", (int)num_sectors_per_fat16
);
690 num_sectors_per_track
= de_getu16le_p(&pos
);
691 de_dbg(c
, "sectors per track: %d", (int)num_sectors_per_track
);
692 num_heads
= de_getu16le_p(&pos
);
693 de_dbg(c
, "number of heads: %d", (int)num_heads
);
696 de_read(cksum_sig
, pos
, 2);
697 de_dbg(c
, "boot sector signature: 0x%02x 0x%02x", (UI
)cksum_sig
[0], (UI
)cksum_sig
[1]);
699 do_atarist_boot_checksum(c
, d
, pos1
);
700 if(d
->has_atarist_checksum
) {
701 d
->platform
= FAT_PLATFORM_ATARIST
;
702 de_dbg(c
, "[This is probably a bootable Atari ST disk.]");
704 else if(cksum_sig
[0]==0x55 && cksum_sig
[1]==0xaa) {
705 d
->platform
= FAT_PLATFORM_PC
;
706 de_dbg(c
, "[Disk has PC-compatible boot code.]");
709 if(num_sectors16
==0) {
710 num_sectors32
= de_getu32le(pos1
+32);
711 de_dbg(c
, "num sectors (new 32-bit field): %"I64_FMT
, num_sectors32
);
714 if(num_sectors_per_fat16
==0) {
715 num_sectors_per_fat32
= de_getu32le(pos1
+36);
716 de_dbg(c
, "sectors per FAT (if FAT32): %u", (UI
)num_sectors_per_fat32
);
719 if(num_sectors_per_fat16
==0) {
720 d
->num_sectors_per_fat
= num_sectors_per_fat32
;
723 d
->num_sectors_per_fat
= num_sectors_per_fat16
;
726 if(num_sectors16
==0) {
727 d
->num_sectors
= num_sectors32
;
730 d
->num_sectors
= num_sectors16
;
733 if(d
->sectors_per_cluster
<1) goto done
;
734 if(d
->bytes_per_sector
<32) goto done
;
735 d
->bytes_per_cluster
= d
->bytes_per_sector
* d
->sectors_per_cluster
;
736 d
->root_dir_sector
= d
->num_rsvd_sectors
+ d
->num_sectors_per_fat
* d
->num_fats
;
737 de_dbg(c
, "root dir pos (calculated): %"I64_FMT
" (sector %"I64_FMT
")",
738 sectornum_to_offset(c
, d
, d
->root_dir_sector
), d
->root_dir_sector
);
740 // num_root_dir_sectors is expected to be 0 for FAT32.
741 num_root_dir_sectors
= (d
->max_root_dir_entries16
*32 + d
->bytes_per_sector
- 1)/d
->bytes_per_sector
;
743 num_data_region_sectors
= d
->num_sectors
- (d
->root_dir_sector
+ num_root_dir_sectors
);
744 if(num_data_region_sectors
<0) goto done
;
745 d
->num_data_region_clusters
= num_data_region_sectors
/ d
->sectors_per_cluster
;
746 de_dbg(c
, "num clusters (calculated): %"I64_FMT
, d
->num_data_region_clusters
);
748 d
->data_region_sector
= d
->root_dir_sector
+ num_root_dir_sectors
;
749 d
->data_region_pos
= d
->data_region_sector
* d
->bytes_per_sector
;
750 de_dbg(c
, "data region pos (calculated): %"I64_FMT
" (sector %"I64_FMT
")", d
->data_region_pos
,
751 d
->data_region_sector
);
753 // (The first cluster is numbered "2".)
754 d
->num_cluster_identifiers
= d
->num_data_region_clusters
+ 2;
756 if(d
->num_data_region_clusters
< 4085) {
757 d
->num_fat_bits
= 12;
759 else if(d
->num_data_region_clusters
< 65525) {
760 d
->num_fat_bits
= 16;
763 d
->num_fat_bits
= 32;
766 de_dbg(c
, "bits per cluster id: %u", (UI
)d
->num_fat_bits
);
772 de_err(c
, "Invalid or unsupported boot sector");
774 de_dbg_indent(c
, -1);
778 static int do_read_fat(deark
*c
, lctx
*d
)
782 i64 fat_idx_to_read
= 0;
786 pos1
= sectornum_to_offset(c
, d
, d
->num_rsvd_sectors
+ fat_idx_to_read
*d
->num_sectors_per_fat
);
787 de_dbg(c
, "FAT#%d at %"I64_FMT
, (int)fat_idx_to_read
, pos1
);
790 if(d
->num_cluster_identifiers
> (i64
)(DE_MAX_SANE_OBJECT_SIZE
/sizeof(u32
))) goto done
;
791 d
->num_fat_entries
= d
->num_cluster_identifiers
;
792 d
->fat_nextcluster
= de_mallocarray(c
, d
->num_fat_entries
, sizeof(u32
));
793 d
->cluster_used_flags
= de_malloc(c
, d
->num_fat_entries
);
796 if(d
->num_fat_bits
==12) {
797 for(i
=0; i
<d
->num_fat_entries
+1; i
+=2) {
800 val
= (UI
)dbuf_getint_ext(c
->infile
, pos
, 3, 1, 0);
802 if(i
< d
->num_fat_entries
) {
803 d
->fat_nextcluster
[i
] = (u32
)(val
& 0xfff);
805 if(i
+1 < d
->num_fat_entries
) {
806 d
->fat_nextcluster
[i
+1] = (u32
)(val
>> 12);
810 else if(d
->num_fat_bits
==16) {
811 for(i
=0; i
<d
->num_fat_entries
; i
++) {
812 d
->fat_nextcluster
[i
] = (u32
)de_getu16le_p(&pos
);
816 de_err(c
, "This type of FAT is not supported");
820 if(c
->debug_level
>=3) {
821 for(i
=0; i
<d
->num_fat_entries
; i
++) {
822 de_dbg3(c
, "fat[%"I64_FMT
"]: %"I64_FMT
, i
, (i64
)d
->fat_nextcluster
[i
]);
828 de_dbg_indent(c
, -1);
832 static void de_run_fat(deark
*c
, de_module_params
*mparams
)
836 int got_root_dir
= 0;
837 de_encoding default_encoding
= DE_ENCODING_CP437_G
;
841 // 0x1 = No valid FAT directory structure found
842 mparams
->out_params
.flags
= 0;
845 d
= de_malloc(c
, sizeof(lctx
));
847 d
->opt_check_rootdir
= de_get_ext_option_bool(c
, "fat:checkroot", 1);
848 s
= de_get_ext_option(c
, "fat:subfmt");
850 if(!de_strcmp(s
, "pc")) {
851 d
->subfmt_req
= FAT_SUBFMT_PC
;
853 else if(!de_strcmp(s
, "atarist")) {
854 d
->subfmt_req
= FAT_SUBFMT_ATARIST
;
857 d
->subfmt
= d
->subfmt_req
;
858 if(d
->subfmt
==FAT_SUBFMT_ATARIST
) {
859 default_encoding
= DE_ENCODING_ATARIST
;
862 d
->input_encoding
= de_get_input_encoding(c
, mparams
, default_encoding
);
865 if(!do_boot_sector(c
, d
, 0)) goto done
;
866 if(d
->num_fat_bits
==0) goto done
;
868 switch(d
->platform
) {
869 case FAT_PLATFORM_PC
:
870 de_declare_fmtf(c
, "FAT%d - PC", d
->num_fat_bits
);
872 case FAT_PLATFORM_ATARIST
:
873 de_declare_fmtf(c
, "FAT%d - Atari ST", d
->num_fat_bits
);
876 de_declare_fmtf(c
, "FAT%d - Unknown platform", d
->num_fat_bits
);
880 if(!do_read_fat(c
, d
)) goto done
;
882 if(d
->opt_check_rootdir
) {
883 if(!root_dir_seems_valid(c
, d
)) {
884 de_warn(c
, "This file does not appear to contain a valid FAT "
885 "directory structure. (\"-opt fat:checkroot=0\" to try anyway)");
890 d
->curpath
= de_strarray_create(c
, MAX_NESTING_LEVEL
+10);
896 // Inform the parent module that we failed to do anything.
898 mparams
->out_params
.flags
|= 0x1;
903 de_free(c
, d
->fat_nextcluster
);
904 de_free(c
, d
->cluster_used_flags
);
905 if(d
->curpath
) de_strarray_destroy(d
->curpath
);
910 static int de_identify_fat(deark
*c
)
912 i64 bytes_per_sector
;
913 i64 max_root_dir_entries
;
914 i64 num_rsvd_sectors
;
918 u8 sectors_per_cluster
;
923 // TODO: This needs a lot of work.
924 // It's good enough for most FAT12 floppy disk images.
926 de_read(b
, 0, sizeof(b
));
927 bytes_per_sector
= de_getu16le_direct(&b
[11]);
928 sectors_per_cluster
= b
[13];
929 num_rsvd_sectors
= de_getu16le_direct(&b
[14]);
931 max_root_dir_entries
= de_getu16le_direct(&b
[17]);
934 if(bytes_per_sector
!=512) return 0;
935 switch(sectors_per_cluster
) {
936 case 1: case 2: case 4: case 8:
937 case 16: case 32: case 64: case 128:
942 if(num_fats
!=1 && num_fats
!=2) return 0;
943 if(media_descr
<0xe5 && media_descr
!=0) return 0; // Media descriptor
946 if(b
[0]==0xeb && b
[2]==0x90) confidence
+= 2;
947 else if(b
[0]==0xe9) confidence
+= 1;
948 else if(b
[0]==0x60) confidence
+= 1;
949 has_pc_sig
= (de_getu16be(510)==0x55aa);
950 if(has_pc_sig
) confidence
+= 2;
951 if(num_fats
==2) confidence
+= 1;
952 if(media_descr
>=0xe5) confidence
+= 1;
953 if(num_rsvd_sectors
==1) confidence
+= 1;
954 if(max_root_dir_entries
==112 || max_root_dir_entries
==224) confidence
+= 2;
956 has_ext
= de_input_file_has_ext(c
, "ima") ||
957 de_input_file_has_ext(c
, "img") ||
958 de_input_file_has_ext(c
, "st");
960 if(confidence
>=6) return (has_ext
?100:80);
961 else if(confidence
>=4) return (has_ext
?60:9);
965 static void de_help_fat(deark
*c
)
967 de_msg(c
, "-opt fat:checkroot=0 : Read the directory structure, even if it "
971 void de_module_fat(deark
*c
, struct deark_module_info
*mi
)
974 mi
->desc
= "FAT disk image";
975 mi
->run_fn
= de_run_fat
;
976 mi
->identify_fn
= de_identify_fat
;
977 mi
->help_fn
= de_help_fat
;
980 ///////////////////////// LoadDskF/SaveDskF format (OS/2-centric)
982 // We barely support this format, but if it's uncompressed, we'll try to skip
983 // past the header, and pretend it's FAT.
990 static void loaddskf_decode_as_fat(deark
*c
, struct skf_ctx
*d
)
992 i64 dlen
= c
->infile
->len
- d
->hdr_size
;
994 de_dbg(c
, "decoding as FAT, pos=%"I64_FMT
", len=%"I64_FMT
, d
->hdr_size
, dlen
);
995 if(dlen
<=0) goto done
;
998 de_run_module_by_id_on_slice(c
, "fat", NULL
, c
->infile
, d
->hdr_size
, dlen
);
999 de_dbg_indent(c
, -1);
1004 static int loaddskf_read_header(deark
*c
, struct skf_ctx
*d
)
1008 d
->hdr_size
= de_getu16le(38);
1009 de_dbg(c
, "header size: %"I64_FMT
, d
->hdr_size
);
1010 if((UI
)de_getu16be(d
->hdr_size
+ 510) != 0x55aa) {
1017 de_err(c
, "Failed to parse LoadDskF file");
1022 static void de_run_loaddskf(deark
*c
, de_module_params
*mparams
)
1024 struct skf_ctx
*d
= NULL
;
1027 d
= de_malloc(c
, sizeof(struct skf_ctx
));
1028 sig
= (UI
)de_getu16be(0);
1036 de_err(c
, "Compressed LoadDskF files are not supported");
1039 de_err(c
, "Not a LoadDskF file");
1043 if(!loaddskf_read_header(c
, d
)) goto done
;
1044 loaddskf_decode_as_fat(c
, d
);
1050 static int de_identify_loaddskf(deark
*c
)
1054 sig
= (UI
)de_getu16be(0);
1055 if(sig
==0xaa58 || sig
==0xaa59 || sig
==0xaa5a) {
1056 if((UI
)de_getu16be(2)==0xf000) {
1064 void de_module_loaddskf(deark
*c
, struct deark_module_info
*mi
)
1066 mi
->id
= "loaddskf";
1067 mi
->desc
= "LoadDskF/SaveDskF disk image";
1068 mi
->run_fn
= de_run_loaddskf
;
1069 mi
->identify_fn
= de_identify_loaddskf
;
1072 //---------------------- "EA DATA. SF"
1075 de_encoding input_encoding
;
1076 i64 bytes_per_cluster
;
1079 static int eadata_is_ea_sector_at_offset(deark
*c
, struct eadata_ctx
*d
, i64 pos
, int strictmode
)
1083 if((UI
)de_getu16be(pos
)!=0x4541) return 0;
1085 if((UI
)de_getu32be(pos
+4)!=0) return 0;
1086 b
= de_getbyte(pos
+8);
1088 if((UI
)de_getu32be(pos
+22)!=0) return 0;
1093 static void eadata_do_ea_data(deark
*c
, struct eadata_ctx
*d
, i64 pos1
, i64
*pbytes_consumed
)
1097 int saved_indent_level
;
1099 *pbytes_consumed
= 0;
1100 de_dbg_indent_save(c
, &saved_indent_level
);
1101 de_dbg(c
, "EA data at %"I64_FMT
, pos1
);
1102 de_dbg_indent(c
, 1);
1104 ealen
= de_getu16le_p(&pos
);
1105 *pbytes_consumed
= ealen
;
1106 de_dbg(c
, "data len: %"I64_FMT
, ealen
);
1108 de_dbg_hexdump(c
, c
->infile
, pos
, ealen
-2, 256, NULL
, 0x1);
1110 de_dbg_indent_restore(c
, saved_indent_level
);
1113 static void eadata_do_ea_sector_by_offset(deark
*c
, struct eadata_ctx
*d
, i64 pos1
,
1114 i64
*pbytes_consumed1
)
1118 i64 bytes_consumed2
= 0;
1119 de_ucstring
*fn
= NULL
;
1120 int saved_indent_level
;
1122 de_dbg_indent_save(c
, &saved_indent_level
);
1124 if(!eadata_is_ea_sector_at_offset(c
, d
, pos1
, 0)) {
1125 de_err(c
, "EA sector not found at %"I64_FMT
, pos1
);
1129 de_dbg(c
, "EA sector at %"I64_FMT
, pos1
);
1130 de_dbg_indent(c
, 1);
1132 n
= de_getu16le_p(&pos
);
1133 de_dbg(c
, "sector number (consistency check): %u", (UI
)n
);
1137 fn
= ucstring_create(c
);
1138 dbuf_read_to_ucstring(c
->infile
, pos
, 12, fn
, DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
1139 de_dbg(c
, "file name: \"%s\"", ucstring_getpsz_d(fn
));
1145 eadata_do_ea_data(c
, d
, pos
, &bytes_consumed2
);
1146 pos
+= bytes_consumed2
;
1148 if(pbytes_consumed1
) {
1149 *pbytes_consumed1
= pos
- pos1
;
1153 ucstring_destroy(fn
);
1154 de_dbg_indent_restore(c
, saved_indent_level
);
1157 static int eadata_id_to_offset(deark
*c
, struct eadata_ctx
*d
, UI id
, i64
*poffset
)
1168 if(a_idx
>=240) goto done
;
1169 a_val
= (UI
)de_getu16le(32+2*(i64
)a_idx
);
1170 b_val
= (UI
)de_getu16le(512+2*(i64
)id
);
1171 if(b_val
==0xffff) goto done
;
1173 cluster_num
= (i64
)b_val
+ (i64
)a_val
;
1174 *poffset
= d
->bytes_per_cluster
* cluster_num
;
1176 if(eadata_is_ea_sector_at_offset(c
, d
, *poffset
, 0)) {
1184 static void eadata_scan_file(deark
*c
, struct eadata_ctx
*d
)
1188 while(pos
< c
->infile
->len
) {
1189 if(eadata_is_ea_sector_at_offset(c
, d
, pos
, 1)) {
1192 eadata_do_ea_sector_by_offset(c
, d
, pos
, &bytes_consumed
);
1194 if(bytes_consumed
<1) bytes_consumed
= 1;
1195 pos
= de_pad_to_n(pos
+bytes_consumed
, 512);
1203 static void de_run_eadata(deark
*c
, de_module_params
*mparams
)
1210 de_declare_fmt(c
, "OS/2 extended attributes data");
1212 struct eadata_ctx
*d
= de_malloc(c
, sizeof(struct eadata_ctx
));
1213 s
= de_get_ext_option(c
, "ea_data:handle");
1215 ea_id
= (UI
)de_atoi(s
);
1217 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
1218 d
->bytes_per_cluster
= 512;
1221 eadata_scan_file(c
, d
);
1224 ret
= eadata_id_to_offset(c
, d
, ea_id
, &pos
);
1226 eadata_do_ea_sector_by_offset(c
, d
, pos
, NULL
);
1233 static int de_identify_eadata(deark
*c
)
1235 if(de_getu16be(0)!=0x4544) return 0;
1236 if(de_input_file_has_ext(c
, " sf")) return 100;
1237 if(dbuf_is_all_zeroes(c
->infile
, 2, 30)) {
1243 static void de_help_eadata(deark
*c
)
1245 de_msg(c
, "-opt ea_data:handle=<n> : Decode only EA handle/pointer <n>");
1248 void de_module_ea_data(deark
*c
, struct deark_module_info
*mi
)
1251 mi
->desc
= "EA DATA (OS/2 extended attributes)";
1252 mi
->run_fn
= de_run_eadata
;
1253 mi
->identify_fn
= de_identify_eadata
;
1254 mi
->help_fn
= de_help_eadata
;