mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1078 lines
38 KiB
1078 lines
38 KiB
/*** buildcab.c - Diamond Cabinet Builder routines
|
|
*
|
|
* Microsoft Confidential
|
|
* Copyright (C) Microsoft Corporation 1993-1994
|
|
* All Rights Reserved.
|
|
*
|
|
* Author:
|
|
* Chuck Strouss
|
|
*
|
|
* History:
|
|
* 28-Dec-1993 chuckst Initial version
|
|
* 09-Mar-1994 bens Add CFHEADER.flags support; RESERVE support
|
|
* 15-Mar-1994 bens Finish RESERVE support
|
|
* 22-Mar-1994 bens Renames for include file improvements
|
|
* 28-Mar-1994 bens Store setID and cabinet number
|
|
* 10-Aug-1994 bens Bug fix: If new cabinet is on new disk,
|
|
* ignore unused space on previous disk.
|
|
*/
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys\stat.h>
|
|
#include <io.h>
|
|
#include <errno.h>
|
|
|
|
#include "types.h"
|
|
#include "asrt.h"
|
|
#include "checksum.h"
|
|
#include "fci_int.h"
|
|
#include "cabinet.h"
|
|
#include "erf.h"
|
|
#include "..\mszip\mci.h" // Get MCI definitions for buildcab.h
|
|
#include "buildcab.h"
|
|
|
|
// Internal function prototypes
|
|
|
|
BOOL filecopy(int hfdst,
|
|
int hfsrc,
|
|
PFNFCISTATUS pfnProgress,
|
|
unsigned long *pcbDone,
|
|
unsigned long cbTot,
|
|
void *pv,
|
|
PERF perf);
|
|
|
|
void InitCFHEADER(CFHEADER *pcfheader);
|
|
|
|
BOOL ReadCFFILEEntry(PCAB pcab,
|
|
PFOL pfol);
|
|
|
|
BOOL ReadCFDATAEntry(PCAB pcab,CFDATA *pcfdata,PFOL pfol);
|
|
|
|
BOOL ReadPSZ(int fh, char *pb, int cb, PERF perf);
|
|
|
|
BOOL WritePsz(int hf,
|
|
char *psz,
|
|
PERF perf);
|
|
|
|
|
|
|
|
|
|
HCAB CreateCab(
|
|
char *pszFileName, // name to use for CAB files
|
|
PERF perf, // error type return structure
|
|
PFNFCIFILEPLACED pfnfiledest, // file placement notification
|
|
PFNALLOC pfnalloc, // memory allocation function
|
|
PFNFREE pfnfree, // memory free function
|
|
PFNFCIGETTEMPFILE pfntemp // temp file name generator
|
|
)
|
|
{
|
|
PCAB pcab;
|
|
|
|
pcab = (PCAB)(*pfnalloc)(sizeof(CAB));
|
|
if (pcab == NULL) {
|
|
ErfSetCodes(perf,FCIERR_ALLOC_FAIL,0);
|
|
return NULL;
|
|
}
|
|
|
|
SetAssertSignature(pcab,sigCAB);
|
|
AssertCAB(pcab); // Make sure we've really set sig
|
|
|
|
pcab->perf = perf; // Save error structure
|
|
pcab->pfnfiledest = pfnfiledest; // save file placement notify function
|
|
pcab->pfnalloc = pfnalloc; // Save alloc function in our context
|
|
pcab->pfnfree = pfnfree; // Save free function in our context
|
|
pcab->pfntemp = pfntemp; // Save temp filename generator
|
|
|
|
//** clear the forward/back continuation cabinet info
|
|
pcab->achCabinetFirst[0]=0;
|
|
pcab->achDiskFirst [0]=0;
|
|
pcab->achNxtCabFirst [0]=0;
|
|
pcab->achNxtDskFirst [0]=0;
|
|
pcab->achCabinetNext [0]=0;
|
|
pcab->achDiskNext [0]=0;
|
|
|
|
|
|
//** Create cabinet temp files
|
|
if (!CrTempFiles (pcab->tf,NUM_CAB_TF,pfntemp,pcab->perf))
|
|
{
|
|
ClearAssertSignature(pcab);
|
|
pcab->pfnfree (pcab); // free our data
|
|
return NULL; // error code already filled in
|
|
}
|
|
|
|
InitCFHEADER(&pcab->cfheader);
|
|
pcab->iFolder = 0; // first folder number we'll write
|
|
|
|
return HCABfromPCAB(pcab);
|
|
}
|
|
|
|
|
|
/*** AddFolderToCabinet - Add a completed folder to a cabinet
|
|
*
|
|
* We may have to split the folder across multiple cabinets!
|
|
*
|
|
* Entry:
|
|
* pcab - cabinet builder context
|
|
* pfol - folder builder context
|
|
* fGetNextCab - TRUE => call GetNextCab, even if not overflowing
|
|
* GetNextCab - callback: Gets continuation cabinet information
|
|
* pfnProgress - callback: progress information
|
|
* pv - caller's context for callbacks
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE;
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE; pcab->perf filled in
|
|
*/
|
|
BOOL AddFolderToCabinet(PCAB pcab,
|
|
PFOL pfol,
|
|
BOOL fGetNextCab,
|
|
PFNFCIGETNEXTCABINET GetNextCab,
|
|
PFNFCISTATUS pfnProgress,
|
|
void *pv)
|
|
{
|
|
BOOL fCabinetFull;
|
|
|
|
UOFF uoffLast;
|
|
long cbSlack; // May go negative when vol is overfull
|
|
COFF cbDataLast;
|
|
COFF cbDataLastEnd;
|
|
COFF cbPredict;
|
|
UINT cbPartial;
|
|
|
|
unsigned long cbDone; // for status reporting
|
|
unsigned long cbTot; // ditto
|
|
|
|
TF tf[NUM_FOLDER_TF]; // temp files for folder splitting
|
|
|
|
CFDATA *pcfdata=NULL;
|
|
CFDATA *pcfdataFirstPart=NULL;
|
|
CFFOLDER *pcffolder=NULL;
|
|
|
|
AssertCAB(pcab); // verify structure signatures
|
|
AssertFOL(pfol);
|
|
|
|
//** Assume we'll have no forward continuations for this folder
|
|
pcab->achNxtCabFirst [0]=0;
|
|
pcab->achNxtDskFirst [0]=0;
|
|
|
|
//** Flush the folder
|
|
if (!FolderFlush(pfol,pfnProgress,pv)) {
|
|
goto error; // error already filled in
|
|
}
|
|
|
|
uoffLast = 0; // Track emitted uncompressed size
|
|
|
|
//** Make sure folder has some data in it
|
|
//BUGBUG 01-Apr-1994 bens What if the fGetNextCab is true?
|
|
// We need to make sure the GetNextCab code path gets followed, but the
|
|
// following code just exits.
|
|
//
|
|
if (!(pfol->tf[iftfCFFOLDER].cb + pfol->tf[iftfCFDATA].cb)) {
|
|
return TRUE; // No data, so do not increment iFolder
|
|
}
|
|
|
|
//** Allocate buffers for reading/writing CFDATA and CFFOLDER structures.
|
|
// Due to the RESERVE fields, these are not fixed sized buffers!
|
|
if (!(pcfdata = (*pcab->pfnalloc)(pcab->cbCFDataPlusReserve)) ||
|
|
!(pcfdataFirstPart = (*pcab->pfnalloc)(pcab->cbCFDataPlusReserve)) ||
|
|
!(pcffolder = (*pcab->pfnalloc)(pcab->cbCFFolderPlusReserve))) {
|
|
goto error;
|
|
}
|
|
|
|
//** Zero buffers so that reserved section is zero
|
|
memset(pcfdata,0,pcab->cbCFDataPlusReserve);
|
|
memset(pcfdataFirstPart,0,pcab->cbCFDataPlusReserve);
|
|
memset(pcffolder,0,pcab->cbCFFolderPlusReserve);
|
|
|
|
//** Keep looping through this function until the pending folder is empty
|
|
while (pfol->tf[iftfCFFOLDER].cb + pfol->tf[iftfCFDATA].cb) {
|
|
|
|
//** See how much space (if any) we need to trim from the end of the
|
|
// folder (and push into a new folder).
|
|
|
|
cbPredict = pcab->cbCFHeaderPlusReserve + // Predict total size
|
|
pcab->cbCFFolderPlusReserve + // and one more CFFOLDER
|
|
pcab->tf[ictfCFDATA].cb +
|
|
pcab->tf[ictfCFFILE].cb +
|
|
pcab->tf[ictfCFFOLDER].cb +
|
|
pfol->tf[iftfCFDATA].cb +
|
|
pfol->tf[iftfCFFOLDER].cb;
|
|
|
|
//** Account of previous/next disk/cabinet name space
|
|
if (pcab->achCabinetFirst[0] != '\0') {
|
|
cbPredict += 1+strlen(pcab->achCabinetFirst) +
|
|
1+strlen(pcab->achDiskFirst);
|
|
}
|
|
if (pcab->achCabinetNext[0] != '\0') {
|
|
cbPredict += 1+strlen(pcab->achCabinetNext) +
|
|
1+strlen(pcab->achDiskNext);
|
|
}
|
|
|
|
//** Estimate total bytes we'll move for status callbacks
|
|
cbTot = cbPredict*2; // *2 because we read *and* write
|
|
cbDone = 0; // Nothing moved so far
|
|
|
|
//** Tell client we have started
|
|
if (-1 == pfnProgress(statusFolder,cbDone,cbTot,pv)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
|
|
//** How much room is left over in the cabinet after this folder?
|
|
// NOTE: Can be negative for large folders!
|
|
cbSlack = (signed long)pcab->ccab.cb - cbPredict;
|
|
|
|
//** Create the folder temp files if we have to split this folder
|
|
if (!CrTempFiles(tf,2,pcab->pfntemp,pcab->perf)) {
|
|
goto error; // error already filled in
|
|
}
|
|
|
|
//** Do we have to create a new cabinet?
|
|
if (fCabinetFull = ((cbSlack < 0) || fGetNextCab)) {
|
|
//** Need to adjust for the new continuation strings
|
|
if (pcab->achNxtCabFirst[0] == 0) {
|
|
//** This is the first continuation for this folder;
|
|
// Remember current disk and cabinet names.
|
|
strcpy(pcab->achNxtDskFirst,pcab->ccab.szDisk);
|
|
strcpy(pcab->achNxtCabFirst,pcab->ccab.szCab);
|
|
}
|
|
|
|
//** Subtract out our guess at the next cab/disk name, since
|
|
// we're going to get the real size by calling the client to
|
|
// get their names.
|
|
|
|
//BUGBUG 09-Mar-1994 bens Should we move this down below a bit?
|
|
// cbPredict is passed to client to figure out when to switch to a new disk.
|
|
// by subtracting here, we are underestimating the total cabinet size by
|
|
// a little bit. Also, for the first cabinet, we're not going to have
|
|
// any next strings, so again we'll be underestimating.
|
|
//
|
|
if (pcab->achCabinetNext[0] != '\0') {
|
|
cbPredict -= (1+strlen(pcab->achCabinetNext) +
|
|
1+strlen(pcab->achDiskNext)) ;
|
|
}
|
|
|
|
pcab->ccabNext = pcab->ccab; // copy current ccab to a temp buffer
|
|
pcab->ccabNext.iCab++; // increment cabinet number
|
|
//** Get next cabinet information
|
|
pcab->cbCabinetEstimate = cbPredict; // Remember estimate
|
|
if (-1 == GetNextCab (&pcab->ccabNext,cbPredict,pv)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0);
|
|
return FALSE;
|
|
}
|
|
//** NOTE: Not all fields in pcab->ccabNext are respected!
|
|
// In particular, the reserve sizes are only used on
|
|
// the initial FCICreate call.
|
|
|
|
//** Save the strings for forward cabinet links
|
|
strcpy(pcab->achCabinetNext,pcab->ccabNext.szCab);
|
|
strcpy(pcab->achDiskNext,pcab->ccabNext.szDisk);
|
|
|
|
//** Adjust the predicted size based on changed strings
|
|
cbPredict += (1+strlen(pcab->ccabNext.szCab) +
|
|
1+strlen(pcab->ccabNext.szDisk));
|
|
|
|
cbSlack = (signed long)pcab->ccab.cb - cbPredict;
|
|
|
|
if (pcab->ccabNext.cb == 0) { // 0 implies MAX
|
|
pcab->ccabNext.cb = CB_MAX_DISK;
|
|
}
|
|
} // endif fCabinetFull
|
|
|
|
|
|
//** Now scan our data block file, to see how many blocks we'll have
|
|
// to chop of the end, and where to split the last block. We also
|
|
// need to know the begin and end address of that last block in
|
|
// uncompressed space.
|
|
|
|
//** Set the type of compression
|
|
pcffolder->typeCompress = pfol->typeCompress;
|
|
|
|
pcffolder->cCFData = 0; // Count data blocks as we emit them
|
|
pcffolder->coffCabStart = pcab->tf[ictfCFDATA].cb;
|
|
|
|
//** For now, the offset field will contain relative offset of the
|
|
// first data within the CFDATA field. When the CFDATA temp file
|
|
// is copied to the final CAB, each of these fields must have the
|
|
// base of the CFDATA added in.
|
|
cbDataLastEnd = 0; // keep track of uncompressed data size
|
|
|
|
//** Rewind the CFDDATA file for this folder
|
|
if (-1L == _lseek(pfol->tf[iftfCFDATA].hf,0L,SEEK_SET)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** First, we'll handle the CFDATA records. Some of them will go
|
|
// into the new cabinet, and if there is an overflow, the rest will
|
|
// end up in the replacement temp file. There may be a block split
|
|
// between them. Also, we'll calculate uoffLast, which describes
|
|
// the address in uncompressed space of the last *complete* CFDATA
|
|
// block in this file. This is used to decide which CFFILES will
|
|
// have to be duplicated in the continuation cabinet.
|
|
|
|
while (ReadCFDATAEntry(pcab,pcfdata,pfol)) {
|
|
cbDataLast = cbDataLastEnd; // Track of end of last whole block
|
|
cbDataLastEnd += pcfdata->cbData + pcab->cbCFDataPlusReserve;
|
|
cbPartial = 0; // Make status callback accurate
|
|
|
|
//** Does entire block fit in this cabinet?
|
|
if (cbDataLastEnd < (cbSlack + pfol->tf[iftfCFDATA].cb)) {
|
|
uoffLast += (UOFF)pcfdata->cbUncomp;
|
|
pcffolder->cCFData++; // Keep count of blocks we emit
|
|
|
|
//** Checksum must be calculated on two blocks and combined via
|
|
// CSUMCompute's seed parameter on the second call.
|
|
|
|
pcfdata->csum =
|
|
CSUMCompute(&(pcfdata->cbData),
|
|
pcab->cbCFDataPlusReserve - sizeof(pcfdata->csum),
|
|
CSUMCompute(pfol->pchCompr,pcfdata->cbData,0));
|
|
|
|
if (!WriteCount(&pcab->tf[ictfCFDATA],
|
|
pcfdata,
|
|
pcab->cbCFDataPlusReserve,
|
|
pcab->perf) ||
|
|
!WriteCount(&pcab->tf[ictfCFDATA],
|
|
pfol->pchCompr,
|
|
pcfdata->cbData,
|
|
pcab->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
} // endif full block that fits
|
|
//** Is this our partial block?
|
|
else if ((pcab->cbCFDataPlusReserve+cbDataLast)
|
|
< (cbSlack + pfol->tf[iftfCFDATA].cb)) {
|
|
cbPartial = (UINT)(cbSlack +
|
|
pfol->tf[iftfCFDATA].cb -
|
|
cbDataLast -
|
|
pcab->cbCFDataPlusReserve);
|
|
|
|
//** Partial block! Do not update uoffLast!
|
|
|
|
pcffolder->cCFData++; // keep count of blocks we emit
|
|
//** First partial data blocks have uncomp len == 0
|
|
pcfdataFirstPart->cbUncomp = 0;
|
|
pcfdataFirstPart->cbData = cbPartial; // Partial block size
|
|
|
|
pcfdata->csum = CSUMCompute(&(pcfdataFirstPart->cbData),
|
|
pcab->cbCFDataPlusReserve-sizeof(pcfdataFirstPart->csum),
|
|
CSUMCompute(pfol->pchCompr,cbPartial,0));
|
|
|
|
if (!WriteCount(&pcab->tf[ictfCFDATA],
|
|
pcfdataFirstPart,
|
|
pcab->cbCFDataPlusReserve,
|
|
pcab->perf)
|
|
|| !WriteCount(&pcab->tf[ictfCFDATA],
|
|
pfol->pchCompr,
|
|
cbPartial,
|
|
pcab->perf)) {
|
|
goto error; // error already filled in
|
|
}
|
|
|
|
//** Now save the remainder in our new CFDATA temp file
|
|
pcfdata->cbData -= cbPartial;
|
|
if (!WriteCount(&tf[iftfCFDATA],
|
|
pcfdata,
|
|
pcab->cbCFDataPlusReserve,
|
|
pfol->perf)
|
|
|| !WriteCount(&tf[iftfCFDATA],
|
|
&pfol->pchCompr[cbPartial],
|
|
pcfdata->cbData,
|
|
pfol->perf)) {
|
|
goto error; // error already filled in
|
|
}
|
|
}
|
|
else { //** this whole block gets pushed into temp file
|
|
if (!WriteCount(&tf[iftfCFDATA],
|
|
pcfdata,
|
|
pcab->cbCFDataPlusReserve,
|
|
pfol->perf)
|
|
|| !WriteCount(&tf[iftfCFDATA],
|
|
pfol->pchCompr,
|
|
pcfdata->cbData,
|
|
pfol->perf)) {
|
|
goto error; // error already filled in
|
|
}
|
|
}
|
|
|
|
cbDone += pcfdata->cbData + cbPartial;
|
|
|
|
//** Progress callback
|
|
if (-1 == pfnProgress(statusFolder,cbDone,cbTot,pv)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
} // end of while (ReadCFDATA)
|
|
|
|
//** Now we've completed the CFFOLDER entry and can write it to its
|
|
// temp file
|
|
pcab->cfheader.cFolders++;
|
|
if (!WriteCount(&pcab->tf[ictfCFFOLDER],
|
|
pcffolder,
|
|
pcab->cbCFFolderPlusReserve,
|
|
pcab->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
|
|
//** Now copy the relevant CFFILE entries to the cabinet temp files.
|
|
// For any files which are continued forward from this cabinet, we
|
|
// will have to adjust the iFolder field appropriately, and also put
|
|
// a copy of the CFFILE entry into the new pfol->tf[iftfCFFOLDER]
|
|
// file. This is also the loop where me make the file-placed
|
|
// callback.
|
|
|
|
if (-1L == _lseek(pfol->tf[iftfCFFOLDER].hf, 0L, SEEK_SET)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
while (ReadCFFILEEntry(pcab,pfol))
|
|
{
|
|
if (-1 == pcab->pfnfiledest(&pcab->ccab,
|
|
pcab->achName,
|
|
pcab->cffile.cbFile,
|
|
IS_CONTD_BACK(pcab->cffile.iFolder),
|
|
pv)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
|
|
//** If no PREV information, then set iFolder to current number
|
|
// within current cabinet.
|
|
if (!IS_CONTD_BACK(pcab->cffile.iFolder)) {
|
|
pcab->cffile.iFolder = pcab->iFolder;
|
|
}
|
|
|
|
//** If this file will continue forward, modify the iFolder field
|
|
if ( (pcab->cffile.uoffFolderStart +
|
|
(UOFF)pcab->cffile.cbFile) > uoffLast) {
|
|
if (IS_CONTD_BACK(pcab->cffile.iFolder)) {
|
|
pcab->cffile.iFolder = ifoldCONTINUED_PREV_AND_NEXT;
|
|
}
|
|
else {
|
|
pcab->cffile.iFolder = ifoldCONTINUED_TO_NEXT;
|
|
}
|
|
}
|
|
|
|
if (!WriteCount(&pcab->tf[ictfCFFILE],
|
|
&pcab->cffile,
|
|
sizeof(CFFILE),
|
|
pcab->perf)
|
|
|| !WritePszTmp(&pcab->tf[ictfCFFILE],
|
|
pcab->achName,
|
|
pcab->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
pcab->cfheader.cFiles++; // count CFFILE entries
|
|
|
|
//** Make sure continued-forward files also exist in the next cab
|
|
if (IS_CONTD_FORWARD(pcab->cffile.iFolder))
|
|
{
|
|
//** need to get rid of the 'continued-forward' setting
|
|
pcab->cffile.iFolder = ifoldCONTINUED_FROM_PREV;
|
|
|
|
if (!WriteCount(&tf[iftfCFFOLDER],
|
|
&pcab->cffile,
|
|
sizeof(CFFILE),
|
|
pfol->perf)
|
|
|| !WritePszTmp(&tf[iftfCFFOLDER],
|
|
pcab->achName,
|
|
pfol->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
}
|
|
|
|
} // end while loop reading CFFILE entries
|
|
|
|
//** Now nuke the original folder files, and copy the new ones
|
|
// into the folder structure.
|
|
|
|
if (!NukeTempFiles(&pfol->tf[iftfCFDATA],NUM_FOLDER_TF,pfol->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
|
|
//** Move continuation tempfiles to new folder
|
|
pfol->tf[iftfCFDATA] = tf[iftfCFDATA]; // Do structure assignment!
|
|
pfol->tf[iftfCFFOLDER] = tf[iftfCFFOLDER];
|
|
|
|
if (fCabinetFull) {
|
|
if (!FlushCab(pcab, // Write out cab, update ccab.cb!
|
|
pfnProgress,
|
|
&cbDone,
|
|
cbTot,
|
|
pv)) {
|
|
goto error; // error already filled in
|
|
}
|
|
if ((pcab->ccab.cb > 0) && // Space left on current disk
|
|
(pcab->ccab.iDisk == pcab->ccabNext.iDisk)) { // Not switching disks
|
|
Assert(fGetNextCab); // Should only happen if we were forced
|
|
pcab->ccabNext.cb = pcab->ccab.cb; // Use actual space left
|
|
}
|
|
pcab->ccab = pcab->ccabNext; // Set current cabinet from next cab
|
|
pcab->iFolder = 0; // start back at a new folder number
|
|
}
|
|
} // loop here until pending folder is exhausted.
|
|
|
|
//** Don't increment folder number if we forced a GetNextCab call!
|
|
if (!fGetNextCab) {
|
|
pcab->iFolder++;
|
|
}
|
|
//** Free resources
|
|
(*pcab->pfnfree)(pcfdata);
|
|
(*pcab->pfnfree)(pcfdataFirstPart);
|
|
(*pcab->pfnfree)(pcffolder);
|
|
return TRUE;
|
|
|
|
error:
|
|
//** Clean up and exit
|
|
if (!pcfdata) {
|
|
(*pcab->pfnfree)(pcfdata);
|
|
}
|
|
|
|
if (!pcfdataFirstPart) {
|
|
(*pcab->pfnfree)(pcfdataFirstPart);
|
|
}
|
|
|
|
if (!pcffolder) {
|
|
(*pcab->pfnfree)(pcffolder);
|
|
}
|
|
|
|
return FALSE;
|
|
} /* AddFolderToCabinet() */
|
|
|
|
|
|
/*** CabDestroy - flush buffers, close temp files
|
|
*
|
|
* Entry:
|
|
* hcab -- handle to cabinet context
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error filled in
|
|
*/
|
|
BOOL CabDestroy (HCAB hcab)
|
|
{
|
|
PCAB pcab;
|
|
BOOL retcode;
|
|
|
|
pcab = PCABfromHCAB (hcab);
|
|
AssertCAB (pcab); // verify structure signature
|
|
|
|
retcode = NukeTempFiles(pcab->tf,NUM_CAB_TF,pcab->perf);
|
|
//BUGBUG 15-Mar-1994 bens Need to destroy folder temp files, too?
|
|
|
|
ClearAssertSignature(pcab);
|
|
pcab->pfnfree(pcab); // free our data
|
|
return retcode; // Failure if retcode == FALSE
|
|
}
|
|
|
|
|
|
/*** ReadCFFILEEntry -- Read in a complete CFFILE entry
|
|
*
|
|
* Entry:
|
|
* pcab - location of cffile struct to fill in
|
|
* pfol - get the source file handle from here
|
|
*
|
|
* Exit success:
|
|
* pcab->cffile structure filled in
|
|
* pcab->achName filled in
|
|
* return code TRUE
|
|
*
|
|
* Exit failure:
|
|
* return code FALSE
|
|
* if error is other than EOF, then error struct filled in
|
|
*/
|
|
BOOL ReadCFFILEEntry(PCAB pcab,PFOL pfol)
|
|
{
|
|
int cbRead;
|
|
cbRead = _read(pfol->tf[iftfCFFOLDER].hf,
|
|
&pcab->cffile,
|
|
sizeof(CFFILE));
|
|
//BUGBUG 14-Apr-1994 bens Check for error from _read()!
|
|
|
|
if (cbRead == 0)
|
|
return FALSE; // no more CFFILE entries!
|
|
|
|
if (cbRead != sizeof (CFFILE)
|
|
|| !ReadPSZ(pfol->tf[iftfCFFOLDER].hf,
|
|
pcab->achName,
|
|
CB_MAX_FILENAME,
|
|
pfol->perf))
|
|
return FALSE; // abort if problem reading filename
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** ReadCFDATAEntry -- Read in a complete CFDATA entry
|
|
*
|
|
* Entry:
|
|
* pcab - Cabinet context
|
|
* pcfdata - CFDATA structure to fill in (Must have RESERVE space!)
|
|
* pfol - Folder structure for file handle, buf len, buffer
|
|
*
|
|
* Exit-Success:
|
|
* cfdata structure filled in
|
|
* return code TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* return FALSE
|
|
* Note: If return code is FALSE, but there is no error
|
|
* filled in, then it is a simple EOF which may not
|
|
* really be an error
|
|
*/
|
|
BOOL ReadCFDATAEntry(PCAB pcab,CFDATA *pcfdata,PFOL pfol)
|
|
{
|
|
//** Get CFDATA header
|
|
if (pcab->cbCFDataPlusReserve != (UINT)_read(pfol->tf[iftfCFDATA].hf,
|
|
pcfdata,
|
|
pcab->cbCFDataPlusReserve)) {
|
|
//BUGBUG 14-Apr-1994 bens Check for error from _read()!
|
|
return FALSE; // no more entries in file!
|
|
}
|
|
|
|
//** Make sure compressed data block size is not larger than when
|
|
// we created it the first time!
|
|
if (pcfdata->cbData > pfol->cbDstBuffer) {
|
|
Assert(0);
|
|
return 0; // bogus chunk size!
|
|
}
|
|
|
|
//** Get actual data
|
|
if ((pcfdata->cbData != (UINT)_read(pfol->tf[iftfCFDATA].hf,
|
|
pfol->pchCompr,
|
|
pcfdata->cbData))) {
|
|
ErfSetCodes(pfol->perf,FCIERR_TEMP_FILE,errno);
|
|
return FALSE; // Didn't get all data we asked for
|
|
}
|
|
|
|
return TRUE; // return success!
|
|
}
|
|
|
|
|
|
/*** ReadPSZ -- Read in a psz name
|
|
*
|
|
* Entry:
|
|
* hf - file handle to read from
|
|
* pb - buffer to load name into
|
|
* cb - size of buffer
|
|
* perf - error return structure
|
|
*
|
|
* Exit success:
|
|
* returns TRUE; name filled in
|
|
*
|
|
* Exit failure:
|
|
* returns FALSE; error filled in (read error, or string too long)
|
|
*/
|
|
BOOL ReadPSZ(int fh, char *pb, int cb, PERF perf)
|
|
{
|
|
char chLast;
|
|
int cbValue;
|
|
int cbRead;
|
|
long pos;
|
|
|
|
pos = _lseek(fh,0,SEEK_CUR); // Save current position
|
|
|
|
//** Read in enough to get longest possible value
|
|
cbRead = _read(fh,pb,cb);
|
|
if (cbRead <= 0) { // At EOF, or an error occured
|
|
ErfSetCodes(perf,FCIERR_TEMP_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Pick out just ASCIIZ string and adjust file position
|
|
chLast = pb[cb-1]; // Save last character
|
|
pb[cb-1] = '\0'; // Ensure terminated
|
|
cbValue = strlen(pb); // Get string length
|
|
if ( ((cbValue+1) >= cb) && (chLast != '\0')) {
|
|
//** String filled up buffer and was not null terminated in
|
|
// file, so something must be wrong.
|
|
ErfSetCodes(perf,FCIERR_TEMP_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
|
|
_lseek(fh,pos+cbValue+1,SEEK_SET); // Position to just past string
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** FlushCab - Combine the pcab-tf files into a real cabinet file
|
|
*
|
|
* Entry:
|
|
* pcab - cabinet context structure
|
|
* pfnProgress - status callback function
|
|
* pcbDone - pointer to bytes Done accumulator
|
|
* cbTot - total bytes in AddFolder operation
|
|
* pv - original caller's context for callbacks
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE; pcab->ccab.cb updated with size of cabinet desired
|
|
* by client.
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error struct filled in
|
|
*/
|
|
BOOL FlushCab(PCAB pcab,
|
|
PFNFCISTATUS pfnProgress,
|
|
unsigned long *pcbDone,
|
|
unsigned long cbTot,
|
|
void *pv)
|
|
{
|
|
USHORT cb;
|
|
long cbCabinetActual; // Actual cabinet size
|
|
long cbCabinetClient; // Size client wants us to use
|
|
USHORT cbTotal;
|
|
CFFOLDER *pcffolder=NULL;
|
|
COFF coffFolderBase;
|
|
int hf=-1;
|
|
CFRESERVE *pcfreserve;
|
|
char szDestFilename[CB_MAX_FILENAME]; // Full name of dest cab
|
|
|
|
//** Construct cabinet file name
|
|
strcpy(szDestFilename,pcab->ccab.szCabPath);
|
|
strcat(szDestFilename,pcab->ccab.szCab);
|
|
|
|
if (-1 == (hf=_open(szDestFilename,
|
|
_O_CREAT | _O_TRUNC | _O_BINARY | _O_RDWR,
|
|
_S_IREAD | _S_IWRITE ))) {
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** Allocate buffer for reading/writing CFFOLDER structure.
|
|
// Due to the RESERVE field, this is not a fixed sized buffer!
|
|
|
|
if (!(pcffolder = (*pcab->pfnalloc)(pcab->cbCFFolderPlusReserve))) {
|
|
goto error;
|
|
}
|
|
|
|
//** Begin computation of start of CFFOLDERs
|
|
coffFolderBase = pcab->cbCFHeaderPlusReserve;
|
|
|
|
//** Figure out existence of previous/next disk/cabinet info
|
|
if (pcab->achCabinetFirst[0] != '\0') {
|
|
pcab->cfheader.flags |= cfhdrPREV_CABINET; // Set previous flag
|
|
coffFolderBase += 1+strlen(pcab->achCabinetFirst) +
|
|
1+strlen(pcab->achDiskFirst);
|
|
}
|
|
if (pcab->achCabinetNext[0] != '\0') {
|
|
pcab->cfheader.flags |= cfhdrNEXT_CABINET; // Set previous flag
|
|
coffFolderBase += 1+strlen(pcab->achCabinetNext) +
|
|
1+strlen(pcab->achDiskNext);
|
|
}
|
|
|
|
//** Account for CFFOLDER structures
|
|
pcab->cfheader.coffFiles = coffFolderBase + pcab->tf[ictfCFFOLDER].cb;
|
|
|
|
//** Set overall file size
|
|
pcab->cfheader.cbCabinet = pcab->cfheader.coffFiles
|
|
+pcab->tf[ictfCFFILE].cb
|
|
+pcab->tf[ictfCFDATA].cb;
|
|
|
|
//** Make sure RESERVE flag is set correctly
|
|
if (pcab->cbReserveCF > 0) {
|
|
pcab->cfheader.flags |= cfhdrRESERVE_PRESENT; // Set flag
|
|
}
|
|
|
|
//** Store setID and cabinet number
|
|
pcab->cfheader.setID = pcab->setID;
|
|
pcab->cfheader.iCabinet = pcab->iCabinet;
|
|
pcab->iCabinet++; // Increment for next cabinet
|
|
|
|
//** write out CFHEADER
|
|
if (sizeof(CFHEADER) != _write(hf,&pcab->cfheader,sizeof(CFHEADER)))
|
|
{
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Write out CFRESERVE section (if present) for CFHEADER
|
|
if (pcab->cbReserveCF > 0) {
|
|
//** Fill in CFRESERVE
|
|
pcfreserve = (CFRESERVE *)pcab->abReserve;
|
|
pcfreserve->cbCFHeader = (USHORT)pcab->ccab.cbReserveCFHeader;
|
|
pcfreserve->cbCFFolder = (BYTE)pcab->ccab.cbReserveCFFolder;
|
|
pcfreserve->cbCFData = (BYTE)pcab->ccab.cbReserveCFData;
|
|
|
|
//** Write out CFRESERVE
|
|
if (sizeof(CFRESERVE) != _write(hf,pcab->abReserve,sizeof(CFRESERVE)))
|
|
{
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** Zero RESERVE data area
|
|
memset(pcab->abReserve,0,sizeof(pcab->abReserve));
|
|
cbTotal = (USHORT)(pcab->cbReserveCF - sizeof(CFRESERVE));
|
|
while (cbTotal > 0) {
|
|
//** Figure out how much to write
|
|
if (cbTotal > sizeof(pcab->abReserve)) {
|
|
cb = sizeof(pcab->abReserve);
|
|
}
|
|
else {
|
|
cb = cbTotal;
|
|
}
|
|
//** Write out buffer
|
|
if (cb != (USHORT)_write(hf,pcab->abReserve,cb))
|
|
{
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
goto error;
|
|
}
|
|
//** Adjust count of remaining bytes to write
|
|
cbTotal -= cb;
|
|
}
|
|
}
|
|
|
|
//** Write out previous/next cabinet/disk strings
|
|
if (pcab->cfheader.flags & cfhdrPREV_CABINET)
|
|
{
|
|
if (!WritePsz(hf,
|
|
pcab->achCabinetFirst,
|
|
pcab->perf)
|
|
||!WritePsz(hf,
|
|
pcab->achDiskFirst,
|
|
pcab->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
}
|
|
|
|
if (pcab->cfheader.flags & cfhdrNEXT_CABINET)
|
|
{
|
|
if (!WritePsz(hf,
|
|
pcab->achCabinetNext,
|
|
pcab->perf)
|
|
|| !WritePsz(hf,
|
|
pcab->achDiskNext,
|
|
pcab->perf)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
}
|
|
pcab->achCabinetNext[0]=0; // cancel next-cab links for now
|
|
pcab->achDiskNext[0]=0;
|
|
strcpy(pcab->achCabinetFirst,pcab->achNxtCabFirst); // update back pointers
|
|
strcpy(pcab->achDiskFirst,pcab->achNxtDskFirst);
|
|
|
|
//** Rewind CFFOLDER temporary file
|
|
if (-1 == _lseek(pcab->tf[ictfCFFOLDER].hf, 0L, SEEK_SET))
|
|
{
|
|
ErfSetCodes(pcab->perf,FCIERR_TEMP_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** We must manually copy the CFFOLDER records, because they need to
|
|
// have the start of the CFDATA area added to the coffCabStart fields.
|
|
|
|
while (pcab->cbCFFolderPlusReserve ==
|
|
(UINT)_read(pcab->tf[ictfCFFOLDER].hf,
|
|
pcffolder,
|
|
pcab->cbCFFolderPlusReserve)) {
|
|
//BUGBUG 14-Apr-1994 bens Check for error from _read()!
|
|
//** Adjust folder coffCabStart field
|
|
pcffolder->coffCabStart += (coffFolderBase +
|
|
pcab->tf[ictfCFFOLDER].cb + // size of CFFOLDERs
|
|
pcab->tf[ictfCFFILE].cb); // size of CFFILEs
|
|
if (pcab->cbCFFolderPlusReserve !=
|
|
(UINT)_write(hf,pcffolder,pcab->cbCFFolderPlusReserve)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Copy the CFFILE records to the cabinet file
|
|
if (!filecopy(hf,
|
|
pcab->tf[ictfCFFILE].hf,
|
|
pfnProgress,
|
|
pcbDone,
|
|
cbTot,
|
|
pv,
|
|
pcab->perf)) {
|
|
goto error;
|
|
}
|
|
|
|
//** Copy the CFDATA records to the cabinet file
|
|
if (!filecopy(hf,
|
|
pcab->tf[ictfCFDATA].hf,
|
|
pfnProgress,
|
|
pcbDone,
|
|
cbTot,
|
|
pv,
|
|
pcab->perf)) {
|
|
goto error;
|
|
}
|
|
|
|
//** Get actual size of cabinet for callback
|
|
cbCabinetActual = _lseek(hf,0,SEEK_END);
|
|
if (-1 == cbCabinetActual) {
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** Close the file
|
|
if (-1 == _close(hf)) {
|
|
ErfSetCodes(pcab->perf,FCIERR_CAB_FILE,errno);
|
|
goto error;
|
|
}
|
|
|
|
//** Get rid of the temporary files
|
|
if (!NukeTempFiles(pcab->tf,NUM_CAB_TF,pcab->perf)
|
|
|| !CrTempFiles (pcab->tf,NUM_CAB_TF,pcab->pfntemp,pcab->perf)) {
|
|
goto error; // error already filled in
|
|
}
|
|
|
|
//** Tell client true size of cabinet, get clients desired size
|
|
if (-1 == (cbCabinetClient = pfnProgress(statusCabinet,
|
|
pcab->cbCabinetEstimate,
|
|
cbCabinetActual,
|
|
pv))) {
|
|
ErfSetCodes(pcab->perf,FCIERR_USER_ABORT,errno);
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* Update maximum cabinet size using clients desired size.
|
|
*
|
|
* NOTE: Either we flushed the current cabinet because it had exceeded
|
|
* its maximum size (in which case the following code should
|
|
* reduce pcab->ccab.cb to 0), or because of an explicit
|
|
* FCIFlushCabinet() call (in which case there may be some
|
|
* space left.
|
|
*/
|
|
Assert(cbCabinetClient >= cbCabinetActual); // At least as much as actual
|
|
Assert(cbCabinetClient <= (long)pcab->ccab.cb); // No bigger than max
|
|
pcab->ccab.cb -= cbCabinetClient; // Shrink limit
|
|
|
|
//** Reinitialize header
|
|
InitCFHEADER(&pcab->cfheader);
|
|
|
|
//** Free resources
|
|
(*pcab->pfnfree)(pcffolder);
|
|
return TRUE;
|
|
|
|
error:
|
|
if (!pcffolder) {
|
|
(*pcab->pfnfree)(pcffolder);
|
|
}
|
|
|
|
//BUGBUG 15-Mar-1994 bens Close (and delete?) temp files?
|
|
|
|
if (hf != -1) { // File was created
|
|
_close(hf); // Close it
|
|
#ifndef ASSERT // Leave dregs behind in assert build to help diagnostics
|
|
_unlink(szDestFilename); // Delete it
|
|
#endif
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*** filecopy -- copy a cab temp file into the final cabinet
|
|
*
|
|
* Entry:
|
|
* hfdst - cab file handle
|
|
* hfsrc - temp file handle
|
|
* pfnProgress - status callback
|
|
* pcbDone - bytes done for status callback
|
|
* cbTot - total bytes for status callback
|
|
* pv - context for callback
|
|
* perf - error return structure
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE;
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error structure filled in
|
|
*
|
|
*/
|
|
BOOL filecopy(int hfdst,
|
|
int hfsrc,
|
|
PFNFCISTATUS pfnProgress,
|
|
unsigned long *pcbDone,
|
|
unsigned long cbTot,
|
|
void *pv,
|
|
PERF perf)
|
|
{
|
|
UINT cb;
|
|
#ifdef BIT16
|
|
#define cbFILE_COPY 1024 // Scrimp on memory in real-mode
|
|
#else
|
|
#define cbFILE_COPY 32768 // Pork out a little to speed up I/O
|
|
#endif
|
|
static xbuf[cbFILE_COPY];
|
|
|
|
if (-1 == _lseek(hfsrc, 0L, SEEK_SET)) {
|
|
ErfSetCodes(perf,FCIERR_TEMP_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
|
|
while (cb = _read(hfsrc,xbuf,sizeof(xbuf)))
|
|
{
|
|
if (cb != (unsigned)_write(hfdst,&xbuf,cb))
|
|
{
|
|
ErfSetCodes(perf,FCIERR_CAB_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
*pcbDone += cb;
|
|
if (-1 == pfnProgress(statusFolder,
|
|
*pcbDone,
|
|
cbTot,
|
|
pv)) {
|
|
ErfSetCodes(perf,FCIERR_USER_ABORT,errno);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
} /* filecopy() */
|
|
|
|
|
|
/*** InitCFHEADER -- initialize a CFHEADER to a fresh state
|
|
*
|
|
* Entry:
|
|
* pcfheader - pointer to an allocated CFHEADER
|
|
*
|
|
* Exit:
|
|
* all fields initialized
|
|
*/
|
|
void InitCFHEADER( CFHEADER *pcfheader)
|
|
{
|
|
pcfheader->sig = sigCFHEADER;
|
|
pcfheader->csumHeader = 0;
|
|
pcfheader->cbCabinet = 0;
|
|
pcfheader->version = verCF; // Set cabinet version number
|
|
pcfheader->cFolders = 0;
|
|
pcfheader->cFiles = 0;
|
|
pcfheader->flags = 0;
|
|
pcfheader->csumFolders = 0;
|
|
pcfheader->coffFiles = 0;
|
|
pcfheader->csumFiles = 0;
|
|
}
|
|
|
|
|
|
/*** WritePsz - Writes a psz string to cabinet output file
|
|
*
|
|
* Entry:
|
|
* hf - pointer to temp file structure
|
|
* psz - string to write
|
|
* perf - error structure
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error set to FCIERR_CAB_ERR
|
|
*/
|
|
BOOL WritePsz(int hf, char *psz,PERF perf)
|
|
{
|
|
if ((1+strlen(psz)) != (unsigned) _write(hf,psz,1+strlen(psz)))
|
|
{
|
|
ErfSetCodes(perf,FCIERR_CAB_FILE,errno);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|