Added workaround for malformed HFS option lists.

Added support for skipping junk found at the start of an archive file.
This commit is contained in:
Andy McFadden 2003-10-16 22:28:19 +00:00
parent cd1f9e78a1
commit 68193a70c3
9 changed files with 97 additions and 13 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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.

View File

@ -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),

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;