cdf.c revision 309848
1/*-
2 * Copyright (c) 2008 Christos Zoulas
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26/*
27 * Parse Composite Document Files, the format used in Microsoft Office
28 * document files before they switched to zipped XML.
29 * Info from: http://sc.openoffice.org/compdocfileformat.pdf
30 *
31 * N.B. This is the "Composite Document File" format, and not the
32 * "Compound Document Format", nor the "Channel Definition Format".
33 */
34
35#include "file.h"
36
37#ifndef lint
38FILE_RCSID("@(#)$File: cdf.c,v 1.85 2016/10/24 18:02:17 christos Exp $")
39#endif
40
41#include <assert.h>
42#ifdef CDF_DEBUG
43#include <err.h>
44#endif
45#include <stdlib.h>
46#include <unistd.h>
47#include <string.h>
48#include <time.h>
49#include <ctype.h>
50#ifdef HAVE_LIMITS_H
51#include <limits.h>
52#endif
53
54#ifndef EFTYPE
55#define EFTYPE EINVAL
56#endif
57
58#include "cdf.h"
59
60#ifdef CDF_DEBUG
61#define DPRINTF(a) printf a, fflush(stdout)
62#else
63#define DPRINTF(a)
64#endif
65
66static union {
67	char s[4];
68	uint32_t u;
69} cdf_bo;
70
71#define NEED_SWAP	(cdf_bo.u == (uint32_t)0x01020304)
72
73#define CDF_TOLE8(x)	((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x)))
74#define CDF_TOLE4(x)	((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x)))
75#define CDF_TOLE2(x)	((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x)))
76#define CDF_TOLE(x)	(/*CONSTCOND*/sizeof(x) == 2 ? \
77			    CDF_TOLE2(CAST(uint16_t, x)) : \
78			(/*CONSTCOND*/sizeof(x) == 4 ? \
79			    CDF_TOLE4(CAST(uint32_t, x)) : \
80			    CDF_TOLE8(CAST(uint64_t, x))))
81#define CDF_GETUINT32(x, y)	cdf_getuint32(x, y)
82
83
84/*
85 * swap a short
86 */
87static uint16_t
88_cdf_tole2(uint16_t sv)
89{
90	uint16_t rv;
91	uint8_t *s = (uint8_t *)(void *)&sv;
92	uint8_t *d = (uint8_t *)(void *)&rv;
93	d[0] = s[1];
94	d[1] = s[0];
95	return rv;
96}
97
98/*
99 * swap an int
100 */
101static uint32_t
102_cdf_tole4(uint32_t sv)
103{
104	uint32_t rv;
105	uint8_t *s = (uint8_t *)(void *)&sv;
106	uint8_t *d = (uint8_t *)(void *)&rv;
107	d[0] = s[3];
108	d[1] = s[2];
109	d[2] = s[1];
110	d[3] = s[0];
111	return rv;
112}
113
114/*
115 * swap a quad
116 */
117static uint64_t
118_cdf_tole8(uint64_t sv)
119{
120	uint64_t rv;
121	uint8_t *s = (uint8_t *)(void *)&sv;
122	uint8_t *d = (uint8_t *)(void *)&rv;
123	d[0] = s[7];
124	d[1] = s[6];
125	d[2] = s[5];
126	d[3] = s[4];
127	d[4] = s[3];
128	d[5] = s[2];
129	d[6] = s[1];
130	d[7] = s[0];
131	return rv;
132}
133
134/*
135 * grab a uint32_t from a possibly unaligned address, and return it in
136 * the native host order.
137 */
138static uint32_t
139cdf_getuint32(const uint8_t *p, size_t offs)
140{
141	uint32_t rv;
142	(void)memcpy(&rv, p + offs * sizeof(uint32_t), sizeof(rv));
143	return CDF_TOLE4(rv);
144}
145
146#define CDF_UNPACK(a)	\
147    (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a)
148#define CDF_UNPACKA(a)	\
149    (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a)
150
151uint16_t
152cdf_tole2(uint16_t sv)
153{
154	return CDF_TOLE2(sv);
155}
156
157uint32_t
158cdf_tole4(uint32_t sv)
159{
160	return CDF_TOLE4(sv);
161}
162
163uint64_t
164cdf_tole8(uint64_t sv)
165{
166	return CDF_TOLE8(sv);
167}
168
169void
170cdf_swap_header(cdf_header_t *h)
171{
172	size_t i;
173
174	h->h_magic = CDF_TOLE8(h->h_magic);
175	h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]);
176	h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]);
177	h->h_revision = CDF_TOLE2(h->h_revision);
178	h->h_version = CDF_TOLE2(h->h_version);
179	h->h_byte_order = CDF_TOLE2(h->h_byte_order);
180	h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2);
181	h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2);
182	h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat);
183	h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory);
184	h->h_min_size_standard_stream =
185	    CDF_TOLE4(h->h_min_size_standard_stream);
186	h->h_secid_first_sector_in_short_sat =
187	    CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_short_sat);
188	h->h_num_sectors_in_short_sat =
189	    CDF_TOLE4(h->h_num_sectors_in_short_sat);
190	h->h_secid_first_sector_in_master_sat =
191	    CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_master_sat);
192	h->h_num_sectors_in_master_sat =
193	    CDF_TOLE4(h->h_num_sectors_in_master_sat);
194	for (i = 0; i < __arraycount(h->h_master_sat); i++)
195		h->h_master_sat[i] = CDF_TOLE4((uint32_t)h->h_master_sat[i]);
196}
197
198void
199cdf_unpack_header(cdf_header_t *h, char *buf)
200{
201	size_t i;
202	size_t len = 0;
203
204	CDF_UNPACK(h->h_magic);
205	CDF_UNPACKA(h->h_uuid);
206	CDF_UNPACK(h->h_revision);
207	CDF_UNPACK(h->h_version);
208	CDF_UNPACK(h->h_byte_order);
209	CDF_UNPACK(h->h_sec_size_p2);
210	CDF_UNPACK(h->h_short_sec_size_p2);
211	CDF_UNPACKA(h->h_unused0);
212	CDF_UNPACK(h->h_num_sectors_in_sat);
213	CDF_UNPACK(h->h_secid_first_directory);
214	CDF_UNPACKA(h->h_unused1);
215	CDF_UNPACK(h->h_min_size_standard_stream);
216	CDF_UNPACK(h->h_secid_first_sector_in_short_sat);
217	CDF_UNPACK(h->h_num_sectors_in_short_sat);
218	CDF_UNPACK(h->h_secid_first_sector_in_master_sat);
219	CDF_UNPACK(h->h_num_sectors_in_master_sat);
220	for (i = 0; i < __arraycount(h->h_master_sat); i++)
221		CDF_UNPACK(h->h_master_sat[i]);
222}
223
224void
225cdf_swap_dir(cdf_directory_t *d)
226{
227	d->d_namelen = CDF_TOLE2(d->d_namelen);
228	d->d_left_child = CDF_TOLE4((uint32_t)d->d_left_child);
229	d->d_right_child = CDF_TOLE4((uint32_t)d->d_right_child);
230	d->d_storage = CDF_TOLE4((uint32_t)d->d_storage);
231	d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]);
232	d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]);
233	d->d_flags = CDF_TOLE4(d->d_flags);
234	d->d_created = CDF_TOLE8((uint64_t)d->d_created);
235	d->d_modified = CDF_TOLE8((uint64_t)d->d_modified);
236	d->d_stream_first_sector = CDF_TOLE4((uint32_t)d->d_stream_first_sector);
237	d->d_size = CDF_TOLE4(d->d_size);
238}
239
240void
241cdf_swap_class(cdf_classid_t *d)
242{
243	d->cl_dword = CDF_TOLE4(d->cl_dword);
244	d->cl_word[0] = CDF_TOLE2(d->cl_word[0]);
245	d->cl_word[1] = CDF_TOLE2(d->cl_word[1]);
246}
247
248void
249cdf_unpack_dir(cdf_directory_t *d, char *buf)
250{
251	size_t len = 0;
252
253	CDF_UNPACKA(d->d_name);
254	CDF_UNPACK(d->d_namelen);
255	CDF_UNPACK(d->d_type);
256	CDF_UNPACK(d->d_color);
257	CDF_UNPACK(d->d_left_child);
258	CDF_UNPACK(d->d_right_child);
259	CDF_UNPACK(d->d_storage);
260	CDF_UNPACKA(d->d_storage_uuid);
261	CDF_UNPACK(d->d_flags);
262	CDF_UNPACK(d->d_created);
263	CDF_UNPACK(d->d_modified);
264	CDF_UNPACK(d->d_stream_first_sector);
265	CDF_UNPACK(d->d_size);
266	CDF_UNPACK(d->d_unused0);
267}
268
269int
270cdf_zero_stream(cdf_stream_t *scn)
271{
272	scn->sst_len = 0;
273	scn->sst_dirlen = 0;
274	scn->sst_ss = 0;
275	free(scn->sst_tab);
276	scn->sst_tab = NULL;
277	return -1;
278}
279
280static size_t
281cdf_check_stream(const cdf_stream_t *sst, const cdf_header_t *h)
282{
283	size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
284	    CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
285	assert(ss == sst->sst_ss);
286	return sst->sst_ss;
287}
288
289static int
290cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h,
291    const void *p, size_t tail, int line)
292{
293	const char *b = (const char *)sst->sst_tab;
294	const char *e = ((const char *)p) + tail;
295	size_t ss = cdf_check_stream(sst, h);
296	/*LINTED*/(void)&line;
297	if (e >= b && (size_t)(e - b) <= ss * sst->sst_len)
298		return 0;
299	DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u"
300	    " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %"
301	    SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b),
302	    ss * sst->sst_len, ss, sst->sst_len));
303	errno = EFTYPE;
304	return -1;
305}
306
307static ssize_t
308cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len)
309{
310	size_t siz = (size_t)off + len;
311
312	if ((off_t)(off + len) != (off_t)siz)
313		goto out;
314
315	if (info->i_buf != NULL && info->i_len >= siz) {
316		(void)memcpy(buf, &info->i_buf[off], len);
317		return (ssize_t)len;
318	}
319
320	if (info->i_fd == -1)
321		goto out;
322
323	if (pread(info->i_fd, buf, len, off) != (ssize_t)len)
324		return -1;
325
326	return (ssize_t)len;
327out:
328	errno = EINVAL;
329	return -1;
330}
331
332int
333cdf_read_header(const cdf_info_t *info, cdf_header_t *h)
334{
335	char buf[512];
336
337	(void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
338	if (cdf_read(info, (off_t)0, buf, sizeof(buf)) == -1)
339		return -1;
340	cdf_unpack_header(h, buf);
341	cdf_swap_header(h);
342	if (h->h_magic != CDF_MAGIC) {
343		DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%"
344		    INT64_T_FORMAT "x\n",
345		    (unsigned long long)h->h_magic,
346		    (unsigned long long)CDF_MAGIC));
347		goto out;
348	}
349	if (h->h_sec_size_p2 > 20) {
350		DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2));
351		goto out;
352	}
353	if (h->h_short_sec_size_p2 > 20) {
354		DPRINTF(("Bad short sector size 0x%u\n",
355		    h->h_short_sec_size_p2));
356		goto out;
357	}
358	return 0;
359out:
360	errno = EFTYPE;
361	return -1;
362}
363
364
365ssize_t
366cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len,
367    const cdf_header_t *h, cdf_secid_t id)
368{
369	size_t ss = CDF_SEC_SIZE(h);
370	size_t pos = CDF_SEC_POS(h, id);
371	assert(ss == len);
372	return cdf_read(info, (off_t)pos, ((char *)buf) + offs, len);
373}
374
375ssize_t
376cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs,
377    size_t len, const cdf_header_t *h, cdf_secid_t id)
378{
379	size_t ss = CDF_SHORT_SEC_SIZE(h);
380	size_t pos = CDF_SHORT_SEC_POS(h, id);
381	assert(ss == len);
382	if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) {
383		DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %"
384		    SIZE_T_FORMAT "u\n",
385		    pos + len, CDF_SEC_SIZE(h) * sst->sst_len));
386		goto out;
387	}
388	(void)memcpy(((char *)buf) + offs,
389	    ((const char *)sst->sst_tab) + pos, len);
390	return len;
391out:
392	errno = EFTYPE;
393	return -1;
394}
395
396/*
397 * Read the sector allocation table.
398 */
399int
400cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat)
401{
402	size_t i, j, k;
403	size_t ss = CDF_SEC_SIZE(h);
404	cdf_secid_t *msa, mid, sec;
405	size_t nsatpersec = (ss / sizeof(mid)) - 1;
406
407	for (i = 0; i < __arraycount(h->h_master_sat); i++)
408		if (h->h_master_sat[i] == CDF_SECID_FREE)
409			break;
410
411#define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss))
412	if ((nsatpersec > 0 &&
413	    h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) ||
414	    i > CDF_SEC_LIMIT) {
415		DPRINTF(("Number of sectors in master SAT too big %u %"
416		    SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i));
417		errno = EFTYPE;
418		return -1;
419	}
420
421	sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i;
422	DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n",
423	    sat->sat_len, ss));
424	if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss)))
425	    == NULL)
426		return -1;
427
428	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
429		if (h->h_master_sat[i] < 0)
430			break;
431		if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
432		    h->h_master_sat[i]) != (ssize_t)ss) {
433			DPRINTF(("Reading sector %d", h->h_master_sat[i]));
434			goto out1;
435		}
436	}
437
438	if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL)
439		goto out1;
440
441	mid = h->h_secid_first_sector_in_master_sat;
442	for (j = 0; j < h->h_num_sectors_in_master_sat; j++) {
443		if (mid < 0)
444			goto out;
445		if (j >= CDF_LOOP_LIMIT) {
446			DPRINTF(("Reading master sector loop limit"));
447			goto out3;
448		}
449		if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) {
450			DPRINTF(("Reading master sector %d", mid));
451			goto out2;
452		}
453		for (k = 0; k < nsatpersec; k++, i++) {
454			sec = CDF_TOLE4((uint32_t)msa[k]);
455			if (sec < 0)
456				goto out;
457			if (i >= sat->sat_len) {
458			    DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT
459				"u >= %" SIZE_T_FORMAT "u", i, sat->sat_len));
460			    goto out3;
461			}
462			if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
463			    sec) != (ssize_t)ss) {
464				DPRINTF(("Reading sector %d",
465				    CDF_TOLE4(msa[k])));
466				goto out2;
467			}
468		}
469		mid = CDF_TOLE4((uint32_t)msa[nsatpersec]);
470	}
471out:
472	sat->sat_len = i;
473	free(msa);
474	return 0;
475out3:
476	errno = EFTYPE;
477out2:
478	free(msa);
479out1:
480	free(sat->sat_tab);
481	return -1;
482}
483
484size_t
485cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size)
486{
487	size_t i, j;
488	cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size)
489	    / sizeof(maxsector));
490
491	DPRINTF(("Chain:"));
492	if (sid == CDF_SECID_END_OF_CHAIN) {
493		/* 0-length chain. */
494		DPRINTF((" empty\n"));
495		return 0;
496	}
497
498	for (j = i = 0; sid >= 0; i++, j++) {
499		DPRINTF((" %d", sid));
500		if (j >= CDF_LOOP_LIMIT) {
501			DPRINTF(("Counting chain loop limit"));
502			goto out;
503		}
504		if (sid >= maxsector) {
505			DPRINTF(("Sector %d >= %d\n", sid, maxsector));
506			goto out;
507		}
508		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
509	}
510	if (i == 0) {
511		DPRINTF((" none, sid: %d\n", sid));
512		goto out;
513
514	}
515	DPRINTF(("\n"));
516	return i;
517out:
518	errno = EFTYPE;
519	return (size_t)-1;
520}
521
522int
523cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
524    const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn)
525{
526	size_t ss = CDF_SEC_SIZE(h), i, j;
527	ssize_t nr;
528	scn->sst_tab = NULL;
529	scn->sst_len = cdf_count_chain(sat, sid, ss);
530	scn->sst_dirlen = len;
531	scn->sst_ss = ss;
532
533	if (scn->sst_len == (size_t)-1)
534		goto out;
535
536	scn->sst_tab = calloc(scn->sst_len, ss);
537	if (scn->sst_tab == NULL)
538		return cdf_zero_stream(scn);
539
540	for (j = i = 0; sid >= 0; i++, j++) {
541		if (j >= CDF_LOOP_LIMIT) {
542			DPRINTF(("Read long sector chain loop limit"));
543			goto out;
544		}
545		if (i >= scn->sst_len) {
546			DPRINTF(("Out of bounds reading long sector chain "
547			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
548			    scn->sst_len));
549			goto out;
550		}
551		if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h,
552		    sid)) != (ssize_t)ss) {
553			if (i == scn->sst_len - 1 && nr > 0) {
554				/* Last sector might be truncated */
555				return 0;
556			}
557			DPRINTF(("Reading long sector chain %d", sid));
558			goto out;
559		}
560		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
561	}
562	return 0;
563out:
564	errno = EFTYPE;
565	return cdf_zero_stream(scn);
566}
567
568int
569cdf_read_short_sector_chain(const cdf_header_t *h,
570    const cdf_sat_t *ssat, const cdf_stream_t *sst,
571    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
572{
573	size_t ss = CDF_SHORT_SEC_SIZE(h), i, j;
574	scn->sst_tab = NULL;
575	scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h));
576	scn->sst_dirlen = len;
577	scn->sst_ss = ss;
578
579	if (scn->sst_len == (size_t)-1)
580		goto out;
581
582	scn->sst_tab = calloc(scn->sst_len, ss);
583	if (scn->sst_tab == NULL)
584		return cdf_zero_stream(scn);
585
586	for (j = i = 0; sid >= 0; i++, j++) {
587		if (j >= CDF_LOOP_LIMIT) {
588			DPRINTF(("Read short sector chain loop limit"));
589			goto out;
590		}
591		if (i >= scn->sst_len) {
592			DPRINTF(("Out of bounds reading short sector chain "
593			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n",
594			    i, scn->sst_len));
595			goto out;
596		}
597		if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h,
598		    sid) != (ssize_t)ss) {
599			DPRINTF(("Reading short sector chain %d", sid));
600			goto out;
601		}
602		sid = CDF_TOLE4((uint32_t)ssat->sat_tab[sid]);
603	}
604	return 0;
605out:
606	errno = EFTYPE;
607	return cdf_zero_stream(scn);
608}
609
610int
611cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
612    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
613    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
614{
615
616	if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL)
617		return cdf_read_short_sector_chain(h, ssat, sst, sid, len,
618		    scn);
619	else
620		return cdf_read_long_sector_chain(info, h, sat, sid, len, scn);
621}
622
623int
624cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h,
625    const cdf_sat_t *sat, cdf_dir_t *dir)
626{
627	size_t i, j;
628	size_t ss = CDF_SEC_SIZE(h), ns, nd;
629	char *buf;
630	cdf_secid_t sid = h->h_secid_first_directory;
631
632	ns = cdf_count_chain(sat, sid, ss);
633	if (ns == (size_t)-1)
634		return -1;
635
636	nd = ss / CDF_DIRECTORY_SIZE;
637
638	dir->dir_len = ns * nd;
639	dir->dir_tab = CAST(cdf_directory_t *,
640	    calloc(dir->dir_len, sizeof(dir->dir_tab[0])));
641	if (dir->dir_tab == NULL)
642		return -1;
643
644	if ((buf = CAST(char *, malloc(ss))) == NULL) {
645		free(dir->dir_tab);
646		return -1;
647	}
648
649	for (j = i = 0; i < ns; i++, j++) {
650		if (j >= CDF_LOOP_LIMIT) {
651			DPRINTF(("Read dir loop limit"));
652			goto out;
653		}
654		if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) {
655			DPRINTF(("Reading directory sector %d", sid));
656			goto out;
657		}
658		for (j = 0; j < nd; j++) {
659			cdf_unpack_dir(&dir->dir_tab[i * nd + j],
660			    &buf[j * CDF_DIRECTORY_SIZE]);
661		}
662		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
663	}
664	if (NEED_SWAP)
665		for (i = 0; i < dir->dir_len; i++)
666			cdf_swap_dir(&dir->dir_tab[i]);
667	free(buf);
668	return 0;
669out:
670	free(dir->dir_tab);
671	free(buf);
672	errno = EFTYPE;
673	return -1;
674}
675
676
677int
678cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h,
679    const cdf_sat_t *sat, cdf_sat_t *ssat)
680{
681	size_t i, j;
682	size_t ss = CDF_SEC_SIZE(h);
683	cdf_secid_t sid = h->h_secid_first_sector_in_short_sat;
684
685	ssat->sat_tab = NULL;
686	ssat->sat_len = cdf_count_chain(sat, sid, ss);
687	if (ssat->sat_len == (size_t)-1)
688		goto out;
689
690	ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss));
691	if (ssat->sat_tab == NULL)
692		goto out1;
693
694	for (j = i = 0; sid >= 0; i++, j++) {
695		if (j >= CDF_LOOP_LIMIT) {
696			DPRINTF(("Read short sat sector loop limit"));
697			goto out;
698		}
699		if (i >= ssat->sat_len) {
700			DPRINTF(("Out of bounds reading short sector chain "
701			    "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
702			    ssat->sat_len));
703			goto out;
704		}
705		if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) !=
706		    (ssize_t)ss) {
707			DPRINTF(("Reading short sat sector %d", sid));
708			goto out1;
709		}
710		sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
711	}
712	return 0;
713out:
714	errno = EFTYPE;
715out1:
716	free(ssat->sat_tab);
717	return -1;
718}
719
720int
721cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h,
722    const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn,
723    const cdf_directory_t **root)
724{
725	size_t i;
726	const cdf_directory_t *d;
727
728	*root = NULL;
729	for (i = 0; i < dir->dir_len; i++)
730		if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE)
731			break;
732
733	/* If the it is not there, just fake it; some docs don't have it */
734	if (i == dir->dir_len) {
735		DPRINTF(("Cannot find root storage dir\n"));
736		goto out;
737	}
738	d = &dir->dir_tab[i];
739	*root = d;
740
741	/* If the it is not there, just fake it; some docs don't have it */
742	if (d->d_stream_first_sector < 0) {
743		DPRINTF(("No first secror in dir\n"));
744		goto out;
745	}
746
747	return cdf_read_long_sector_chain(info, h, sat,
748	    d->d_stream_first_sector, d->d_size, scn);
749out:
750	scn->sst_tab = NULL;
751	(void)cdf_zero_stream(scn);
752	return 0;
753}
754
755static int
756cdf_namecmp(const char *d, const uint16_t *s, size_t l)
757{
758	for (; l--; d++, s++)
759		if (*d != CDF_TOLE2(*s))
760			return (unsigned char)*d - CDF_TOLE2(*s);
761	return 0;
762}
763
764int
765cdf_read_doc_summary_info(const cdf_info_t *info, const cdf_header_t *h,
766    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
767    const cdf_dir_t *dir, cdf_stream_t *scn)
768{
769	return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
770	    "\05DocumentSummaryInformation", scn);
771}
772
773int
774cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h,
775    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
776    const cdf_dir_t *dir, cdf_stream_t *scn)
777{
778	return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
779	    "\05SummaryInformation", scn);
780}
781
782int
783cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h,
784    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
785    const cdf_dir_t *dir, const char *name, cdf_stream_t *scn)
786{
787	const cdf_directory_t *d;
788	int i = cdf_find_stream(dir, name, CDF_DIR_TYPE_USER_STREAM);
789
790	if (i <= 0) {
791		memset(scn, 0, sizeof(*scn));
792		return -1;
793	}
794
795	d = &dir->dir_tab[i - 1];
796	return cdf_read_sector_chain(info, h, sat, ssat, sst,
797	    d->d_stream_first_sector, d->d_size, scn);
798}
799
800int
801cdf_find_stream(const cdf_dir_t *dir, const char *name, int type)
802{
803	size_t i, name_len = strlen(name) + 1;
804
805	for (i = dir->dir_len; i > 0; i--)
806		if (dir->dir_tab[i - 1].d_type == type &&
807		    cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len)
808		    == 0)
809			break;
810	if (i > 0)
811		return i;
812
813	DPRINTF(("Cannot find type %d `%s'\n", type, name));
814	errno = ESRCH;
815	return 0;
816}
817
818int
819cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h,
820    uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount)
821{
822	const cdf_section_header_t *shp;
823	cdf_section_header_t sh;
824	const uint8_t *p, *q, *e;
825	int16_t s16;
826	int32_t s32;
827	uint32_t u32;
828	int64_t s64;
829	uint64_t u64;
830	cdf_timestamp_t tp;
831	size_t i, o, o4, nelements, j;
832	cdf_property_info_t *inp;
833
834	if (offs > UINT32_MAX / 4) {
835		errno = EFTYPE;
836		goto out;
837	}
838	shp = CAST(const cdf_section_header_t *, (const void *)
839	    ((const char *)sst->sst_tab + offs));
840	if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1)
841		goto out;
842	sh.sh_len = CDF_TOLE4(shp->sh_len);
843#define CDF_SHLEN_LIMIT (UINT32_MAX / 8)
844	if (sh.sh_len > CDF_SHLEN_LIMIT) {
845		errno = EFTYPE;
846		goto out;
847	}
848	sh.sh_properties = CDF_TOLE4(shp->sh_properties);
849#define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp)))
850	if (sh.sh_properties > CDF_PROP_LIMIT)
851		goto out;
852	DPRINTF(("section len: %u properties %u\n", sh.sh_len,
853	    sh.sh_properties));
854	if (*maxcount) {
855		if (*maxcount > CDF_PROP_LIMIT)
856			goto out;
857		*maxcount += sh.sh_properties;
858		inp = CAST(cdf_property_info_t *,
859		    realloc(*info, *maxcount * sizeof(*inp)));
860	} else {
861		*maxcount = sh.sh_properties;
862		inp = CAST(cdf_property_info_t *,
863		    malloc(*maxcount * sizeof(*inp)));
864	}
865	if (inp == NULL)
866		goto out1;
867	*info = inp;
868	inp += *count;
869	*count += sh.sh_properties;
870	p = CAST(const uint8_t *, (const void *)
871	    ((const char *)(const void *)sst->sst_tab +
872	    offs + sizeof(sh)));
873	e = CAST(const uint8_t *, (const void *)
874	    (((const char *)(const void *)shp) + sh.sh_len));
875	if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1)
876		goto out;
877	for (i = 0; i < sh.sh_properties; i++) {
878		size_t tail = (i << 1) + 1;
879		size_t ofs;
880		if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t),
881		    __LINE__) == -1)
882			goto out;
883		ofs = CDF_GETUINT32(p, tail);
884		q = (const uint8_t *)(const void *)
885		    ((const char *)(const void *)p + ofs
886		    - 2 * sizeof(uint32_t));
887		if (q < p) {
888			DPRINTF(("Wrapped around %p < %p\n", q, p));
889			goto out;
890		}
891		if (q > e) {
892			DPRINTF(("Ran of the end %p > %p\n", q, e));
893			goto out;
894		}
895		inp[i].pi_id = CDF_GETUINT32(p, i << 1);
896		inp[i].pi_type = CDF_GETUINT32(q, 0);
897		DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n",
898		    i, inp[i].pi_id, inp[i].pi_type, q - p, offs));
899		if (inp[i].pi_type & CDF_VECTOR) {
900			nelements = CDF_GETUINT32(q, 1);
901			if (nelements == 0) {
902				DPRINTF(("CDF_VECTOR with nelements == 0\n"));
903				goto out;
904			}
905			o = 2;
906		} else {
907			nelements = 1;
908			o = 1;
909		}
910		o4 = o * sizeof(uint32_t);
911		if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED))
912			goto unknown;
913		switch (inp[i].pi_type & CDF_TYPEMASK) {
914		case CDF_NULL:
915		case CDF_EMPTY:
916			break;
917		case CDF_SIGNED16:
918			if (inp[i].pi_type & CDF_VECTOR)
919				goto unknown;
920			(void)memcpy(&s16, &q[o4], sizeof(s16));
921			inp[i].pi_s16 = CDF_TOLE2(s16);
922			break;
923		case CDF_SIGNED32:
924			if (inp[i].pi_type & CDF_VECTOR)
925				goto unknown;
926			(void)memcpy(&s32, &q[o4], sizeof(s32));
927			inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32);
928			break;
929		case CDF_BOOL:
930		case CDF_UNSIGNED32:
931			if (inp[i].pi_type & CDF_VECTOR)
932				goto unknown;
933			(void)memcpy(&u32, &q[o4], sizeof(u32));
934			inp[i].pi_u32 = CDF_TOLE4(u32);
935			break;
936		case CDF_SIGNED64:
937			if (inp[i].pi_type & CDF_VECTOR)
938				goto unknown;
939			(void)memcpy(&s64, &q[o4], sizeof(s64));
940			inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64);
941			break;
942		case CDF_UNSIGNED64:
943			if (inp[i].pi_type & CDF_VECTOR)
944				goto unknown;
945			(void)memcpy(&u64, &q[o4], sizeof(u64));
946			inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64);
947			break;
948		case CDF_FLOAT:
949			if (inp[i].pi_type & CDF_VECTOR)
950				goto unknown;
951			(void)memcpy(&u32, &q[o4], sizeof(u32));
952			u32 = CDF_TOLE4(u32);
953			memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f));
954			break;
955		case CDF_DOUBLE:
956			if (inp[i].pi_type & CDF_VECTOR)
957				goto unknown;
958			(void)memcpy(&u64, &q[o4], sizeof(u64));
959			u64 = CDF_TOLE8((uint64_t)u64);
960			memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d));
961			break;
962		case CDF_LENGTH32_STRING:
963		case CDF_LENGTH32_WSTRING:
964			if (nelements > 1) {
965				size_t nelem = inp - *info;
966				if (*maxcount > CDF_PROP_LIMIT
967				    || nelements > CDF_PROP_LIMIT)
968					goto out;
969				*maxcount += nelements;
970				inp = CAST(cdf_property_info_t *,
971				    realloc(*info, *maxcount * sizeof(*inp)));
972				if (inp == NULL)
973					goto out1;
974				*info = inp;
975				inp = *info + nelem;
976			}
977			DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n",
978			    nelements));
979			for (j = 0; j < nelements && i < sh.sh_properties;
980			    j++, i++)
981			{
982				uint32_t l = CDF_GETUINT32(q, o);
983				inp[i].pi_str.s_len = l;
984				inp[i].pi_str.s_buf = (const char *)
985				    (const void *)(&q[o4 + sizeof(l)]);
986				DPRINTF(("l = %d, r = %" SIZE_T_FORMAT
987				    "u, s = %s\n", l,
988				    CDF_ROUND(l, sizeof(l)),
989				    inp[i].pi_str.s_buf));
990				if (l & 1)
991					l++;
992				o += l >> 1;
993				if (q + o >= e)
994					goto out;
995				o4 = o * sizeof(uint32_t);
996			}
997			i--;
998			break;
999		case CDF_FILETIME:
1000			if (inp[i].pi_type & CDF_VECTOR)
1001				goto unknown;
1002			(void)memcpy(&tp, &q[o4], sizeof(tp));
1003			inp[i].pi_tp = CDF_TOLE8((uint64_t)tp);
1004			break;
1005		case CDF_CLIPBOARD:
1006			if (inp[i].pi_type & CDF_VECTOR)
1007				goto unknown;
1008			break;
1009		default:
1010		unknown:
1011			DPRINTF(("Don't know how to deal with %x\n",
1012			    inp[i].pi_type));
1013			break;
1014		}
1015	}
1016	return 0;
1017out:
1018	errno = EFTYPE;
1019out1:
1020	free(*info);
1021	return -1;
1022}
1023
1024int
1025cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h,
1026    cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count)
1027{
1028	size_t maxcount;
1029	const cdf_summary_info_header_t *si =
1030	    CAST(const cdf_summary_info_header_t *, sst->sst_tab);
1031	const cdf_section_declaration_t *sd =
1032	    CAST(const cdf_section_declaration_t *, (const void *)
1033	    ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET));
1034
1035	if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 ||
1036	    cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1)
1037		return -1;
1038	ssi->si_byte_order = CDF_TOLE2(si->si_byte_order);
1039	ssi->si_os_version = CDF_TOLE2(si->si_os_version);
1040	ssi->si_os = CDF_TOLE2(si->si_os);
1041	ssi->si_class = si->si_class;
1042	cdf_swap_class(&ssi->si_class);
1043	ssi->si_count = CDF_TOLE4(si->si_count);
1044	*count = 0;
1045	maxcount = 0;
1046	*info = NULL;
1047	if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info,
1048	    count, &maxcount) == -1)
1049		return -1;
1050	return 0;
1051}
1052
1053
1054#define extract_catalog_field(t, f, l) \
1055    if (b + l + sizeof(cep->f) > eb) { \
1056	    cep->ce_namlen = 0; \
1057	    break; \
1058    } \
1059    memcpy(&cep->f, b + (l), sizeof(cep->f)); \
1060    ce[i].f = CAST(t, CDF_TOLE(cep->f))
1061
1062int
1063cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst,
1064    cdf_catalog_t **cat)
1065{
1066	size_t ss = cdf_check_stream(sst, h);
1067	const char *b = CAST(const char *, sst->sst_tab);
1068	const char *eb = b + ss * sst->sst_len;
1069	size_t nr, i, j, k;
1070	cdf_catalog_entry_t *ce;
1071	uint16_t reclen;
1072	const uint16_t *np;
1073
1074	for (nr = 0;; nr++) {
1075		memcpy(&reclen, b, sizeof(reclen));
1076		reclen = CDF_TOLE2(reclen);
1077		if (reclen == 0)
1078			break;
1079		b += reclen;
1080		if (b > eb)
1081		    break;
1082	}
1083	if (nr == 0)
1084		return -1;
1085	nr--;
1086	*cat = CAST(cdf_catalog_t *,
1087	    malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce)));
1088	if (*cat == NULL)
1089		return -1;
1090	ce = (*cat)->cat_e;
1091	memset(ce, 0, nr * sizeof(*ce));
1092	b = CAST(const char *, sst->sst_tab);
1093	for (j = i = 0; i < nr; b += reclen) {
1094		cdf_catalog_entry_t *cep = &ce[j];
1095		uint16_t rlen;
1096
1097		extract_catalog_field(uint16_t, ce_namlen, 0);
1098		extract_catalog_field(uint16_t, ce_num, 4);
1099		extract_catalog_field(uint64_t, ce_timestamp, 8);
1100		reclen = cep->ce_namlen;
1101
1102		if (reclen < 14) {
1103			cep->ce_namlen = 0;
1104			continue;
1105		}
1106
1107		cep->ce_namlen = __arraycount(cep->ce_name) - 1;
1108		rlen = reclen - 14;
1109		if (cep->ce_namlen > rlen)
1110			cep->ce_namlen = rlen;
1111
1112		np = CAST(const uint16_t *, CAST(const void *, (b + 16)));
1113		if (RCAST(const char *, np + cep->ce_namlen) > eb) {
1114			cep->ce_namlen = 0;
1115			break;
1116		}
1117
1118		for (k = 0; k < cep->ce_namlen; k++)
1119			cep->ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */
1120		cep->ce_name[cep->ce_namlen] = 0;
1121		j = i;
1122		i++;
1123	}
1124	(*cat)->cat_num = j;
1125	return 0;
1126}
1127
1128int
1129cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id)
1130{
1131	return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-"
1132	    "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0],
1133	    id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0],
1134	    id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4],
1135	    id->cl_six[5]);
1136}
1137
1138static const struct {
1139	uint32_t v;
1140	const char *n;
1141} vn[] = {
1142	{ CDF_PROPERTY_CODE_PAGE, "Code page" },
1143	{ CDF_PROPERTY_TITLE, "Title" },
1144	{ CDF_PROPERTY_SUBJECT, "Subject" },
1145	{ CDF_PROPERTY_AUTHOR, "Author" },
1146	{ CDF_PROPERTY_KEYWORDS, "Keywords" },
1147	{ CDF_PROPERTY_COMMENTS, "Comments" },
1148	{ CDF_PROPERTY_TEMPLATE, "Template" },
1149	{ CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" },
1150	{ CDF_PROPERTY_REVISION_NUMBER, "Revision Number" },
1151	{ CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" },
1152	{ CDF_PROPERTY_LAST_PRINTED, "Last Printed" },
1153	{ CDF_PROPERTY_CREATE_TIME, "Create Time/Date" },
1154	{ CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" },
1155	{ CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" },
1156	{ CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" },
1157	{ CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" },
1158	{ CDF_PROPERTY_THUMBNAIL, "Thumbnail" },
1159	{ CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" },
1160	{ CDF_PROPERTY_SECURITY, "Security" },
1161	{ CDF_PROPERTY_LOCALE_ID, "Locale ID" },
1162};
1163
1164int
1165cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p)
1166{
1167	size_t i;
1168
1169	for (i = 0; i < __arraycount(vn); i++)
1170		if (vn[i].v == p)
1171			return snprintf(buf, bufsiz, "%s", vn[i].n);
1172	return snprintf(buf, bufsiz, "0x%x", p);
1173}
1174
1175int
1176cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts)
1177{
1178	int len = 0;
1179	int days, hours, mins, secs;
1180
1181	ts /= CDF_TIME_PREC;
1182	secs = (int)(ts % 60);
1183	ts /= 60;
1184	mins = (int)(ts % 60);
1185	ts /= 60;
1186	hours = (int)(ts % 24);
1187	ts /= 24;
1188	days = (int)ts;
1189
1190	if (days) {
1191		len += snprintf(buf + len, bufsiz - len, "%dd+", days);
1192		if ((size_t)len >= bufsiz)
1193			return len;
1194	}
1195
1196	if (days || hours) {
1197		len += snprintf(buf + len, bufsiz - len, "%.2d:", hours);
1198		if ((size_t)len >= bufsiz)
1199			return len;
1200	}
1201
1202	len += snprintf(buf + len, bufsiz - len, "%.2d:", mins);
1203	if ((size_t)len >= bufsiz)
1204		return len;
1205
1206	len += snprintf(buf + len, bufsiz - len, "%.2d", secs);
1207	return len;
1208}
1209
1210char *
1211cdf_u16tos8(char *buf, size_t len, const uint16_t *p)
1212{
1213	size_t i;
1214	for (i = 0; i < len && p[i]; i++)
1215		buf[i] = (char)p[i];
1216	buf[i] = '\0';
1217	return buf;
1218}
1219
1220#ifdef CDF_DEBUG
1221void
1222cdf_dump_header(const cdf_header_t *h)
1223{
1224	size_t i;
1225
1226#define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b)
1227#define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \
1228    h->h_ ## b, 1 << h->h_ ## b)
1229	DUMP("%d", revision);
1230	DUMP("%d", version);
1231	DUMP("0x%x", byte_order);
1232	DUMP2("%d", sec_size_p2);
1233	DUMP2("%d", short_sec_size_p2);
1234	DUMP("%d", num_sectors_in_sat);
1235	DUMP("%d", secid_first_directory);
1236	DUMP("%d", min_size_standard_stream);
1237	DUMP("%d", secid_first_sector_in_short_sat);
1238	DUMP("%d", num_sectors_in_short_sat);
1239	DUMP("%d", secid_first_sector_in_master_sat);
1240	DUMP("%d", num_sectors_in_master_sat);
1241	for (i = 0; i < __arraycount(h->h_master_sat); i++) {
1242		if (h->h_master_sat[i] == CDF_SECID_FREE)
1243			break;
1244		(void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n",
1245		    "master_sat", i, h->h_master_sat[i]);
1246	}
1247}
1248
1249void
1250cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size)
1251{
1252	size_t i, j, s = size / sizeof(cdf_secid_t);
1253
1254	for (i = 0; i < sat->sat_len; i++) {
1255		(void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6"
1256		    SIZE_T_FORMAT "u: ", prefix, i, i * s);
1257		for (j = 0; j < s; j++) {
1258			(void)fprintf(stderr, "%5d, ",
1259			    CDF_TOLE4(sat->sat_tab[s * i + j]));
1260			if ((j + 1) % 10 == 0)
1261				(void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT
1262				    "u: ", i * s + j + 1);
1263		}
1264		(void)fprintf(stderr, "\n");
1265	}
1266}
1267
1268void
1269cdf_dump(const void *v, size_t len)
1270{
1271	size_t i, j;
1272	const unsigned char *p = v;
1273	char abuf[16];
1274
1275	(void)fprintf(stderr, "%.4x: ", 0);
1276	for (i = 0, j = 0; i < len; i++, p++) {
1277		(void)fprintf(stderr, "%.2x ", *p);
1278		abuf[j++] = isprint(*p) ? *p : '.';
1279		if (j == 16) {
1280			j = 0;
1281			abuf[15] = '\0';
1282			(void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ",
1283			    abuf, i + 1);
1284		}
1285	}
1286	(void)fprintf(stderr, "\n");
1287}
1288
1289void
1290cdf_dump_stream(const cdf_stream_t *sst)
1291{
1292	size_t ss = sst->sst_ss;
1293	cdf_dump(sst->sst_tab, ss * sst->sst_len);
1294}
1295
1296void
1297cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h,
1298    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
1299    const cdf_dir_t *dir)
1300{
1301	size_t i, j;
1302	cdf_directory_t *d;
1303	char name[__arraycount(d->d_name)];
1304	cdf_stream_t scn;
1305	struct timespec ts;
1306
1307	static const char *types[] = { "empty", "user storage",
1308	    "user stream", "lockbytes", "property", "root storage" };
1309
1310	for (i = 0; i < dir->dir_len; i++) {
1311		char buf[26];
1312		d = &dir->dir_tab[i];
1313		for (j = 0; j < sizeof(name); j++)
1314			name[j] = (char)CDF_TOLE2(d->d_name[j]);
1315		(void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n",
1316		    i, name);
1317		if (d->d_type < __arraycount(types))
1318			(void)fprintf(stderr, "Type: %s\n", types[d->d_type]);
1319		else
1320			(void)fprintf(stderr, "Type: %d\n", d->d_type);
1321		(void)fprintf(stderr, "Color: %s\n",
1322		    d->d_color ? "black" : "red");
1323		(void)fprintf(stderr, "Left child: %d\n", d->d_left_child);
1324		(void)fprintf(stderr, "Right child: %d\n", d->d_right_child);
1325		(void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags);
1326		cdf_timestamp_to_timespec(&ts, d->d_created);
1327		(void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf));
1328		cdf_timestamp_to_timespec(&ts, d->d_modified);
1329		(void)fprintf(stderr, "Modified %s",
1330		    cdf_ctime(&ts.tv_sec, buf));
1331		(void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector);
1332		(void)fprintf(stderr, "Size %d\n", d->d_size);
1333		switch (d->d_type) {
1334		case CDF_DIR_TYPE_USER_STORAGE:
1335			(void)fprintf(stderr, "Storage: %d\n", d->d_storage);
1336			break;
1337		case CDF_DIR_TYPE_USER_STREAM:
1338			if (sst == NULL)
1339				break;
1340			if (cdf_read_sector_chain(info, h, sat, ssat, sst,
1341			    d->d_stream_first_sector, d->d_size, &scn) == -1) {
1342				warn("Can't read stream for %s at %d len %d",
1343				    name, d->d_stream_first_sector, d->d_size);
1344				break;
1345			}
1346			cdf_dump_stream(&scn);
1347			free(scn.sst_tab);
1348			break;
1349		default:
1350			break;
1351		}
1352
1353	}
1354}
1355
1356void
1357cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
1358{
1359	cdf_timestamp_t tp;
1360	struct timespec ts;
1361	char buf[64];
1362	size_t i, j;
1363
1364	for (i = 0; i < count; i++) {
1365		cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
1366		(void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf);
1367		switch (info[i].pi_type) {
1368		case CDF_NULL:
1369			break;
1370		case CDF_SIGNED16:
1371			(void)fprintf(stderr, "signed 16 [%hd]\n",
1372			    info[i].pi_s16);
1373			break;
1374		case CDF_SIGNED32:
1375			(void)fprintf(stderr, "signed 32 [%d]\n",
1376			    info[i].pi_s32);
1377			break;
1378		case CDF_UNSIGNED32:
1379			(void)fprintf(stderr, "unsigned 32 [%u]\n",
1380			    info[i].pi_u32);
1381			break;
1382		case CDF_FLOAT:
1383			(void)fprintf(stderr, "float [%g]\n",
1384			    info[i].pi_f);
1385			break;
1386		case CDF_DOUBLE:
1387			(void)fprintf(stderr, "double [%g]\n",
1388			    info[i].pi_d);
1389			break;
1390		case CDF_LENGTH32_STRING:
1391			(void)fprintf(stderr, "string %u [%.*s]\n",
1392			    info[i].pi_str.s_len,
1393			    info[i].pi_str.s_len, info[i].pi_str.s_buf);
1394			break;
1395		case CDF_LENGTH32_WSTRING:
1396			(void)fprintf(stderr, "string %u [",
1397			    info[i].pi_str.s_len);
1398			for (j = 0; j < info[i].pi_str.s_len - 1; j++)
1399			    (void)fputc(info[i].pi_str.s_buf[j << 1], stderr);
1400			(void)fprintf(stderr, "]\n");
1401			break;
1402		case CDF_FILETIME:
1403			tp = info[i].pi_tp;
1404			if (tp < 1000000000000000LL) {
1405				cdf_print_elapsed_time(buf, sizeof(buf), tp);
1406				(void)fprintf(stderr, "timestamp %s\n", buf);
1407			} else {
1408				char tbuf[26];
1409				cdf_timestamp_to_timespec(&ts, tp);
1410				(void)fprintf(stderr, "timestamp %s",
1411				    cdf_ctime(&ts.tv_sec, tbuf));
1412			}
1413			break;
1414		case CDF_CLIPBOARD:
1415			(void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32);
1416			break;
1417		default:
1418			DPRINTF(("Don't know how to deal with %x\n",
1419			    info[i].pi_type));
1420			break;
1421		}
1422	}
1423}
1424
1425
1426void
1427cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst)
1428{
1429	char buf[128];
1430	cdf_summary_info_header_t ssi;
1431	cdf_property_info_t *info;
1432	size_t count;
1433
1434	(void)&h;
1435	if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1)
1436		return;
1437	(void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order);
1438	(void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff,
1439	    ssi.si_os_version >> 8);
1440	(void)fprintf(stderr, "Os %d\n", ssi.si_os);
1441	cdf_print_classid(buf, sizeof(buf), &ssi.si_class);
1442	(void)fprintf(stderr, "Class %s\n", buf);
1443	(void)fprintf(stderr, "Count %d\n", ssi.si_count);
1444	cdf_dump_property_info(info, count);
1445	free(info);
1446}
1447
1448
1449void
1450cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst)
1451{
1452	cdf_catalog_t *cat;
1453	cdf_unpack_catalog(h, sst, &cat);
1454	const cdf_catalog_entry_t *ce = cat->cat_e;
1455	struct timespec ts;
1456	char tbuf[64], sbuf[256];
1457	size_t i;
1458
1459	printf("Catalog:\n");
1460	for (i = 0; i < cat->cat_num; i++) {
1461		cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp);
1462		printf("\t%d %s %s", ce[i].ce_num,
1463		    cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
1464		    cdf_ctime(&ts.tv_sec, tbuf));
1465	}
1466	free(cat);
1467}
1468
1469#endif
1470
1471#ifdef TEST
1472int
1473main(int argc, char *argv[])
1474{
1475	int i;
1476	cdf_header_t h;
1477	cdf_sat_t sat, ssat;
1478	cdf_stream_t sst, scn;
1479	cdf_dir_t dir;
1480	cdf_info_t info;
1481	const cdf_directory_t *root;
1482#ifdef __linux__
1483#define getprogname() __progname
1484	extern char *__progname;
1485#endif
1486	if (argc < 2) {
1487		(void)fprintf(stderr, "Usage: %s <filename>\n", getprogname());
1488		return -1;
1489	}
1490
1491	info.i_buf = NULL;
1492	info.i_len = 0;
1493	for (i = 1; i < argc; i++) {
1494		if ((info.i_fd = open(argv[1], O_RDONLY)) == -1)
1495			err(1, "Cannot open `%s'", argv[1]);
1496
1497		if (cdf_read_header(&info, &h) == -1)
1498			err(1, "Cannot read header");
1499#ifdef CDF_DEBUG
1500		cdf_dump_header(&h);
1501#endif
1502
1503		if (cdf_read_sat(&info, &h, &sat) == -1)
1504			err(1, "Cannot read sat");
1505#ifdef CDF_DEBUG
1506		cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
1507#endif
1508
1509		if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1)
1510			err(1, "Cannot read ssat");
1511#ifdef CDF_DEBUG
1512		cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
1513#endif
1514
1515		if (cdf_read_dir(&info, &h, &sat, &dir) == -1)
1516			err(1, "Cannot read dir");
1517
1518		if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root)
1519		    == -1)
1520			err(1, "Cannot read short stream");
1521#ifdef CDF_DEBUG
1522		cdf_dump_stream(&sst);
1523#endif
1524
1525#ifdef CDF_DEBUG
1526		cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
1527#endif
1528
1529
1530		if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
1531		    &scn) == -1)
1532			warn("Cannot read summary info");
1533#ifdef CDF_DEBUG
1534		else
1535			cdf_dump_summary_info(&h, &scn);
1536#endif
1537		if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst,
1538		    &dir, "Catalog", &scn) == -1)
1539			warn("Cannot read catalog");
1540#ifdef CDF_DEBUG
1541		else
1542			cdf_dump_catalog(&h, &scn);
1543#endif
1544
1545		(void)close(info.i_fd);
1546	}
1547
1548	return 0;
1549}
1550#endif
1551