- most of FDICopy is now implemented, although the actual decompression is not.

- "can" -> "do"
- a novella about a bug
- fix some memory leaks
This commit is contained in:
Gregory M. Turner 2003-06-13 23:15:55 +00:00 committed by Alexandre Julliard
parent a8ec5411aa
commit d1957c6092
1 changed files with 335 additions and 16 deletions

View File

@ -36,12 +36,69 @@
#include "winbase.h" #include "winbase.h"
#include "winerror.h" #include "winerror.h"
#include "fdi.h" #include "fdi.h"
#include "msvcrt/fcntl.h" /* _O_.* */
#include "cabinet.h" #include "cabinet.h"
#include "wine/debug.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(cabinet); WINE_DEFAULT_DEBUG_CHANNEL(cabinet);
struct fdi_cab {
struct fdi_cab *next; /* for making a list of cabinets */
LPCSTR filename; /* input name of cabinet */
int *fh; /* open file handle or NULL */
cab_off_t filelen; /* length of cabinet file */
struct fdi_cab *prevcab, *nextcab; /* multipart cabinet chains */
char *prevname, *nextname; /* and their filenames */
char *previnfo, *nextinfo; /* and their visible names */
struct fdi_folder *folders; /* first folder in this cabinet */
struct fdi_file *files; /* first file in this cabinet */
cab_UBYTE block_resv; /* reserved space in datablocks */
cab_UBYTE flags; /* header flags */
};
struct fdi_file {
struct fdi_file *next; /* next file in sequence */
struct fdi_folder *folder; /* folder that contains this file */
LPCSTR filename; /* output name of file */
int fh; /* open file handle or NULL */
cab_ULONG length; /* uncompressed length of file */
cab_ULONG offset; /* uncompressed offset in folder */
cab_UWORD index; /* magic index number of folder */
cab_UWORD time, date, attribs; /* MS-DOS time/date/attributes */
};
struct fdi_folder {
struct fdi_folder *next;
struct fdi_cab *cab[CAB_SPLITMAX]; /* cabinet(s) this folder spans */
cab_off_t offset[CAB_SPLITMAX]; /* offset to data blocks (32 bit) */
cab_UWORD comp_type; /* compression format/window size */
cab_ULONG comp_size; /* compressed size of folder */
cab_UBYTE num_splits; /* number of split blocks + 1 */
cab_UWORD num_blocks; /* total number of blocks */
struct fdi_file *contfile; /* the first split file */
};
/*
* ugh, well, this ended up being pretty damn silly...
* now that I've conceeded to build equivalent structures to struct cab.*,
* I should have just used those, or, better yet, unified the two... sue me.
* (Note to Microsoft: That's a joke. Please /don't/ actually sue me! -gmt).
* Nevertheless, I've come this far, it works, so I'm not gonna change it
* for now.
*/
/*
* this structure fills the gaps between what is available in a PFDICABINETINFO
* vs what is needed by FDICopy. Memory allocated for these becomes the responsibility
* of the caller to free. Yes, I am aware that this is totally, utterly inelegant.
*/
typedef struct {
char *prevname, *previnfo;
char *nextname, *nextinfo;
int folder_resv, header_resv;
} MORE_ISCAB_INFO, *PMORE_ISCAB_INFO;
/*********************************************************************** /***********************************************************************
* FDICreate (CABINET.20) * FDICreate (CABINET.20)
*/ */
@ -66,7 +123,7 @@ HFDI __cdecl FDICreate(
/* PONDERME: Certainly, we cannot tolerate a missing pfnalloc, as we call it just below. /* PONDERME: Certainly, we cannot tolerate a missing pfnalloc, as we call it just below.
pfnfree is tested as well, for symmetry. As for the rest, should we test these pfnfree is tested as well, for symmetry. As for the rest, should we test these
too? In a vacuum, I would say yes... but does Windows care? If not, then, I guess, too? In a vacuum, I would say yes... but does Windows care? If not, then, I guess,
neither can we.... */ neither do we.... */
if ((!pfnalloc) || (!pfnfree)) { if ((!pfnalloc) || (!pfnfree)) {
perf->erfOper = FDIERROR_NONE; perf->erfOper = FDIERROR_NONE;
perf->erfType = ERROR_BAD_ARGUMENTS; perf->erfType = ERROR_BAD_ARGUMENTS;
@ -187,20 +244,27 @@ char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize)
* *
* process the cabinet header in the style of FDIIsCabinet, but * process the cabinet header in the style of FDIIsCabinet, but
* without the sanity checks (and bug) * without the sanity checks (and bug)
*
* if pmii is non-null, some info not expressed in FDICABINETINFO struct
* will be stored there... responsibility to free the enclosed stuff is
* delegated to the caller in this case.
*/ */
BOOL FDI_read_entries( BOOL FDI_read_entries(
HFDI hfdi, HFDI hfdi,
INT_PTR hf, INT_PTR hf,
PFDICABINETINFO pfdici) PFDICABINETINFO pfdici,
PMORE_ISCAB_INFO pmii)
{ {
int num_folders, num_files, header_resv, folder_resv = 0; int num_folders, num_files, header_resv, folder_resv = 0;
LONG base_offset, cabsize; LONG base_offset, cabsize;
USHORT setid, cabidx, flags; USHORT setid, cabidx, flags;
cab_UBYTE buf[64], block_resv; cab_UBYTE buf[64], block_resv;
char *prevname, *previnfo, *nextname, *nextinfo; char *prevname = NULL, *previnfo = NULL, *nextname = NULL, *nextinfo = NULL;
TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici); TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici);
if (pmii) ZeroMemory(pmii, sizeof(MORE_ISCAB_INFO));
/* get basic offset & size info */ /* get basic offset & size info */
base_offset = FDI_getoffset(hfdi, hf); base_offset = FDI_getoffset(hfdi, hf);
@ -222,7 +286,7 @@ BOOL FDI_read_entries(
} }
/* read in the CFHEADER */ /* read in the CFHEADER */
if (!PFDI_READ(hfdi, hf, buf, cfhead_SIZEOF)) { if (PFDI_READ(hfdi, hf, buf, cfhead_SIZEOF) != cfhead_SIZEOF) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0; PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE; PFDI_INT(hfdi)->perf->fError = TRUE;
@ -281,8 +345,8 @@ BOOL FDI_read_entries(
/* read the reserved-sizes part of header, if present */ /* read the reserved-sizes part of header, if present */
if (flags & cfheadRESERVE_PRESENT) { if (flags & cfheadRESERVE_PRESENT) {
if (!PFDI_READ(hfdi, hf, buf, cfheadext_SIZEOF)) { if (PFDI_READ(hfdi, hf, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF) {
WARN("bunk reserve-sizes?\n"); ERR("bunk reserve-sizes?\n");
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ PFDI_INT(hfdi)->perf->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE; PFDI_INT(hfdi)->perf->fError = TRUE;
@ -290,7 +354,9 @@ BOOL FDI_read_entries(
} }
header_resv = EndGetI16(buf+cfheadext_HeaderReserved); header_resv = EndGetI16(buf+cfheadext_HeaderReserved);
if (pmii) pmii->header_resv = header_resv;
folder_resv = buf[cfheadext_FolderReserved]; folder_resv = buf[cfheadext_FolderReserved];
if (pmii) pmii->folder_resv = folder_resv;
block_resv = buf[cfheadext_DataReserved]; block_resv = buf[cfheadext_DataReserved];
if (header_resv > 60000) { if (header_resv > 60000) {
@ -314,19 +380,43 @@ BOOL FDI_read_entries(
PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ PFDI_INT(hfdi)->perf->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE; PFDI_INT(hfdi)->perf->fError = TRUE;
return FALSE; return FALSE;
} } else
if (pmii)
pmii->prevname = prevname;
else
PFDI_FREE(hfdi, prevname);
previnfo = FDI_read_string(hfdi, hf, cabsize); previnfo = FDI_read_string(hfdi, hf, cabsize);
if (previnfo) {
if (pmii)
pmii->previnfo = previnfo;
else
PFDI_FREE(hfdi, previnfo);
}
} }
if (flags & cfheadNEXT_CABINET) { if (flags & cfheadNEXT_CABINET) {
nextname = FDI_read_string(hfdi, hf, cabsize); nextname = FDI_read_string(hfdi, hf, cabsize);
if (!nextname) { if (!nextname) {
if ((flags & cfheadPREV_CABINET) && pmii) {
if (pmii->prevname) PFDI_FREE(hfdi, prevname);
if (pmii->previnfo) PFDI_FREE(hfdi, previnfo);
}
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ PFDI_INT(hfdi)->perf->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE; PFDI_INT(hfdi)->perf->fError = TRUE;
return FALSE; return FALSE;
} } else
if (pmii)
pmii->nextname = nextname;
else
PFDI_FREE(hfdi, nextname);
nextinfo = FDI_read_string(hfdi, hf, cabsize); nextinfo = FDI_read_string(hfdi, hf, cabsize);
if (nextinfo) {
if (pmii)
pmii->nextinfo = nextinfo;
else
PFDI_FREE(hfdi, nextinfo);
}
} }
/* we could process the whole cabinet searching for problems; /* we could process the whole cabinet searching for problems;
@ -377,7 +467,7 @@ BOOL __cdecl FDIIsCabinet(
SetLastError(ERROR_BAD_ARGUMENTS); SetLastError(ERROR_BAD_ARGUMENTS);
return FALSE; return FALSE;
} }
rv = FDI_read_entries(hfdi, hf, pfdici); rv = FDI_read_entries(hfdi, hf, pfdici, NULL);
if (rv) if (rv)
pfdici->hasnext = FALSE; /* yuck. duplicate apparent cabinet.dll bug */ pfdici->hasnext = FALSE; /* yuck. duplicate apparent cabinet.dll bug */
@ -396,9 +486,23 @@ BOOL __cdecl FDICopy(
PFNFDINOTIFY pfnfdin, PFNFDINOTIFY pfnfdin,
PFNFDIDECRYPT pfnfdid, PFNFDIDECRYPT pfnfdid,
void *pvUser) void *pvUser)
{ {
FIXME("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \ FDICABINETINFO fdici;
pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p): stub\n", FDINOTIFICATION fdin;
MORE_ISCAB_INFO mii;
int hf, i, idx;
char fullpath[MAX_PATH];
size_t pathlen, filenamelen;
char emptystring = '\0';
cab_UBYTE buf[64];
BOOL initialcab = TRUE;
struct fdi_folder *fol = NULL, *linkfol = NULL, *firstfol = NULL;
struct fdi_file *file = NULL, *linkfile = NULL, *firstfile = NULL;
struct fdi_cab _cab;
struct fdi_cab *cab = &_cab;
TRACE("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \
pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p)\n",
hfdi, pszCabinet, pszCabPath, flags, pfnfdin, pfnfdid, pvUser); hfdi, pszCabinet, pszCabPath, flags, pfnfdin, pfnfdid, pvUser);
if (!REALLY_IS_FDI(hfdi)) { if (!REALLY_IS_FDI(hfdi)) {
@ -406,8 +510,223 @@ BOOL __cdecl FDICopy(
return FALSE; return FALSE;
} }
SetLastError(ERROR_CALL_NOT_IMPLEMENTED); while (TRUE) { /* this loop executes one per. cabinet */
return FALSE; pathlen = (pszCabPath) ? strlen(pszCabPath) : 0;
filenamelen = (pszCabinet) ? strlen(pszCabinet) : 0;
/* slight overestimation here to save CPU cycles in the developer's brain */
if ((pathlen + filenamelen + 3) > MAX_PATH) {
ERR("MAX_PATH exceeded.\n");
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND;
PFDI_INT(hfdi)->perf->erfType = ERROR_FILE_NOT_FOUND;
PFDI_INT(hfdi)->perf->fError = TRUE;
SetLastError(ERROR_FILE_NOT_FOUND);
return FALSE;
}
/* paste the path and filename together */
idx = 0;
if (pathlen) {
for (i = 0; i < pathlen; i++) fullpath[idx++] = pszCabPath[i];
if (fullpath[idx - 1] != '\\') fullpath[idx++] = '\\';
}
if (filenamelen) for (i = 0; i < filenamelen; i++) fullpath[idx++] = pszCabinet[i];
fullpath[idx] = '\0';
TRACE("full cab path/file name: %s\n", debugstr_a(fullpath));
/* get a handle to the cabfile */
hf = PFDI_OPEN(hfdi, fullpath, _O_BINARY | _O_RDONLY | _O_SEQUENTIAL, 0);
if (hf == -1) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND;
PFDI_INT(hfdi)->perf->erfType = ERROR_FILE_NOT_FOUND;
PFDI_INT(hfdi)->perf->fError = TRUE;
SetLastError(ERROR_FILE_NOT_FOUND);
return FALSE;
}
/* check if it's really a cabfile. Note that this doesn't implement the bug */
if (!FDI_read_entries(hfdi, hf, &fdici, &mii)) {
ERR("FDIIsCabinet failed.\n");
PFDI_CLOSE(hfdi, hf);
return FALSE;
}
/* cabinet notification */
ZeroMemory(&fdin, sizeof(FDINOTIFICATION));
fdin.setID = fdici.setID;
fdin.iCabinet = fdici.iCabinet;
fdin.pv = pvUser;
fdin.psz1 = (mii.nextname) ? mii.nextname : &emptystring;
fdin.psz2 = (mii.nextinfo) ? mii.nextinfo : &emptystring;
fdin.psz3 = pszCabPath;
if (((*pfnfdin)(fdintCABINET_INFO, &fdin))) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT;
PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
goto bail_and_fail;
}
/* read folders */
for (i = 0; i < fdici.cFolders; i++) {
if (PFDI_READ(hfdi, hf, buf, cffold_SIZEOF) != cffold_SIZEOF) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
goto bail_and_fail;
}
if (mii.folder_resv > 0)
PFDI_SEEK(hfdi, hf, mii.folder_resv, SEEK_CUR);
fol = (struct fdi_folder *) PFDI_ALLOC(hfdi, sizeof(struct fdi_folder));
if (!fol) {
ERR("out of memory!\n");
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL;
PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY;
PFDI_INT(hfdi)->perf->fError = TRUE;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto bail_and_fail;
}
ZeroMemory(fol, sizeof(struct fdi_folder));
if (!firstfol) firstfol = fol;
fol->cab[0] = cab;
fol->offset[0] = (cab_off_t) EndGetI32(buf+cffold_DataOffset);
fol->num_blocks = EndGetI16(buf+cffold_NumBlocks);
fol->comp_type = EndGetI16(buf+cffold_CompType);
if (!linkfol)
cab->folders = fol;
else
linkfol->next = fol;
linkfol = fol;
}
/* read files */
for (i = 0; i < fdici.cFiles; i++) {
if (PFDI_READ(hfdi, hf, buf, cffile_SIZEOF) != cffile_SIZEOF) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
goto bail_and_fail;
}
file = (struct fdi_file *) PFDI_ALLOC(hfdi, sizeof(struct fdi_file));
if (!file) {
ERR("out of memory!\n");
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL;
PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY;
PFDI_INT(hfdi)->perf->fError = TRUE;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto bail_and_fail;
}
ZeroMemory(file, sizeof(struct fdi_file));
if (!firstfile) firstfile = file;
file->length = EndGetI32(buf+cffile_UncompressedSize);
file->offset = EndGetI32(buf+cffile_FolderOffset);
file->index = EndGetI16(buf+cffile_FolderIndex);
file->time = EndGetI16(buf+cffile_Time);
file->date = EndGetI16(buf+cffile_Date);
file->attribs = EndGetI16(buf+cffile_Attribs);
file->filename = FDI_read_string(hfdi, hf, fdici.cbCabinet);
if (!file->filename) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
goto bail_and_fail;
}
if (!linkfile)
cab->files = file;
else
linkfile->next = file;
linkfile = file;
}
/* partial file notification (do it just once for the first cabinet) */
if (initialcab && (firstfile->attribs & cffileCONTINUED_FROM_PREV) && (fdici.iCabinet != 0)) {
/* OK, more MS bugs to simulate here, I think. I don't have a huge spanning
* cabinet to test this theory on ATM, but here's the deal. The SDK says that we
* are supposed to notify the user of the filename and "disk name" (info) of
* the cabinet where the spanning file /started/. That would certainly be convenient
* for the consumer, who could decide to abort everything and try to start over with
* that cabinet so as not to create a front-truncated output file. Note that this
* task would be a horrible bitch from the implementor's (wine's) perspective: the
* information is associated nowhere with the file header and is not to be found in
* the cabinet header. So we would have to open the previous cabinet, and check
* if it contains a single spanning file that's continued from yet another prior cabinet,
* and so-on, until we find the beginning. Note that cabextract.c has code to do exactly
* this. Luckily, MS clearly didn't implement this logic, so we don't have to either.
* Watching the callbacks (and debugmsg +file) clearly shows that they don't open
* the preceeding cabinet -- and therefore, I deduce, there is NO WAY they could
* have implemented what's in the spec. Instead, they are obviously just returning
* the previous cabinet and it's info from the header of this cabinet. So we shall
* do the same. Of course, I could be missing something...
*/
ZeroMemory(&fdin, sizeof(FDINOTIFICATION));
fdin.pv = pvUser;
fdin.psz1 = (char *)firstfile->filename;
fdin.psz2 = (mii.prevname) ? mii.prevname : &emptystring;
fdin.psz3 = (mii.previnfo) ? mii.previnfo : &emptystring;
if (((*pfnfdin)(fdintPARTIAL_FILE, &fdin))) {
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT;
PFDI_INT(hfdi)->perf->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
goto bail_and_fail;
}
}
while (firstfol) {
fol = firstfol;
firstfol = firstfol->next;
PFDI_FREE(hfdi, fol);
}
while (firstfile) {
file = firstfile;
firstfile = firstfile->next;
PFDI_FREE(hfdi, file);
}
/* free the storage remembered by mii */
if (mii.nextname) PFDI_FREE(hfdi, mii.nextname);
if (mii.nextinfo) PFDI_FREE(hfdi, mii.nextinfo);
if (mii.prevname) PFDI_FREE(hfdi, mii.prevname);
if (mii.previnfo) PFDI_FREE(hfdi, mii.previnfo);
PFDI_CLOSE(hfdi, hf);
/* TODO: if (:?) */ return TRUE; /* else { ...; initialcab=FALSE; continue; } */
bail_and_fail: /* here we free ram before error returns */
while (firstfol) {
fol = firstfol;
firstfol = firstfol->next;
PFDI_FREE(hfdi, fol);
}
while (firstfile) {
file = firstfile;
firstfile = firstfile->next;
PFDI_FREE(hfdi, file);
}
/* free the storage remembered by mii */
if (mii.nextname) PFDI_FREE(hfdi, mii.nextname);
if (mii.nextinfo) PFDI_FREE(hfdi, mii.nextinfo);
if (mii.prevname) PFDI_FREE(hfdi, mii.prevname);
if (mii.previnfo) PFDI_FREE(hfdi, mii.previnfo);
PFDI_CLOSE(hfdi, hf);
return FALSE;
}
} }
/*********************************************************************** /***********************************************************************