1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_dsstore
);
13 #define DSSTORE_MAX_DEPTH 16
15 #define CODE_blob 0x626c6f62U
16 #define CODE_bool 0x626f6f6cU
17 #define CODE_comp 0x636f6d70U
18 #define CODE_dutc 0x64757463U
19 #define CODE_long 0x6c6f6e67U
20 #define CODE_shor 0x73686f72U
21 #define CODE_type 0x74797065U
22 #define CODE_ustr 0x75737472U
27 de_ucstring
*filename
;
28 struct de_fourcc rtype
;
29 struct de_fourcc dtype
;
32 struct addr_table_entry
{
37 typedef struct localctx_struct
{
44 u32 root_node_block_id
;
47 struct addr_table_entry
*block_addr_table
; // 'blkcount' entries
51 static void do_dir_entry(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytes_consumed
)
56 struct de_stringreaderdata
*name_srd
= NULL
;
58 nlen
= (i64
)de_getbyte_p(&pos
);
59 *bytes_consumed
= 1 + nlen
+ 4;
60 name_srd
= dbuf_read_string(c
->infile
, pos
, nlen
, nlen
, 0, DE_ENCODING_MACROMAN
);
62 de_dbg(c
, "name: \"%s\"", ucstring_getpsz(name_srd
->str
));
63 blk_id
= (u32
)de_getu32be_p(&pos
);
64 de_dbg(c
, "block id: %u", (unsigned int)blk_id
);
66 if(!de_strcmp(name_srd
->sz
, "DSDB")) {
68 d
->dsdb_block_id
= blk_id
;
71 de_destroy_stringreaderdata(c
, name_srd
);
74 static void do_info_block(deark
*c
, lctx
*d
)
76 i64 pos
= d
->infoblk_offs
;
78 i64 blk_addr_array_start
;
79 i64 blk_addr_array_size_padded
;
81 int saved_indent_level
;
83 de_dbg_indent_save(c
, &saved_indent_level
);
84 de_dbg(c
, "info block at %"I64_FMT
, d
->infoblk_offs
);
86 d
->blkcount
= de_getu32be_p(&pos
);
87 de_dbg(c
, "block count: %u", (unsigned int)d
->blkcount
);
88 if(d
->blkcount
>1000000) goto done
;
91 blk_addr_array_start
= pos
;
92 blk_addr_array_size_padded
= de_pad_to_n(d
->blkcount
*4, 1024);
94 de_dbg(c
, "block address table at %d", (int)pos
);
95 d
->block_addr_table
= de_mallocarray(c
, d
->blkcount
, sizeof(struct addr_table_entry
));
97 for(k
=0; k
<d
->blkcount
; k
++) {
98 d
->block_addr_table
[k
].addr_code
= (u32
)de_getu32be_p(&pos
);
99 if(d
->block_addr_table
[k
].addr_code
!=0) {
100 de_dbg(c
, "addr[%d] = 0x%08x", (int)k
,
101 (unsigned int)d
->block_addr_table
[k
].addr_code
);
104 de_dbg_indent(c
, -1);
106 pos
= blk_addr_array_start
+ blk_addr_array_size_padded
;
107 dircount
= de_getu32be_p(&pos
);
108 de_dbg(c
, "dir count: %u", (unsigned int)dircount
);
109 if(dircount
>1000000) goto done
;
110 for(k
=0; k
<dircount
; k
++) {
113 de_dbg(c
, "dir entry[%d] at %"I64_FMT
, (int)k
, pos
);
115 do_dir_entry(c
, d
, pos
, &bytes_consumed
);
116 pos
+= bytes_consumed
;
117 de_dbg_indent(c
, -1);
121 de_dbg_indent_restore(c
, saved_indent_level
);
124 static int block_id_to_offset_and_size(deark
*c
, lctx
*d
, u32 blk_id
,
125 i64
*poffs
, i64
*psize
)
129 unsigned int size_indicator
;
131 if((i64
)blk_id
>=d
->blkcount
) {
135 addr_code
= d
->block_addr_table
[blk_id
].addr_code
;
136 size_indicator
= addr_code
&0x1f;
137 *psize
= de_pow2((i64
)size_indicator
);
138 *poffs
= HDRSIZE
+(i64
)(addr_code
-size_indicator
);
143 *poffs
= c
->infile
->len
;
149 static void do_blob(deark
*c
, lctx
*d
, struct record_info
*ri
)
154 len
= de_getu32be(ri
->dpos
);
155 de_dbg(c
, "blob len: %d", (int)len
);
157 blobpos
= ri
->dpos
+4;
159 if(len
>=8 && !dbuf_memcmp(c
->infile
, blobpos
, "bplist00", 8)) {
161 de_ucstring
*fn
= NULL
;
163 de_dbg(c
, "binary plist at %d", (int)blobpos
);
165 fn
= ucstring_create(c
);
166 if(c
->filenames_from_file
) {
167 ucstring_append_ucstring(fn
, ri
->filename
);
168 ucstring_append_sz(fn
, ".", DE_ENCODING_LATIN1
);
170 ucstring_printf(fn
, DE_ENCODING_LATIN1
, "%s.plist", ri
->rtype
.id_sanitized_sz
);
171 fi
= de_finfo_create(c
);
172 de_finfo_set_name_from_ucstring(c
, fi
, fn
, 0);
173 fmtutil_handle_plist(c
, c
->infile
, blobpos
, len
, fi
, 0);
174 ucstring_destroy(fn
);
175 de_finfo_destroy(c
, fi
);
176 de_dbg_indent(c
, -1);
179 de_dbg_hexdump(c
, c
->infile
, blobpos
, len
, 256, NULL
, 0x1);
183 static void do_ustr(deark
*c
, lctx
*d
, struct record_info
*ri
)
186 de_ucstring
*s
= NULL
;
188 len
= de_getu32be(ri
->dpos
);
190 s
= ucstring_create(c
);
191 dbuf_read_to_ucstring_n(c
->infile
, ri
->dpos
+4, len
*2, DE_DBG_MAX_STRLEN
*2, s
, 0,
192 DE_ENCODING_UTF16BE
);
193 de_dbg(c
, "value: \"%s\"", ucstring_getpsz_d(s
));
197 static void do_record_int(deark
*c
, lctx
*d
, struct record_info
*ri
,
202 val
= dbuf_getint_ext(c
->infile
, dpos
, (unsigned int)dlen
, 0, 0);
203 de_dbg(c
, "value: %"I64_FMT
, val
);
206 static void do_record_date(deark
*c
, lctx
*d
, struct record_info
*ri
)
210 struct de_timestamp ts
;
211 char timestamp_buf
[64];
213 val1
= dbuf_getu64be(c
->infile
, ri
->dpos
);
214 val2
= (i64
)(val1
>>16);
215 de_mac_time_to_timestamp(val2
, &ts
);
216 ts
.tzcode
= DE_TZCODE_UTC
;
217 de_timestamp_to_string(&ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
218 de_dbg(c
, "date: %"U64_FMT
" (%s)", val1
, timestamp_buf
);
221 // Returns 1 if we calculated the bytes_consumed.
222 static int do_record(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytes_consumed
)
226 struct record_info ri
;
229 de_zeromem(&ri
, sizeof(struct record_info
));
231 nlen
= de_getu32be_p(&pos
);
232 if(nlen
>2048) goto done
;
233 ri
.filename
= ucstring_create(c
);
234 dbuf_read_to_ucstring(c
->infile
, pos
, nlen
*2, ri
.filename
, 0, DE_ENCODING_UTF16BE
);
235 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(ri
.filename
));
238 dbuf_read_fourcc(c
->infile
, pos
, &ri
.rtype
, 4, 0x0);
239 de_dbg(c
, "record type: '%s'", ri
.rtype
.id_dbgstr
);
242 dbuf_read_fourcc(c
->infile
, pos
, &ri
.dtype
, 4, 0x0);
243 de_dbg(c
, "data type: '%s'", ri
.dtype
.id_dbgstr
);
248 switch(ri
.dtype
.id
) {
254 do_record_int(c
, d
, &ri
, ri
.dpos
, ri
.dlen
);
258 do_record_int(c
, d
, &ri
, ri
.dpos
, ri
.dlen
);
262 do_record_date(c
, d
, &ri
);
266 do_record_int(c
, d
, &ri
, ri
.dpos
, ri
.dlen
);
270 do_record_int(c
, d
, &ri
, ri
.dpos
+2, 2);
279 de_warn(c
, "Unknown data type '%s'. Remaining records in this node cannot "
280 "be processed.", ri
.dtype
.id_sanitized_sz
);
283 if(ri
.dlen
<1) goto done
;
288 *bytes_consumed
= pos
-pos1
;
289 ucstring_destroy(ri
.filename
);
293 static void do_one_node(deark
*c
, lctx
*d
, u32 blk_id
)
295 i64 node_offs
, node_size
;
300 int saved_indent_level
;
302 de_dbg_indent_save(c
, &saved_indent_level
);
305 if(d
->depth
> DSSTORE_MAX_DEPTH
) goto done
;
306 if(blk_id
>= d
->blkcount
) goto done
;
307 if(d
->block_addr_table
[blk_id
].decoded
) goto done
;
308 d
->block_addr_table
[blk_id
].decoded
= 1;
310 if(!block_id_to_offset_and_size(c
, d
, blk_id
, &node_offs
,
316 de_dbg(c
, "node: id=%u, offs=%d, len=%d", (unsigned int)blk_id
,
317 (int)node_offs
, (int)node_size
);
320 mode
= (unsigned int)de_getu32be_p(&pos
);
321 de_dbg(c
, "mode: %u", mode
);
322 count
= de_getu32be_p(&pos
);
323 de_dbg(c
, "count: %d", (int)count
);
326 // If 'mode' is 0, there are 'count' records here.
327 i64 bytes_consumed
= 0;
331 next_blk_id
= (u32
)de_getu32be_p(&pos
);
332 de_dbg(c
, "next block id: %u", (unsigned int)next_blk_id
);
333 do_one_node(c
, d
, next_blk_id
);
336 for(k
=0; k
<count
; k
++) {
337 de_dbg(c
, "record[%d] at %d (for node@%d)", (int)k
, (int)pos
, (int)node_offs
);
339 if(!do_record(c
, d
, pos
, &bytes_consumed
)) break;
340 de_dbg_indent(c
, -1);
341 pos
+= bytes_consumed
;
346 de_dbg_indent_restore(c
, saved_indent_level
);
350 static int do_dsdb(deark
*c
, lctx
*d
)
352 i64 dsdb_offs
, dsdb_size
;
357 if(!d
->found_dsdb
) goto done
;
358 if(!block_id_to_offset_and_size(c
, d
, d
->dsdb_block_id
, &dsdb_offs
,
364 de_dbg(c
, "DSDB block: id=%u, offs=%d, len=%d", (unsigned int)d
->dsdb_block_id
,
365 (int)dsdb_offs
, (int)dsdb_size
);
369 d
->root_node_block_id
= (u32
)de_getu32be_p(&pos
);
370 de_dbg(c
, "root node block id: %u", (unsigned int)d
->root_node_block_id
);
372 n
= de_getu32be_p(&pos
);
373 de_dbg(c
, "num levels: %d", (int)n
);
374 n
= de_getu32be_p(&pos
);
375 de_dbg(c
, "num records in tree: %d", (int)n
);
376 n
= de_getu32be_p(&pos
);
377 de_dbg(c
, "num blocks in tree: %d", (int)n
);
378 de_dbg_indent(c
, -1);
385 static void de_run_dsstore(deark
*c
, de_module_params
*mparams
)
390 d
= de_malloc(c
, sizeof(lctx
));
393 d
->infoblk_offs
= de_getu32be_p(&pos
);
394 de_dbg(c
, "info block offset: (%d+)%"I64_FMT
, HDRSIZE
, d
->infoblk_offs
);
395 d
->infoblk_offs
+= HDRSIZE
;
396 d
->infoblk_size
= de_getu32be_p(&pos
);
397 de_dbg(c
, "info block size: %"I64_FMT
, d
->infoblk_size
);
402 de_err(c
, "DSDB block not found. This file is probably corrupted, or "
403 "an unsupported version.");
406 if(!do_dsdb(c
, d
)) goto done
;
408 do_one_node(c
, d
, d
->root_node_block_id
);
412 de_free(c
, d
->block_addr_table
);
417 static int de_identify_dsstore(deark
*c
)
419 if(!dbuf_memcmp(c
->infile
, 0, "\x00\x00\x00\x01" "Bud1", 8))
424 static void de_help_dsstore(deark
*c
)
426 de_msg(c
, "-opt extractplist : Write plist records to files");
429 void de_module_dsstore(deark
*c
, struct deark_module_info
*mi
)
432 mi
->desc
= "Mac Finder .DS_Store format";
433 mi
->run_fn
= de_run_dsstore
;
434 mi
->identify_fn
= de_identify_dsstore
;
435 mi
->help_fn
= de_help_dsstore
;