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