cabinet: Store the data block headers in a memory list instead of on-disk.

Compute the checksums only when writing to the final file.
oldstable
Alexandre Julliard 2011-02-16 00:20:49 +01:00
parent afae86aa1e
commit ea22b17065
1 changed files with 195 additions and 198 deletions

View File

@ -132,6 +132,13 @@ struct file
char name[1];
};
struct data_block
{
struct list entry;
cab_UWORD compressed;
cab_UWORD uncompressed;
};
typedef struct
{
unsigned int magic;
@ -172,6 +179,7 @@ typedef struct
cab_ULONG estimatedCabinetSize;
struct list folders_list;
struct list files_list;
struct list blocks_list;
cab_ULONG folders_size;
cab_ULONG files_size;
cab_ULONG placed_files_size;
@ -322,16 +330,93 @@ static void free_folder( FCI_Int *fci, struct folder *folder )
fci->free( folder );
}
static struct data_block *add_data_block( FCI_Int *fci, cab_UWORD compressed, cab_UWORD uncompressed )
{
struct data_block *block = fci->alloc( sizeof(*block) );
if (!block)
{
set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
return NULL;
}
block->compressed = compressed;
block->uncompressed = uncompressed;
list_add_tail( &fci->blocks_list, &block->entry );
fci->data2.size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + compressed;
fci->cDataBlocks++;
return block;
}
static void free_data_block( FCI_Int *fci, struct data_block *block )
{
list_remove( &block->entry );
fci->free( block );
}
/* reset state for the next cabinet file once the current one has been flushed */
static void reset_cabinet( FCI_Int *fci )
{
struct folder *folder, *folder_next;
struct data_block *block, *block_next;
LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &fci->folders_list, struct folder, entry )
free_folder( fci, folder );
LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &fci->blocks_list, struct data_block, entry )
free_data_block( fci, block );
close_temp_file( fci, &fci->data2 );
fci->cFolders = 0;
fci->cFiles = 0;
fci->folders_size = 0;
fci->placed_files_size = 0;
fci->data2.size = 0;
}
static cab_ULONG fci_get_checksum( const void *pv, UINT cb, cab_ULONG seed )
{
cab_ULONG csum;
cab_ULONG ul;
int cUlong;
const BYTE *pb;
csum = seed;
cUlong = cb / 4;
pb = pv;
while (cUlong-- > 0) {
ul = *pb++;
ul |= (((cab_ULONG)(*pb++)) << 8);
ul |= (((cab_ULONG)(*pb++)) << 16);
ul |= (((cab_ULONG)(*pb++)) << 24);
csum ^= ul;
}
ul = 0;
switch (cb % 4) {
case 3:
ul |= (((ULONG)(*pb++)) << 16);
case 2:
ul |= (((ULONG)(*pb++)) << 8);
case 1:
ul |= *pb;
default:
break;
}
csum ^= ul;
return csum;
}
/* write all folders to disk and remove them from the list */
static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size, PFNFCISTATUS status_callback )
{
struct folder *folder, *next;
struct file *file;
struct folder *folder;
int err;
BOOL ret = TRUE;
CFFILE *cffile;
CFFOLDER *cffolder;
cab_ULONG file_size, folder_size = sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
cab_ULONG folder_size = sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
if (!(cffolder = fci->alloc( folder_size )))
{
@ -340,13 +425,6 @@ static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size,
}
memset( cffolder, 0, folder_size );
if (!(cffile = fci->alloc( sizeof(CFFILE) + CB_MAX_FILENAME )))
{
fci->free( cffolder );
set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
/* write the folders */
LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
{
@ -361,7 +439,26 @@ static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size,
}
}
/* write all the folders files */
fci->free( cffolder );
return ret;
}
/* write all the files to the cabinet file */
static BOOL write_files( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback )
{
cab_ULONG file_size;
struct folder *folder;
struct file *file;
int err;
BOOL ret = TRUE;
CFFILE *cffile;
if (!(cffile = fci->alloc( sizeof(CFFILE) + CB_MAX_FILENAME )))
{
set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
{
LIST_FOR_EACH_ENTRY( file, &folder->files_list, struct file, entry )
@ -398,31 +495,75 @@ static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size,
}
}
/* cleanup */
LIST_FOR_EACH_ENTRY_SAFE( folder, next, &fci->folders_list, struct folder, entry )
{
free_folder( fci, folder );
}
fci->free( cffolder );
fci->free( cffile );
return ret;
}
/* write all data blocks to the cabinet file */
static BOOL write_data_blocks( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback )
{
struct data_block *block;
int err, len;
CFDATA *cfdata;
void *data;
cab_UWORD header_size;
if (!fci->data_out) return TRUE;
if (fci->seek( fci->data2.handle, 0, SEEK_SET, &err, fci->pv ) != 0 )
{
set_error( fci, FCIERR_CAB_FILE, err );
return FALSE;
}
header_size = sizeof(CFDATA) + fci->ccab.cbReserveCFData;
cfdata = (CFDATA *)fci->data_out;
memset( cfdata, 0, header_size );
data = (char *)cfdata + header_size;
LIST_FOR_EACH_ENTRY( block, &fci->blocks_list, struct data_block, entry )
{
len = fci->read( fci->data2.handle, data, block->compressed, &err, fci->pv );
if (len != block->compressed) return FALSE;
cfdata->cbData = fci_endian_uword( block->compressed );
cfdata->cbUncomp = fci_endian_uword( block->uncompressed );
cfdata->csum = fci_endian_ulong( fci_get_checksum( &cfdata->cbData,
header_size - FIELD_OFFSET( CFDATA, cbData ),
fci_get_checksum( data, len, 0 )));
fci->statusFolderCopied += len;
len += header_size;
if (fci->write( handle, fci->data_out, len, &err, fci->pv ) != len)
{
set_error( fci, FCIERR_CAB_FILE, err );
return FALSE;
}
if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv) == -1)
{
set_error( fci, FCIERR_USER_ABORT, 0 );
return FALSE;
}
}
return TRUE;
}
/* write the cabinet file to disk */
static INT_PTR write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
static BOOL write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
{
char filename[CB_MAX_CAB_PATH + CB_MAX_CABINET_NAME];
int err;
char *ptr;
INT_PTR handle;
CFHEADER *cfheader;
cab_UWORD flags = 0;
cab_ULONG header_size = get_header_size( fci );
CFHEADER *cfheader;
char *ptr;
cab_ULONG total_size = header_size + fci->folders_size + fci->placed_files_size + fci->data2.size;
if (!(cfheader = fci->alloc( header_size )))
{
set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
return -1;
return FALSE;
}
memset( cfheader, 0, header_size );
@ -432,8 +573,7 @@ static INT_PTR write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
flags |= cfheadRESERVE_PRESENT;
memcpy( cfheader->signature, "!CAB", 4 );
cfheader->cbCabinet = fci_endian_ulong( header_size + fci->folders_size +
fci->placed_files_size + fci->data2.size );
cfheader->cbCabinet = fci_endian_ulong( total_size );
cfheader->coffFiles = fci_endian_ulong( header_size + fci->folders_size );
cfheader->versionMinor = 3;
cfheader->versionMajor = 1;
@ -486,7 +626,7 @@ static INT_PTR write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
{
set_error( fci, FCIERR_CAB_FILE, err );
fci->free( cfheader );
return -1;
return FALSE;
}
if (fci->write( handle, cfheader, header_size, &err, fci->pv ) != header_size)
@ -498,16 +638,33 @@ static INT_PTR write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
/* add size of header size of all CFFOLDERs and size of all CFFILEs */
header_size += fci->placed_files_size + fci->folders_size;
if (!write_folders( fci, handle, header_size, status_callback )) goto failed;
if (!write_files( fci, handle, status_callback )) goto failed;
if (!write_data_blocks( fci, handle, status_callback )) goto failed;
/* FIXME: write data blocks here */
/* update the signature */
if (fci->seek( handle, 0, SEEK_SET, &err, fci->pv ) != 0 )
{
set_error( fci, FCIERR_CAB_FILE, err );
goto failed;
}
memcpy( cfheader->signature, "MSCF", 4 );
if (fci->write( handle, cfheader->signature, 4, &err, fci->pv ) != 4)
{
set_error( fci, FCIERR_CAB_FILE, err );
goto failed;
}
fci->close( handle, &err, fci->pv );
fci->free( cfheader );
return handle;
reset_cabinet( fci );
status_callback( statusCabinet, fci->estimatedCabinetSize, total_size, fci->pv );
return TRUE;
failed:
fci->close( handle, &err, fci->pv );
fci->delete( filename, &err, fci->pv );
fci->free( cfheader );
return -1;
return FALSE;
}
/* add all pending files to folder */
@ -701,6 +858,7 @@ HFCI __cdecl FCICreate(
list_init( &p_fci_internal->folders_list );
list_init( &p_fci_internal->files_list );
list_init( &p_fci_internal->blocks_list );
memcpy(p_fci_internal->szPrevCab, pccab->szCab, CB_MAX_CABINET_NAME);
memcpy(p_fci_internal->szPrevDisk, pccab->szDisk, CB_MAX_DISK_NAME);
@ -810,48 +968,11 @@ static BOOL fci_flush_data_block (FCI_Int *p_fci_internal, int* err,
static cab_ULONG fci_get_checksum(const void *pv, UINT cb, CHECKSUM seed)
{
cab_ULONG csum;
cab_ULONG ul;
int cUlong;
const BYTE *pb;
csum = seed;
cUlong = cb / 4;
pb = pv;
while (cUlong-- > 0) {
ul = *pb++;
ul |= (((cab_ULONG)(*pb++)) << 8);
ul |= (((cab_ULONG)(*pb++)) << 16);
ul |= (((cab_ULONG)(*pb++)) << 24);
csum ^= ul;
}
ul = 0;
switch (cb % 4) {
case 3:
ul |= (((ULONG)(*pb++)) << 16);
case 2:
ul |= (((ULONG)(*pb++)) << 8);
case 1:
ul |= *pb;
default:
break;
}
csum ^= ul;
return csum;
} /* end of fci_get_checksum */
static BOOL fci_flushfolder_copy_cfdata(FCI_Int *p_fci_internal, char* buffer, UINT cbReserveCFData,
PFNFCISTATUS pfnfcis, int* err, struct temp_file *data1new,
cab_ULONG* payload)
{
struct data_block *block;
cab_ULONG read_result;
CFDATA* pcfdata=(CFDATA*)buffer;
BOOL split_block=FALSE;
@ -957,35 +1078,7 @@ static BOOL fci_flushfolder_copy_cfdata(FCI_Int *p_fci_internal, char* buffer, U
return FALSE;
}
/* set little endian */
pcfdata->cbData=fci_endian_uword(pcfdata->cbData);
pcfdata->cbUncomp=fci_endian_uword(pcfdata->cbUncomp);
/* get checksum and write to cfdata.csum */
pcfdata->csum = fci_get_checksum( &(pcfdata->cbData),
sizeof(CFDATA)+cbReserveCFData -
sizeof(pcfdata->csum), fci_get_checksum( p_fci_internal->data_out, /*buffer*/
pcfdata->cbData, 0 ) );
/* set little endian */
pcfdata->csum=fci_endian_ulong(pcfdata->csum);
/* write cfdata with checksum to p_fci_internal->data2.handle */
if( p_fci_internal->write( p_fci_internal->data2.handle, /* file handle */
buffer, /* memory buffer */
sizeof(CFDATA)+cbReserveCFData, /* number of bytes to copy */
err, p_fci_internal->pv) != sizeof(CFDATA)+cbReserveCFData ) {
set_error( p_fci_internal, FCIERR_TEMP_FILE, ERROR_WRITE_FAULT );
return FALSE;
}
/* TODO error handling of err */
p_fci_internal->data2.size += sizeof(CFDATA)+cbReserveCFData;
/* reset little endian */
pcfdata->cbData=fci_endian_uword(pcfdata->cbData);
pcfdata->cbUncomp=fci_endian_uword(pcfdata->cbUncomp);
pcfdata->csum=fci_endian_ulong(pcfdata->csum);
if (!(block = add_data_block( p_fci_internal, pcfdata->cbData, pcfdata->cbUncomp ))) return FALSE;
/* write compressed data into p_fci_internal->data2.handle */
if( p_fci_internal->write( p_fci_internal->data2.handle, /* file handle */
@ -997,8 +1090,6 @@ static BOOL fci_flushfolder_copy_cfdata(FCI_Int *p_fci_internal, char* buffer, U
}
/* TODO error handling of err */
p_fci_internal->data2.size += pcfdata->cbData;
++(p_fci_internal->cDataBlocks);
p_fci_internal->statusFolderCopied += pcfdata->cbData;
(*payload)+=pcfdata->cbUncomp;
/* if cabinet size too large and data has been split */
@ -1365,11 +1456,8 @@ static BOOL fci_flush_cabinet( FCI_Int *p_fci_internal,
PFNFCIGETNEXTCABINET pfnfcignc,
PFNFCISTATUS pfnfcis)
{
int err;
cab_ULONG total_size, read_result=0;
int handleCABINET; /* file handle for cabinet */
cab_ULONG read_result=0;
BOOL returntrue=FALSE;
char signature[] = "MSCF";
/* TODO test if fci_flush_cabinet really aborts if there was no FCIAddFile */
@ -1396,107 +1484,11 @@ static BOOL fci_flush_cabinet( FCI_Int *p_fci_internal,
return FALSE;
}
total_size = /* size of the cabinet file in bytes */
get_header_size( p_fci_internal ) +
p_fci_internal->folders_size +
p_fci_internal->placed_files_size +
p_fci_internal->data2.size;
if (total_size > p_fci_internal->ccab.cb)
{
set_error( p_fci_internal, FCIERR_NONE, ERROR_MORE_DATA );
return FALSE;
}
/* create the cabinet */
handleCABINET = write_cabinet( p_fci_internal, pfnfcis );
if (handleCABINET == -1) return FALSE;
/* set seek of p_fci_internal->data2.handle to 0 */
if( p_fci_internal->seek(p_fci_internal->data2.handle,
0, SEEK_SET, &err, p_fci_internal->pv) != 0 ) {
/* wrong return value */
set_error( p_fci_internal, FCIERR_NONE, ERROR_SEEK );
return FALSE;
}
/* TODO error handling of err */
/* reset the number of folders for the next cabinet */
p_fci_internal->cFolders=0;
/* reset the number of files for the next cabinet */
p_fci_internal->cFiles=0;
/* while not all CFDATA structures have been copied to the cabinet do */
if (p_fci_internal->data_out) while(!FALSE) {
/* REUSE the variable read_result AGAIN */
/* REUSE the buffer p_fci_internal->data_out AGAIN */
/* read a block from p_fci_internal->data2.handle */
read_result = p_fci_internal->read( p_fci_internal->data2.handle /* handle */,
p_fci_internal->data_out, /* memory buffer */
CB_MAX_CHUNK, /* number of bytes to copy */
&err, p_fci_internal->pv);
if( read_result == 0 ) break; /* ALL CFDATA structures have been copied */
/* TODO error handling of err */
/* write the block to the cabinet file */
if( p_fci_internal->write( handleCABINET, /* file handle */
p_fci_internal->data_out, /* memory buffer */
read_result, /* number of bytes to copy */
&err, p_fci_internal->pv) != read_result ) {
/* write error */
set_error( p_fci_internal, FCIERR_CAB_FILE, ERROR_WRITE_FAULT );
return FALSE;
}
/* TODO error handling of err */
p_fci_internal->statusFolderCopied += read_result;
/* report status with pfnfcis about copied size of folder */
if( (*pfnfcis)(statusFolder,
p_fci_internal->statusFolderCopied, /* length of copied blocks */
p_fci_internal->statusFolderTotal, /* total size of folder */
p_fci_internal->pv) == -1) {
/* set error code and abort */
set_error( p_fci_internal, FCIERR_USER_ABORT, 0 );
return FALSE;
}
} /* END OF while */
/* set seek of the cabinet file to 0 */
if( p_fci_internal->seek( handleCABINET,
0, SEEK_SET, &err, p_fci_internal->pv) != 0 ) {
/* wrong return value */
set_error( p_fci_internal, FCIERR_NONE, ERROR_SEEK );
return FALSE;
}
/* TODO error handling of err */
/* write the signature "MSCF" into the cabinet file */
if( p_fci_internal->write( handleCABINET, /* file handle */
signature, /* memory buffer */
4, /* number of bytes to copy */
&err, p_fci_internal->pv) != 4 ) {
/* wrtie error */
set_error( p_fci_internal, FCIERR_CAB_FILE, ERROR_WRITE_FAULT );
return FALSE;
}
/* TODO error handling of err */
/* close the cabinet file */
p_fci_internal->close(handleCABINET,&err,p_fci_internal->pv);
/* TODO error handling of err */
close_temp_file( p_fci_internal, &p_fci_internal->data2 );
p_fci_internal->data2.size = 0;
p_fci_internal->placed_files_size = 0;
p_fci_internal->folders_size = 0;
if (!write_cabinet( p_fci_internal, pfnfcis )) return FALSE;
if (!create_temp_file( p_fci_internal, &p_fci_internal->data2 )) return FALSE;
/* report status with pfnfcis about copied size of folder */
(*pfnfcis)(statusCabinet,
p_fci_internal->estimatedCabinetSize, /* estimated cabinet file size */
total_size /* real cabinet file size */, p_fci_internal->pv);
p_fci_internal->fPrevCab=TRUE;
/* The sections szPrevCab and szPrevDisk are not being updated, because */
/* MS CABINET.DLL always puts the first cabinet name and disk into them */
@ -2007,6 +1999,7 @@ BOOL __cdecl FCIDestroy(HFCI hfci)
{
struct folder *folder, *folder_next;
struct file *file, *file_next;
struct data_block *block, *block_next;
FCI_Int *p_fci_internal = get_fci_ptr( hfci );
if (!p_fci_internal) return FALSE;
@ -2023,6 +2016,10 @@ BOOL __cdecl FCIDestroy(HFCI hfci)
{
free_file( p_fci_internal, file );
}
LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &p_fci_internal->blocks_list, struct data_block, entry )
{
free_data_block( p_fci_internal, block );
}
close_temp_file( p_fci_internal, &p_fci_internal->data1 );
close_temp_file( p_fci_internal, &p_fci_internal->data2 );