cab_C_03.diff (WIP)
Gregory M. Turner
gmturner007 at ameritech.net
Mon Jun 9 02:59:26 CDT 2003
relative to cab_C_02.
This one adds most of the remaining infrastructure ... and still does not
implement actual decompression. Probably, it's best not to apply this until C_04,
which will hopefully finish all of the FDI API's except the undocumented Truncate.
License: LGPL (sorry)
ChangeLog:
* dlls/cabinet: fdi.c:
Greg Turner <gmturner007 at ameritech.net>
- most of FDICopy is now implemented, although the actual decompression is not.
- "can" -> "do"
- a novella about a bug
- fix some memory leaks
--
diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine/dlls/cabinet/fdi.c ./dlls/cabinet/fdi.c
--- ../wine/dlls/cabinet/fdi.c 2003-06-09 02:32:40.000000000 -0500
+++ ./dlls/cabinet/fdi.c 2003-06-09 02:28:48.000000000 -0500
@@ -34,6 +34,7 @@
#include "winbase.h"
#include "winerror.h"
#include "stdio.h"
+#include "msvcrt/fcntl.h" /* _O_.* */
#include "fdi.h"
#include "cabinet.h"
@@ -42,6 +43,62 @@
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)
*/
@@ -66,7 +123,7 @@
/* 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
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)) {
perf->erfOper = FDIERROR_NONE;
perf->erfType = ERROR_BAD_ARGUMENTS;
@@ -187,20 +244,27 @@
*
* process the cabinet header in the style of FDIIsCabinet, but
* 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(
- HFDI hfdi,
- INT_PTR hf,
- PFDICABINETINFO pfdici)
+ HFDI hfdi,
+ INT_PTR hf,
+ PFDICABINETINFO pfdici,
+ PMORE_ISCAB_INFO pmii)
{
int num_folders, num_files, header_resv, folder_resv = 0;
LONG base_offset, cabsize;
USHORT setid, cabidx, flags;
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);
+ if (pmii) ZeroMemory(pmii, sizeof(MORE_ISCAB_INFO));
+
/* get basic offset & size info */
base_offset = FDI_getoffset(hfdi, hf);
@@ -222,7 +286,7 @@
}
/* 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->erfType = 0;
PFDI_INT(hfdi)->perf->fError = TRUE;
@@ -281,8 +345,8 @@
/* read the reserved-sizes part of header, if present */
if (flags & cfheadRESERVE_PRESENT) {
- if (!PFDI_READ(hfdi, hf, buf, cfheadext_SIZEOF)) {
- WARN("bunk reserve-sizes?\n");
+ if (PFDI_READ(hfdi, hf, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF) {
+ ERR("bunk reserve-sizes?\n");
PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET;
PFDI_INT(hfdi)->perf->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE;
@@ -290,7 +354,9 @@
}
header_resv = EndGetI16(buf+cfheadext_HeaderReserved);
+ if (pmii) pmii->header_resv = header_resv;
folder_resv = buf[cfheadext_FolderReserved];
+ if (pmii) pmii->folder_resv = folder_resv;
block_resv = buf[cfheadext_DataReserved];
if (header_resv > 60000) {
@@ -314,19 +380,43 @@
PFDI_INT(hfdi)->perf->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE;
return FALSE;
- }
+ } else
+ if (pmii)
+ pmii->prevname = prevname;
+ else
+ PFDI_FREE(hfdi, prevname);
previnfo = FDI_read_string(hfdi, hf, cabsize);
+ if (previnfo) {
+ if (pmii)
+ pmii->previnfo = previnfo;
+ else
+ PFDI_FREE(hfdi, previnfo);
+ }
}
if (flags & cfheadNEXT_CABINET) {
nextname = FDI_read_string(hfdi, hf, cabsize);
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->erfType = 0; /* ? */
PFDI_INT(hfdi)->perf->fError = TRUE;
return FALSE;
- }
+ } else
+ if (pmii)
+ pmii->nextname = nextname;
+ else
+ PFDI_FREE(hfdi, nextname);
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;
@@ -377,7 +467,7 @@
SetLastError(ERROR_BAD_ARGUMENTS);
return FALSE;
}
- rv = FDI_read_entries(hfdi, hf, pfdici);
+ rv = FDI_read_entries(hfdi, hf, pfdici, NULL);
if (rv)
pfdici->hasnext = FALSE; /* yuck. duplicate apparent cabinet.dll bug */
@@ -396,9 +486,23 @@
PFNFDINOTIFY pfnfdin,
PFNFDIDECRYPT pfnfdid,
void *pvUser)
-{
- FIXME("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \
- pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p): stub\n",
+{
+ FDICABINETINFO fdici;
+ 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);
if (!REALLY_IS_FDI(hfdi)) {
@@ -406,8 +510,223 @@
return FALSE;
}
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
+ while (TRUE) { /* this loop executes one per. cabinet */
+ 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;
+ }
}
/***********************************************************************
--
"The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man." -- George Bernard Shaw
gmt
More information about the wine-patches
mailing list