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