diff options
Diffstat (limited to 'src/cdf.c')
-rw-r--r-- | src/cdf.c | 198 |
1 files changed, 161 insertions, 37 deletions
@@ -35,7 +35,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: cdf.c,v 1.52 2013/02/18 15:40:59 christos Exp $") +FILE_RCSID("@(#)$File: cdf.c,v 1.69 2014/12/04 15:56:46 christos Exp $") #endif #include <assert.h> @@ -73,6 +73,8 @@ static union { #define CDF_TOLE8(x) ((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x))) #define CDF_TOLE4(x) ((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x))) #define CDF_TOLE2(x) ((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x))) +#define CDF_TOLE(x) (sizeof(x) == 2 ? CDF_TOLE2(x) : (sizeof(x) == 4 ? \ + CDF_TOLE4(x) : CDF_TOLE8(x))) #define CDF_GETUINT32(x, y) cdf_getuint32(x, y) @@ -267,13 +269,15 @@ cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h, { const char *b = (const char *)sst->sst_tab; const char *e = ((const char *)p) + tail; + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); (void)&line; - if (e >= b && (size_t)(e - b) <= CDF_SEC_SIZE(h) * sst->sst_len) + if (e >= b && (size_t)(e - b) <= ss * sst->sst_len) return 0; DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u" " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %" SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b), - CDF_SEC_SIZE(h) * sst->sst_len, CDF_SEC_SIZE(h), sst->sst_len)); + ss * sst->sst_len, ss, sst->sst_len)); errno = EFTYPE; return -1; } @@ -296,8 +300,6 @@ cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len) if (info->i_fd == -1) return -1; - return -1; - if (pread(info->i_fd, buf, len, off) != (ssize_t)len) return -1; @@ -354,10 +356,10 @@ cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, size_t ss = CDF_SHORT_SEC_SIZE(h); size_t pos = CDF_SHORT_SEC_POS(h, id); assert(ss == len); - if (pos > CDF_SEC_SIZE(h) * sst->sst_len) { + if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) { DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", - pos, CDF_SEC_SIZE(h) * sst->sst_len)); + pos + len, CDF_SEC_SIZE(h) * sst->sst_len)); return -1; } (void)memcpy(((char *)buf) + offs, @@ -457,9 +459,16 @@ size_t cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size) { size_t i, j; - cdf_secid_t maxsector = (cdf_secid_t)(sat->sat_len * size); + cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size) + / sizeof(maxsector)); DPRINTF(("Chain:")); + if (sid == CDF_SECID_END_OF_CHAIN) { + /* 0-length chain. */ + DPRINTF((" empty\n")); + return 0; + } + for (j = i = 0; sid >= 0; i++, j++) { DPRINTF((" %d", sid)); if (j >= CDF_LOOP_LIMIT) { @@ -467,13 +476,18 @@ cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size) errno = EFTYPE; return (size_t)-1; } - if (sid > maxsector) { - DPRINTF(("Sector %d > %d\n", sid, maxsector)); + if (sid >= maxsector) { + DPRINTF(("Sector %d >= %d\n", sid, maxsector)); errno = EFTYPE; return (size_t)-1; } sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); } + if (i == 0) { + DPRINTF((" none, sid: %d\n", sid)); + return (size_t)-1; + + } DPRINTF(("\n")); return i; } @@ -677,11 +691,13 @@ out: int cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h, - const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn) + const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn, + const cdf_directory_t **root) { size_t i; const cdf_directory_t *d; + *root = NULL; for (i = 0; i < dir->dir_len; i++) if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) break; @@ -690,6 +706,7 @@ cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h, if (i == dir->dir_len) goto out; d = &dir->dir_tab[i]; + *root = d; /* If the it is not there, just fake it; some docs don't have it */ if (d->d_stream_first_sector < 0) @@ -718,18 +735,27 @@ cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn) { + return cdf_read_user_stream(info, h, sat, ssat, sst, dir, + "\05SummaryInformation", scn); +} + +int +cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, const char *name, cdf_stream_t *scn) +{ size_t i; const cdf_directory_t *d; - static const char name[] = "\05SummaryInformation"; + size_t name_len = strlen(name) + 1; for (i = dir->dir_len; i > 0; i--) if (dir->dir_tab[i - 1].d_type == CDF_DIR_TYPE_USER_STREAM && - cdf_namecmp(name, dir->dir_tab[i - 1].d_name, sizeof(name)) + cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len) == 0) break; if (i == 0) { - DPRINTF(("Cannot find summary information section\n")); + DPRINTF(("Cannot find user stream `%s'\n", name)); errno = ESRCH; return -1; } @@ -798,10 +824,19 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1) goto out; for (i = 0; i < sh.sh_properties; i++) { - size_t ofs = CDF_GETUINT32(p, (i << 1) + 1); + size_t tail = (i << 1) + 1; + size_t ofs; + if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t), + __LINE__) == -1) + goto out; + ofs = CDF_GETUINT32(p, tail); q = (const uint8_t *)(const void *) ((const char *)(const void *)p + ofs - 2 * sizeof(uint32_t)); + if (q < p) { + DPRINTF(("Wrapped around %p < %p\n", q, p)); + goto out; + } if (q > e) { DPRINTF(("Ran of the end %p > %p\n", q, e)); goto out; @@ -812,6 +847,10 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, i, inp[i].pi_id, inp[i].pi_type, q - p, offs)); if (inp[i].pi_type & CDF_VECTOR) { nelements = CDF_GETUINT32(q, 1); + if (nelements == 0) { + DPRINTF(("CDF_VECTOR with nelements == 0\n")); + goto out; + } o = 2; } else { nelements = 1; @@ -886,7 +925,9 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, } DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n", nelements)); - for (j = 0; j < nelements; j++, i++) { + for (j = 0; j < nelements && i < sh.sh_properties; + j++, i++) + { uint32_t l = CDF_GETUINT32(q, o); inp[i].pi_str.s_len = l; inp[i].pi_str.s_buf = (const char *) @@ -931,7 +972,7 @@ int cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h, cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count) { - size_t i, maxcount; + size_t maxcount; const cdf_summary_info_header_t *si = CAST(const cdf_summary_info_header_t *, sst->sst_tab); const cdf_section_declaration_t *sd = @@ -946,25 +987,65 @@ cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h, ssi->si_os = CDF_TOLE2(si->si_os); ssi->si_class = si->si_class; cdf_swap_class(&ssi->si_class); - ssi->si_count = CDF_TOLE2(si->si_count); + ssi->si_count = CDF_TOLE4(si->si_count); *count = 0; maxcount = 0; *info = NULL; - for (i = 0; i < CDF_TOLE4(si->si_count); i++) { - if (i >= CDF_LOOP_LIMIT) { - DPRINTF(("Unpack summary info loop limit")); - errno = EFTYPE; - return -1; - } - if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), - info, count, &maxcount) == -1) { - return -1; - } - } + if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info, + count, &maxcount) == -1) + return -1; return 0; } +#define extract_catalog_field(f, l) \ + memcpy(&ce[i].f, b + (l), sizeof(ce[i].f)); \ + ce[i].f = CDF_TOLE(ce[i].f) + +int +cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst, + cdf_catalog_t **cat) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + const char *b = CAST(const char *, sst->sst_tab); + const char *eb = b + ss * sst->sst_len; + size_t nr, i, k; + cdf_catalog_entry_t *ce; + uint16_t reclen; + const uint16_t *np; + + for (nr = 0; b < eb; nr++) { + memcpy(&reclen, b, sizeof(reclen)); + reclen = CDF_TOLE2(reclen); + if (reclen == 0) + break; + b += reclen; + } + *cat = CAST(cdf_catalog_t *, + malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce))); + (*cat)->cat_num = nr; + ce = (*cat)->cat_e; + b = CAST(const char *, sst->sst_tab); + for (i = 0; i < nr; i++) { + extract_catalog_field(ce_namlen, 0); + extract_catalog_field(ce_num, 2); + extract_catalog_field(ce_timestamp, 6); + reclen = ce[i].ce_namlen; + ce[i].ce_namlen = + sizeof(ce[i].ce_name) / sizeof(ce[i].ce_name[0]) - 1; + if (ce[i].ce_namlen > reclen - 14) + ce[i].ce_namlen = reclen - 14; + np = CAST(const uint16_t *, (b + 16)); + for (k = 0; k < ce[i].ce_namlen; k++) { + ce[i].ce_name[k] = np[k]; + CDF_TOLE2(ce[i].ce_name[k]); + } + ce[i].ce_name[ce[i].ce_namlen] = 0; + b += reclen; + } + return 0; +} int cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) @@ -1048,6 +1129,15 @@ cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) return len; } +char * +cdf_u16tos8(char *buf, size_t len, const uint16_t *p) +{ + size_t i; + for (i = 0; i < len && p[i]; i++) + buf[i] = (char)p[i]; + buf[i] = '\0'; + return buf; +} #ifdef CDF_DEBUG void @@ -1073,7 +1163,7 @@ cdf_dump_header(const cdf_header_t *h) for (i = 0; i < __arraycount(h->h_master_sat); i++) { if (h->h_master_sat[i] == CDF_SECID_FREE) break; - (void)fprintf(stderr, "%35.35s[%.3zu] = %d\n", + (void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n", "master_sat", i, h->h_master_sat[i]); } } @@ -1140,6 +1230,7 @@ cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h, "user stream", "lockbytes", "property", "root storage" }; for (i = 0; i < dir->dir_len; i++) { + char buf[26]; d = &dir->dir_tab[i]; for (j = 0; j < sizeof(name); j++) name[j] = (char)CDF_TOLE2(d->d_name[j]); @@ -1155,9 +1246,10 @@ cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h, (void)fprintf(stderr, "Right child: %d\n", d->d_right_child); (void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags); cdf_timestamp_to_timespec(&ts, d->d_created); - (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec)); + (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf)); cdf_timestamp_to_timespec(&ts, d->d_modified); - (void)fprintf(stderr, "Modified %s", cdf_ctime(&ts.tv_sec)); + (void)fprintf(stderr, "Modified %s", + cdf_ctime(&ts.tv_sec, buf)); (void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector); (void)fprintf(stderr, "Size %d\n", d->d_size); switch (d->d_type) { @@ -1235,9 +1327,10 @@ cdf_dump_property_info(const cdf_property_info_t *info, size_t count) cdf_print_elapsed_time(buf, sizeof(buf), tp); (void)fprintf(stderr, "timestamp %s\n", buf); } else { + char buf[26]; cdf_timestamp_to_timespec(&ts, tp); (void)fprintf(stderr, "timestamp %s", - cdf_ctime(&ts.tv_sec)); + cdf_ctime(&ts.tv_sec, buf)); } break; case CDF_CLIPBOARD: @@ -1265,7 +1358,7 @@ cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) return; (void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order); (void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff, - ssi.si_os_version >> 8); + ssi.si_os_version >> 8); (void)fprintf(stderr, "Os %d\n", ssi.si_os); cdf_print_classid(buf, sizeof(buf), &ssi.si_class); (void)fprintf(stderr, "Class %s\n", buf); @@ -1274,6 +1367,27 @@ cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) free(info); } + +void +cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst) +{ + cdf_catalog_t *cat; + cdf_unpack_catalog(h, sst, &cat); + const cdf_catalog_entry_t *ce = cat->cat_e; + struct timespec ts; + char tbuf[64], sbuf[256]; + size_t i; + + printf("Catalog:\n"); + for (i = 0; i < cat->cat_num; i++) { + cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp); + printf("\t%d %s %s", ce[i].ce_num, + cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name), + cdf_ctime(&ts.tv_sec, tbuf)); + } + free(cat); +} + #endif #ifdef TEST @@ -1286,6 +1400,7 @@ main(int argc, char *argv[]) cdf_stream_t sst, scn; cdf_dir_t dir; cdf_info_t info; + const cdf_directory_t *root; if (argc < 2) { (void)fprintf(stderr, "Usage: %s <filename>\n", getprogname()); @@ -1319,7 +1434,8 @@ main(int argc, char *argv[]) if (cdf_read_dir(&info, &h, &sat, &dir) == -1) err(1, "Cannot read dir"); - if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst) == -1) + if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root) + == -1) err(1, "Cannot read short stream"); #ifdef CDF_DEBUG cdf_dump_stream(&h, &sst); @@ -1332,9 +1448,17 @@ main(int argc, char *argv[]) if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir, &scn) == -1) - err(1, "Cannot read summary info"); + warn("Cannot read summary info"); +#ifdef CDF_DEBUG + else + cdf_dump_summary_info(&h, &scn); +#endif + if (cdf_read_catalog(&info, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + warn("Cannot read catalog"); #ifdef CDF_DEBUG - cdf_dump_summary_info(&h, &scn); + else + cdf_dump_catalog(&h, &scn); #endif (void)close(info.i_fd); |