1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // OS Extended Attributes, including "EA DATA. SF" files
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_ea_data
);
16 de_encoding input_encoding
;
17 UI createflags_for_icons
;
18 i64 bytes_per_cluster
;
19 de_ucstring
*base_filename_ref
; // May be NULL. Do not free.
20 struct easector_ctx
*cur_md
; // May be NULL. Do not free.
23 static int eadata_is_ea_sector_at_offset(deark
*c
, struct eadata_ctx
*d
, i64 pos
, int strictmode
)
27 if((UI
)de_getu16be(pos
)!=0x4541) return 0;
29 if((UI
)de_getu32be(pos
+4)!=0) return 0;
30 b
= de_getbyte(pos
+8);
32 if((UI
)de_getu32be(pos
+22)!=0) return 0;
37 static const char *eadata_get_data_type_name(UI t
)
39 const char *name
= NULL
;
41 case 0xffde: name
="multi-val/single-type"; break;
42 case 0xffdf: name
="multi-val/multi-type"; break;
43 case 0xfffe: name
="binary"; break;
44 case 0xfffd: name
="text"; break;
45 case 0xfff9: name
="icon"; break;
51 static void eadata_extract_icon(deark
*c
, struct eadata_ctx
*d
, i64 pos
, i64 len
)
55 fi
= de_finfo_create(c
);
57 if(d
->base_filename_ref
) {
58 de_finfo_set_name_from_ucstring(c
, fi
, d
->base_filename_ref
, 0);
60 else if(d
->cur_md
&& ucstring_isnonempty(d
->cur_md
->fn
)) {
61 de_finfo_set_name_from_ucstring(c
, fi
, d
->cur_md
->fn
, 0);
64 dbuf_create_file_from_slice(c
->infile
, pos
, len
, "os2.ico", fi
, d
->createflags_for_icons
);
65 de_finfo_destroy(c
, fi
);
68 static void eadata_do_text_attrib(deark
*c
, struct eadata_ctx
*d
, i64 pos
, i64 len
)
70 de_ucstring
*s
= NULL
;
72 s
= ucstring_create(c
);
73 // Documented as "ASCII text" -- but I wonder if the actual encoding might
74 // depend on the attribute name.
75 dbuf_read_to_ucstring_n(c
->infile
, pos
, len
, 2048, s
, 0, DE_ENCODING_ASCII
);
76 de_dbg(c
, "text: \"%s\"", ucstring_getpsz_d(s
));
80 static int eadata_do_attribute_lowlevel_singleval(deark
*c
, struct eadata_ctx
*d
,
81 UI attr_dtype
, i64 pos1
, i64 maxlen
, i64
*pbytes_consumed
)
87 attr_dlen
= de_getu16le(pos1
);
88 de_dbg(c
, "inner data len: %"I64_FMT
, attr_dlen
);
89 if(attr_dlen
<2 || attr_dlen
>maxlen
) goto done
;
90 *pbytes_consumed
= 2 + attr_dlen
;
96 eadata_extract_icon(c
, d
, dpos
, attr_dlen
);
99 eadata_do_text_attrib(c
, d
, dpos
, attr_dlen
);
102 de_dbg_hexdump(c
, c
->infile
, dpos
, attr_dlen
, 256, NULL
, 0x1);
109 static int eadata_do_attribute_lowlevel(deark
*c
, struct eadata_ctx
*d
,
110 UI attr_dtype
, i64 pos1
, i64 nbytes_avail
, i64
*pbytes_consumed
, int nesting_level
);
112 // multi-val, multi-type container attribute
113 static int eadata_do_MVMT(deark
*c
, struct eadata_ctx
*d
,
114 i64 pos1
, i64 nbytes_avail
, i64
*pbytes_consumed
, int nesting_level
)
122 int saved_indent_level
;
124 de_dbg_indent_save(c
, &saved_indent_level
);
125 codepage
= (UI
)de_getu16le_p(&pos
);
126 de_dbg(c
, "code page: %u", codepage
);
128 num_entries
= de_getu16le_p(&pos
);
129 de_dbg(c
, "num entries: %d", (int)num_entries
);
130 for(i
=0; i
<num_entries
; i
++) {
132 i64 bytes_consumed2
= 0;
134 if(pos
> pos1
+nbytes_avail
) goto done
;
135 de_dbg(c
, "entry %d at %"I64_FMT
, (int)i
, pos
);
137 attr_dtype
= (UI
)de_getu16le_p(&pos
);
138 de_dbg(c
, "data type: 0x%04x (%s)", attr_dtype
, eadata_get_data_type_name(attr_dtype
));
140 ret
= eadata_do_attribute_lowlevel(c
, d
, attr_dtype
, pos
, pos1
+nbytes_avail
-pos
,
141 &bytes_consumed2
, nesting_level
+1);
143 pos
+= bytes_consumed2
;
144 de_dbg_indent(c
, -1);
147 *pbytes_consumed
= pos
- pos1
;
150 de_dbg_indent_restore(c
, saved_indent_level
);
154 static int eadata_do_attribute_lowlevel(deark
*c
, struct eadata_ctx
*d
,
155 UI attr_dtype
, i64 pos1
, i64 nbytes_avail
, i64
*pbytes_consumed
, int nesting_level
)
159 *pbytes_consumed
= 0;
161 // I don't know if multi-val attributes are allowed to contain other multi-val attributes.
162 if(nesting_level
>5) goto done
;
166 if(!eadata_do_MVMT(c
, d
, pos1
, nbytes_avail
, pbytes_consumed
, nesting_level
)) goto done
;
168 case 0xffde: // MVST (TODO)
172 if(!eadata_do_attribute_lowlevel_singleval(c
, d
, attr_dtype
, pos1
, nbytes_avail
,
186 // FEA2 structure, starting at the 'fEA' field (1 byte before the name-length byte).
187 static int eadata_do_attribute(deark
*c
, struct eadata_ctx
*d
, i64 pos1
, i64 maxlen
,
188 de_ucstring
*tmps
, i64
*pbytes_consumed
)
199 namelen
= (i64
)de_getbyte_p(&pos
);
201 attr_dlen
= (i64
)de_getu16le_p(&pos
);
202 ucstring_empty(tmps
);
203 dbuf_read_to_ucstring(c
->infile
, pos
, namelen
, tmps
, 0, DE_ENCODING_ASCII
);
204 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(tmps
));
207 de_dbg(c
, "outer data len: %"I64_FMT
, attr_dlen
);
208 if(attr_dpos
+ attr_dlen
> pos1
+maxlen
) goto done
;
210 attr_dtype
= (UI
)de_getu16le_p(&pos
);
211 de_dbg(c
, "data type: 0x%04x (%s)", attr_dtype
, eadata_get_data_type_name(attr_dtype
));
214 eadata_do_attribute_lowlevel(c
, d
, attr_dtype
, attr_dpos
+2, attr_dlen
-2, &tmpbc
, 0);
216 pos
= attr_dpos
+ attr_dlen
;
217 *pbytes_consumed
= pos
- pos1
;
223 // Sets md->ea_data_len.
224 static void eadata_do_ea_data(deark
*c
, struct eadata_ctx
*d
, struct easector_ctx
*md
,
229 int saved_indent_level
;
230 de_ucstring
*s
= NULL
;
232 de_dbg_indent_save(c
, &saved_indent_level
);
233 de_dbg(c
, "EA data at %"I64_FMT
, pos1
);
236 md
->ea_data_len
= de_getu16le_p(&pos
); // TODO: Is this actually a 4-byte field?
237 de_dbg(c
, "data len: %"I64_FMT
, md
->ea_data_len
);
240 endpos
= pos1
+ md
->ea_data_len
;
241 s
= ucstring_create(c
);
244 while(pos
< endpos
-4) {
246 i64 bytes_consumed
= 0;
248 de_dbg(c
, "attribute at %"I64_FMT
, pos
);
250 ret
= eadata_do_attribute(c
, d
, pos
, endpos
-pos
, s
, &bytes_consumed
);
251 de_dbg_indent(c
, -1);
252 if(!ret
|| bytes_consumed
<1) goto done
;
253 pos
+= bytes_consumed
;
259 de_dbg_indent_restore(c
, saved_indent_level
);
262 static void eadata_do_raw_list(deark
*c
, struct eadata_ctx
*d
, i64 pos1
, i64 len
)
266 de_ucstring
*tmps
= NULL
;
267 int saved_indent_level
;
269 de_dbg_indent_save(c
, &saved_indent_level
);
270 tmps
= ucstring_create(c
);
276 i64 bytes_consumed
= 0;
277 i64 offset_to_next_attr
;
280 if(pos
>= endpos
) goto done
;
283 de_dbg(c
, "attribute at %"I64_FMT
, attr_pos
);
285 offset_to_next_attr
= de_getu32le_p(&pos
);
286 de_dbg(c
, "offset to next attr: %"I64_FMT
, offset_to_next_attr
);
288 ret
= eadata_do_attribute(c
, d
, pos
, endpos
-pos
, tmps
, &bytes_consumed
);
289 if(!ret
|| bytes_consumed
<1) goto done
;
290 if(offset_to_next_attr
==0) goto done
;
291 pos
= attr_pos
+ offset_to_next_attr
;
292 de_dbg_indent(c
, -1);
296 ucstring_destroy(tmps
);
297 de_dbg_indent_restore(c
, saved_indent_level
);
300 static void eadata_do_FEA2LIST(deark
*c
, struct eadata_ctx
*d
)
306 de_dbg(c
, "FEA2LIST at %"I64_FMT
, pos1
);
308 fea2list_len
= de_getu32le_p(&pos
);
309 de_dbg(c
, "list len: %"I64_FMT
, fea2list_len
);
310 eadata_do_raw_list(c
, d
, pos
, fea2list_len
-4);
311 de_dbg_indent(c
, -1);
314 static void eadata_do_ea_sector_by_offset(deark
*c
, struct eadata_ctx
*d
, i64 pos1
,
315 i64
*pbytes_consumed1
)
319 struct easector_ctx
*md
= NULL
;
320 int saved_indent_level
;
322 de_dbg_indent_save(c
, &saved_indent_level
);
323 if(pbytes_consumed1
) {
324 *pbytes_consumed1
= 0;
326 md
= de_malloc(c
, sizeof(struct easector_ctx
));
328 if(!eadata_is_ea_sector_at_offset(c
, d
, pos1
, 0)) {
329 de_err(c
, "EA sector not found at %"I64_FMT
, pos1
);
333 de_dbg(c
, "EA sector at %"I64_FMT
, pos1
);
336 n
= de_getu16le_p(&pos
);
337 de_dbg(c
, "sector number (consistency check): %u", (UI
)n
);
341 md
->fn
= ucstring_create(c
);
342 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->fn
, DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
343 de_dbg(c
, "file name: \"%s\"", ucstring_getpsz_d(md
->fn
));
349 eadata_do_ea_data(c
, d
, md
, pos
);
350 pos
+= md
->ea_data_len
;
352 if(pbytes_consumed1
) {
353 *pbytes_consumed1
= pos
- pos1
;
358 ucstring_destroy(md
->fn
);
361 de_dbg_indent_restore(c
, saved_indent_level
);
364 static int eadata_id_to_offset(deark
*c
, struct eadata_ctx
*d
, UI id
, i64
*poffset
)
375 if(a_idx
>=240) goto done
;
376 a_val
= (UI
)de_getu16le(32+2*(i64
)a_idx
);
377 b_val
= (UI
)de_getu16le(512+2*(i64
)id
);
378 if(b_val
==0xffff) goto done
;
380 cluster_num
= (i64
)b_val
+ (i64
)a_val
;
381 *poffset
= d
->bytes_per_cluster
* cluster_num
;
383 if(eadata_is_ea_sector_at_offset(c
, d
, *poffset
, 0)) {
391 static void eadata_scan_file(deark
*c
, struct eadata_ctx
*d
)
395 while(pos
< c
->infile
->len
) {
396 if(eadata_is_ea_sector_at_offset(c
, d
, pos
, 1)) {
399 eadata_do_ea_sector_by_offset(c
, d
, pos
, &bytes_consumed
);
401 if(bytes_consumed
<1) bytes_consumed
= 1;
402 pos
= de_pad_to_n(pos
+bytes_consumed
, 512);
410 static void de_run_eadata(deark
*c
, de_module_params
*mparams
)
416 struct eadata_ctx
*d
= NULL
;
418 de_declare_fmt(c
, "OS/2 extended attributes data");
420 d
= de_malloc(c
, sizeof(struct eadata_ctx
));
422 if(mparams
&& (mparams
->in_params
.flags
& 0x8)!=0) {
423 if(ucstring_isnonempty(mparams
->in_params
.str1
)) {
424 d
->base_filename_ref
= mparams
->in_params
.str1
;
428 if(de_havemodcode(c
, mparams
, 'L')) {
429 d
->createflags_for_icons
= DE_CREATEFLAG_IS_AUX
;
430 eadata_do_FEA2LIST(c
, d
);
432 else if(de_havemodcode(c
, mparams
, 'R')) {
433 d
->createflags_for_icons
= DE_CREATEFLAG_IS_AUX
;
434 eadata_do_raw_list(c
, d
, 0, c
->infile
->len
);
436 else if(mparams
&& (mparams
->in_params
.flags
& 0x1)) {
437 // We're being used by another module, to handle a specific ea_id.
438 ea_id
= (UI
)mparams
->in_params
.uint1
;
439 if(ea_id
==0) goto done
;
440 d
->createflags_for_icons
= DE_CREATEFLAG_IS_AUX
;
443 s
= de_get_ext_option(c
, "ea_data:handle");
445 ea_id
= (UI
)de_atoi(s
);
449 d
->input_encoding
= de_get_input_encoding(c
, mparams
, DE_ENCODING_CP437
);
450 d
->bytes_per_cluster
= 512;
453 eadata_scan_file(c
, d
);
456 ret
= eadata_id_to_offset(c
, d
, ea_id
, &pos
);
458 eadata_do_ea_sector_by_offset(c
, d
, pos
, NULL
);
465 static int de_identify_eadata(deark
*c
)
467 if(de_getu16be(0)!=0x4544) return 0;
468 if(de_input_file_has_ext(c
, " sf")) return 100;
469 if(dbuf_is_all_zeroes(c
->infile
, 2, 30)) {
475 static void de_help_eadata(deark
*c
)
477 de_msg(c
, "-opt ea_data:handle=<n> : Decode only EA handle/pointer <n>");
480 void de_module_ea_data(deark
*c
, struct deark_module_info
*mi
)
483 mi
->desc
= "EA DATA (OS/2 extended attributes)";
484 mi
->run_fn
= de_run_eadata
;
485 mi
->identify_fn
= de_identify_eadata
;
486 mi
->help_fn
= de_help_eadata
;