From 68193a70c384d141391e77b0670a3e23d7e8a70b Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Thu, 16 Oct 2003 22:28:19 +0000 Subject: [PATCH] Added workaround for malformed HFS option lists. Added support for skipping junk found at the start of an archive file. --- nufxlib-0/Archive.c | 49 ++++++++++++++++++++++++++++++++++++----- nufxlib-0/ArchiveIO.c | 4 ++-- nufxlib-0/ChangeLog.txt | 8 +++++++ nufxlib-0/Debug.c | 3 ++- nufxlib-0/Deferred.c | 2 ++ nufxlib-0/NufxLib.h | 5 +++-- nufxlib-0/NufxLibPriv.h | 4 ++++ nufxlib-0/Record.c | 20 ++++++++++++++++- nufxlib-0/Value.c | 15 ++++++++++++- 9 files changed, 97 insertions(+), 13 deletions(-) diff --git a/nufxlib-0/Archive.c b/nufxlib-0/Archive.c index 6c7d316..bebe574 100644 --- a/nufxlib-0/Archive.c +++ b/nufxlib-0/Archive.c @@ -39,6 +39,7 @@ static const uchar kNuSHKSEAID[] = #define kNuSEALength1 11946 /* length of archive (4B?) */ #define kNuSEALength2 12001 /* length of archive (4B?) */ +#define kDefaultJunkSkipMax 1024 /* default junk scan size */ static void Nu_CloseAndFree(NuArchive* pArchive); @@ -93,6 +94,8 @@ Nu_NuArchiveNew(NuArchive** ppArchive) (*ppArchive)->valMimicSHK = false; (*ppArchive)->valMaskDataless = false; (*ppArchive)->valStripHighASCII = false; + /* bug: this can't be set by application! */ + (*ppArchive)->valJunkSkipMax = kDefaultJunkSkipMax; (*ppArchive)->messageHandlerFunc = gNuGlobalErrorMessageHandler; @@ -231,6 +234,7 @@ bail: * assumed to start at offset 0. * * Wrappers must appear in this order: + * Leading junk * Binary II * ShrinkIt SEA (Self-Extracting Archive) * @@ -266,7 +270,9 @@ Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp) hasBinary2 = hasSea = true; break; default: - if (pArchive->headerOffset) { + if (pArchive->headerOffset != 0 && + pArchive->headerOffset != pArchive->junkOffset) + { Nu_ReportError(NU_BLOB, kNuErrNone, "Can't fix the wrapper??"); err = kNuErrInternal; goto bail; @@ -274,7 +280,7 @@ Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp) goto bail; } - err = Nu_FSeek(fp, 0, SEEK_SET); + err = Nu_FSeek(fp, pArchive->junkOffset, SEEK_SET); BailError(err); if (hasBinary2) { @@ -290,9 +296,10 @@ Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp) goto bail; } - /* archiveLen includes the SEA wrapper, if any */ + /* archiveLen includes the SEA wrapper, if any, but excludes junk */ archiveLen = pArchive->newMasterHeader.mhMasterEOF + - (pArchive->headerOffset - kNuBinary2BlockSize); + (pArchive->headerOffset - pArchive->junkOffset) - + kNuBinary2BlockSize; archiveLen512 = (archiveLen + 511) / 512; err = Nu_FSeek(fp, kNuBNYFileSizeLo - kNufileIDLen, SEEK_CUR); @@ -411,7 +418,9 @@ Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp) hasBinary2 = hasSea = true; break; default: - if (pArchive->headerOffset) { + if (pArchive->headerOffset != 0 && + pArchive->headerOffset != pArchive->junkOffset) + { Nu_ReportError(NU_BLOB, kNuErrNone, "Can't check the padding??"); err = kNuErrInternal; goto bail; @@ -433,7 +442,9 @@ Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp) err = Nu_FTell(fp, &curOffset); BailError(err); + curOffset -= pArchive->junkOffset; /* don't factor junk into account */ + DBUG(("+++ BNY needs %ld bytes of padding\n", curOffset & 0x7f)); if (curOffset & 0x7f) { int i; @@ -473,6 +484,13 @@ bail: * also interested in related formats, we try to return a meaningful error * code for stuff we recognize (especially Binary II). * + * If at first we don't succeed, we keep trying further along until we + * find something we recognize. We don't want to just scan for the + * NuFile ID, because that might prevent this from working properly with + * SEA archives which push the NuFX start out about 12K. We also wouldn't + * be able to update the BNY/SEA wrappers correctly. So, we inch our way + * along until we find something we recognize or get bored. + * * On exit, the stream will be positioned just past the master header. */ static NuError @@ -490,7 +508,10 @@ Nu_ReadMasterHeader(NuArchive* pArchive) fp = pArchive->archiveFp; /* saves typing */ pHeader = &pArchive->masterHeader; - pArchive->headerOffset = 0; + pArchive->junkOffset = 0; + +retry: + pArchive->headerOffset = pArchive->junkOffset; Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); /* may have read fewer than kNufileIDLen; that's okay */ @@ -548,6 +569,22 @@ Nu_ReadMasterHeader(NuArchive* pArchive) } if (memcmp(kNuMasterID, pHeader->mhNufileID, kNufileIDLen) != 0) { + /* + * Doesn't look like a NuFX archive. Scan forward and see if we + * can find the start past some leading junk. MacBinary headers + * and chunks of HTTP seem popular on FTP sites. + */ + if ((pArchive->openMode == kNuOpenRO || + pArchive->openMode == kNuOpenRW) && + pArchive->junkOffset < (long)pArchive->valJunkSkipMax) + { + pArchive->junkOffset++; + DBUG(("+++ scanning from offset %ld\n", pArchive->junkOffset)); + err = Nu_SeekArchive(pArchive, fp, pArchive->junkOffset, SEEK_SET); + BailError(err); + goto retry; + } + err = kNuErrNotNuFX; if (isBinary2) { diff --git a/nufxlib-0/ArchiveIO.c b/nufxlib-0/ArchiveIO.c index bb09f29..6d6e68b 100644 --- a/nufxlib-0/ArchiveIO.c +++ b/nufxlib-0/ArchiveIO.c @@ -24,7 +24,7 @@ */ /* - * Read one little-endian bytes, optionally computing a CRC. + * Read one byte, optionally computing a CRC. */ uchar Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc) @@ -38,7 +38,7 @@ Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc) ic = getc(fp); *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); - return ic; + return (uchar) ic; } uchar diff --git a/nufxlib-0/ChangeLog.txt b/nufxlib-0/ChangeLog.txt index 9845e6e..c2a8679 100644 --- a/nufxlib-0/ChangeLog.txt +++ b/nufxlib-0/ChangeLog.txt @@ -1,3 +1,11 @@ +2003/10/16 ***** v2.0.1 shipped ***** + +2003/10/16 fadden + - Added workaround for bad HFS option lists created by GSHK. + - Added junk-skipping feature. Up to 1024 bytes of crud (e.g. + MacBinary headers or HTTP remnants) will be searched for evidence + of an archive. + 2003/06/19 sheppy - Added support for resource forks and file and aux types when built for Mac OS X. diff --git a/nufxlib-0/Debug.c b/nufxlib-0/Debug.c index 47abd98..d77000b 100644 --- a/nufxlib-0/Debug.c +++ b/nufxlib-0/Debug.c @@ -366,7 +366,8 @@ Nu_DebugDumpAll(NuArchive* pArchive) printf("*Archive pathname: '%s'\n", pArchive->archivePathname); printf("*Archive type: %d\n", pArchive->archiveType); - printf("*Header offset: %ld\n", pArchive->headerOffset); + printf("*Header offset: %ld (junk offset=%ld)\n", + pArchive->headerOffset, pArchive->junkOffset); printf("*Num records: %ld orig, %ld copy, %ld new\n", Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet), Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet), diff --git a/nufxlib-0/Deferred.c b/nufxlib-0/Deferred.c index fd09581..08ad517 100644 --- a/nufxlib-0/Deferred.c +++ b/nufxlib-0/Deferred.c @@ -2351,6 +2351,8 @@ Nu_Flush(NuArchive* pArchive, long* pStatusFlags) * assumption is invalid, we'd need to adjust "headerOffset" earlier, * or do lots of data copying. Looks like Binary II and SEA headers * are both fixed size, so we should be okay. + * + * We also carry forward any unrecognized junk. */ if (pArchive->headerOffset) { if (writeToTemp) { diff --git a/nufxlib-0/NufxLib.h b/nufxlib-0/NufxLib.h index a1c5539..925546e 100644 --- a/nufxlib-0/NufxLib.h +++ b/nufxlib-0/NufxLib.h @@ -33,7 +33,7 @@ extern "C" { */ #define kNuVersionMajor 2 #define kNuVersionMinor 0 -#define kNuVersionBug 0 +#define kNuVersionBug 1 /* @@ -268,7 +268,8 @@ typedef enum NuValueID { kNuValueModifyOrig = 9, kNuValueMimicSHK = 10, kNuValueMaskDataless = 11, - kNuValueStripHighASCII = 12 + kNuValueStripHighASCII = 12, + kNuValueJunkSkipMax = 13 } NuValueID; typedef unsigned long NuValue; diff --git a/nufxlib-0/NufxLibPriv.h b/nufxlib-0/NufxLibPriv.h index 60df09c..ecc520e 100644 --- a/nufxlib-0/NufxLibPriv.h +++ b/nufxlib-0/NufxLibPriv.h @@ -103,6 +103,9 @@ struct NuArchive { char* archivePathname; /* pathname or "(stream)" */ FILE* archiveFp; NuArchiveType archiveType; + + /* stuff before NuFX; both offsets are from 0, i.e. hdrOff includes junk */ + long junkOffset; /* skip past leading junk */ long headerOffset; /* adjustment for BXY/SEA/BSE */ char* tmpPathname; /* temp file, for writes */ @@ -152,6 +155,7 @@ struct NuArchive { NuValue valModifyOrig; /* modify original arc in place? */ NuValue valOnlyUpdateOlder; /* modify original arc in place? */ NuValue valStripHighASCII; /* during EOL conv, strip hi bit? */ + NuValue valJunkSkipMax; /* scan this far for header */ /* callback functions */ NuCallback selectionFilterFunc; diff --git a/nufxlib-0/Record.c b/nufxlib-0/Record.c index 1b2924d..c2f3d7e 100644 --- a/nufxlib-0/Record.c +++ b/nufxlib-0/Record.c @@ -953,11 +953,29 @@ Nu_ReadRecordHeader(NuArchive* pArchive, NuRecord* pRecord) pRecord->recOptionSize = Nu_ReadTwoC(pArchive, fp, &crc); bytesRead += 2; + /* + * It appears GS/ShrinkIt is creating bad option lists, claiming + * 36 bytes of data when there's only room for 18. Since we don't + * really pay attention to the option list + */ + if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { + DBUG(("--- truncating option list from %d to %d\n", + pRecord->recOptionSize, + pRecord->recAttribCount -2 - bytesRead)); + if (pRecord->recAttribCount -2 > bytesRead) + pRecord->recOptionSize = pRecord->recAttribCount -2 - bytesRead; + else + pRecord->recOptionSize = 0; + } + + /* this is the older test, which rejected funky archives */ if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { /* option size exceeds the total attribute area */ err = kNuErrBadRecord; Nu_ReportError(NU_BLOB, kNuErrBadRecord, - "Option size exceeds attribs (%u)", pRecord->recOptionSize); + "Option size (%u) exceeds attribs (%u,%u-2)", + pRecord->recOptionSize, bytesRead, + pRecord->recAttribCount); goto bail; } diff --git a/nufxlib-0/Value.c b/nufxlib-0/Value.c index 7f56b65..83be316 100644 --- a/nufxlib-0/Value.c +++ b/nufxlib-0/Value.c @@ -8,6 +8,8 @@ */ #include "NufxLibPriv.h" +#define kMaxJunkSkipMax 8192 + /* * Get a configurable parameter. @@ -57,6 +59,9 @@ Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue) case kNuValueStripHighASCII: *pValue = pArchive->valStripHighASCII; break; + case kNuValueJunkSkipMax: + *pValue = pArchive->valJunkSkipMax; + break; default: err = kNuErrInvalidArg; Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); @@ -168,11 +173,19 @@ Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value) case kNuValueStripHighASCII: if (value != true && value != false) { Nu_ReportError(NU_BLOB, err, - "Invalid kNuStripHighASCII value %ld", value); + "Invalid kNuValueStripHighASCII value %ld", value); goto bail; } pArchive->valStripHighASCII = value; break; + case kNuValueJunkSkipMax: + if (value > kMaxJunkSkipMax) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueJunkSkipMax value %ld", value); + goto bail; + } + pArchive->valJunkSkipMax = value; + break; default: Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); goto bail;