Archivers can now specify whether an archive definitely was intended for them.
authorRyan C. Gordon <icculus@icculus.org>
Mon, 14 Aug 2017 02:28:00 -0400
changeset 1571 fbd9698219c1
parent 1570 3bd8afc43ee3
child 1572 5c091e04ac46
Archivers can now specify whether an archive definitely was intended for them.

So if a zip file goes to the zip archiver but is corrupted, the system can now
know not to bother trying other archivers once the zip archiver has had a shot
at it, and just as important: it can report the real error from that archiver
instead of a generic "unsupported."
src/physfs.c
src/physfs.h
src/physfs_archiver_7z.c
src/physfs_archiver_dir.c
src/physfs_archiver_grp.c
src/physfs_archiver_hog.c
src/physfs_archiver_iso9660.c
src/physfs_archiver_mvl.c
src/physfs_archiver_qpak.c
src/physfs_archiver_slb.c
src/physfs_archiver_vdf.c
src/physfs_archiver_wad.c
src/physfs_archiver_zip.c
--- a/src/physfs.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs.c	Mon Aug 14 02:28:00 2017 -0400
@@ -838,7 +838,7 @@
 
 
 static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs,
-                             const char *d, int forWriting)
+                             const char *d, int forWriting, int *_claimed)
 {
     DirHandle *retval = NULL;
     void *opaque = NULL;
@@ -846,7 +846,7 @@
     if (io != NULL)
         BAIL_IF_ERRPASS(!io->seek(io, 0), NULL);
 
-    opaque = funcs->openArchive(io, d, forWriting);
+    opaque = funcs->openArchive(io, d, forWriting, _claimed);
     if (opaque != NULL)
     {
         retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
@@ -871,14 +871,16 @@
     PHYSFS_Archiver **i;
     const char *ext;
     int created_io = 0;
+    int claimed = 0;
+    PHYSFS_ErrorCode errcode;
 
     assert((io != NULL) || (d != NULL));
 
     if (io == NULL)
     {
         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
-        retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
-        if (retval != NULL)
+        retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting, &claimed);
+        if (retval || claimed)
             return retval;
 
         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
@@ -890,30 +892,32 @@
     if (ext != NULL)
     {
         /* Look for archivers with matching file extensions first... */
-        for (i = archivers; (*i != NULL) && (retval == NULL); i++)
+        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
         {
             if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0)
-                retval = tryOpenDir(io, *i, d, forWriting);
+                retval = tryOpenDir(io, *i, d, forWriting, &claimed);
         } /* for */
 
         /* failing an exact file extension match, try all the others... */
-        for (i = archivers; (*i != NULL) && (retval == NULL); i++)
+        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
         {
             if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0)
-                retval = tryOpenDir(io, *i, d, forWriting);
+                retval = tryOpenDir(io, *i, d, forWriting, &claimed);
         } /* for */
     } /* if */
 
     else  /* no extension? Try them all. */
     {
-        for (i = archivers; (*i != NULL) && (retval == NULL); i++)
-            retval = tryOpenDir(io, *i, d, forWriting);
+        for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++)
+            retval = tryOpenDir(io, *i, d, forWriting, &claimed);
     } /* else */
 
+    errcode = currentErrorCode();
+
     if ((!retval) && (created_io))
         io->destroy(io);
 
-    BAIL_IF(!retval, PHYSFS_ERR_UNSUPPORTED, NULL);
+    BAIL_IF(!retval, claimed ? errcode : PHYSFS_ERR_UNSUPPORTED, NULL);
     return retval;
 } /* openDirectory */
 
--- a/src/physfs.h	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs.h	Mon Aug 14 02:28:00 2017 -0400
@@ -3492,20 +3492,30 @@
     /**
      * \brief Open an archive provided by (io).
      *
-     * This is where resources are allocated and data is parsed when mounting an archive.
-     *  (name) is a filename associated with (io), but doesn't necessarily
+     * This is where resources are allocated and data is parsed when mounting
+     *  an archive.
+     * (name) is a filename associated with (io), but doesn't necessarily
      *  map to anything, let alone a real filename. This possibly-
      *  meaningless name is in platform-dependent notation.
      * (forWrite) is non-zero if this is to be used for
      *  the write directory, and zero if this is to be used for an
      *  element of the search path.
-     * Return NULL on failure. We ignore any error code you set here;
-     *  when PHYSFS_mount() returns, the error will be PHYSFS_ERR_UNSUPPORTED
-     *  (no Archivers could handle this data).  // !!! FIXME-3.0: yeah?
-     *  Returns non-NULL on success. The pointer returned will be
+     * (claimed) should be set to 1 if this is definitely an archive your
+     *  archiver implementation can handle, even if it fails. We use to
+     *  decide if we should stop trying other archivers if you fail to open
+     *  it. For example: the .zip archiver will set this to 1 for something
+     *  that's got a .zip file signature, even if it failed because the file
+     *  was also truncated. No sense in trying other archivers here, we
+     *  already tried to handle it with the appropriate implementation!.
+     * Return NULL on failure and set (claimed) appropriately. If no archiver
+     *  opened the archive or set (claimed), PHYSFS_mount() will report
+     *  PHYSFS_ERR_UNSUPPORTED. Otherwise, it will report the error from the
+     *  archiver that claimed the data through (claimed).
+     * Return non-NULL on success. The pointer returned will be
      *  passed as the "opaque" parameter for later calls.
      */
-    void *(*openArchive)(PHYSFS_Io *io, const char *name, int forWrite);
+    void *(*openArchive)(PHYSFS_Io *io, const char *name,
+                         int forWrite, int *claimed);
 
     /**
      * \brief List all files in (dirname).
--- a/src/physfs_archiver_7z.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_7z.c	Mon Aug 14 02:28:00 2017 -0400
@@ -210,14 +210,23 @@
 } /* SZIP_closeArchive */
 
 
-static void *SZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *SZIP_openArchive(PHYSFS_Io *io, const char *name,
+                              int forWriting, int *claimed)
 {
+    static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C };
     SZIPLookToRead stream;
     ISzAlloc *alloc = &SZIP_SzAlloc;
     SZIPinfo *info = NULL;
     SRes rc;
+    PHYSFS_uint8 sig[6];
+    PHYSFS_sint64 pos;
 
     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
+    pos = io->tell(io);
+    BAIL_IF_ERRPASS(pos == -1, NULL);
+    BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL);
+    *claimed = (memcmp(sig, wantedsig, 6) == 0);
+    BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
 
     info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo));
     BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
--- a/src/physfs_archiver_dir.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_dir.c	Mon Aug 14 02:28:00 2017 -0400
@@ -37,7 +37,8 @@
 
 
 
-static void *DIR_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *DIR_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_Stat st;
     const char dirsep = __PHYSFS_platformDirSeparator;
@@ -50,6 +51,7 @@
     if (st.filetype != PHYSFS_FILETYPE_DIRECTORY)
         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
 
+    *claimed = 1;
     retval = allocator.Malloc(namelen + seplen + 1);
     BAIL_IF(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
 
--- a/src/physfs_archiver_grp.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_grp.c	Mon Aug 14 02:28:00 2017 -0400
@@ -56,7 +56,8 @@
 } /* grpLoadEntries */
 
 
-static void *GRP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *GRP_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint8 buf[12];
     PHYSFS_uint32 count = 0;
@@ -70,6 +71,8 @@
     if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0)
         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
 
+    *claimed = 1;
+
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
     count = PHYSFS_swapULE32(count);
 
--- a/src/physfs_archiver_hog.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_hog.c	Mon Aug 14 02:28:00 2017 -0400
@@ -61,7 +61,8 @@
 } /* hogLoadEntries */
 
 
-static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *HOG_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint8 buf[3];
     void *unpkarc = NULL;
@@ -71,6 +72,8 @@
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL);
     BAIL_IF(memcmp(buf, "DHF", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
 
+    *claimed = 1;
+
     unpkarc = UNPK_openArchive(io);
     BAIL_IF_ERRPASS(!unpkarc, NULL);
 
--- a/src/physfs_archiver_iso9660.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_iso9660.c	Mon Aug 14 02:28:00 2017 -0400
@@ -215,11 +215,11 @@
 
 
 static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos,
-                                 PHYSFS_uint64 *_rootlen, int *_joliet)
+                                 PHYSFS_uint64 *_rootlen, int *_joliet,
+                                 int *_claimed)
 {
     PHYSFS_uint64 pos = 32768; /* start at the Primary Volume Descriptor */
     int found = 0;
-    int first = 1;
     int done = 0;
 
     *_joliet = 0;
@@ -242,13 +242,13 @@
         BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &type, 1), 0);
         BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &identifier, 5), 0);
 
-        if (memcmp(identifier, "CD001", 5) != 0)
+        if (memcmp(identifier, "CD001", 5) != 0)  /* maybe not an iso? */
         {
-            BAIL_IF(first, PHYSFS_ERR_UNSUPPORTED, 0);  /* maybe not an iso? */
+            BAIL_IF(!*_claimed, PHYSFS_ERR_UNSUPPORTED, 0);
             continue;  /* just skip this one */
         } /* if */
 
-        first = 0; /* okay, this is probably an iso. */
+        *_claimed = 1; /* okay, this is probably an iso. */
 
         BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, 1), 0);  /* version */
         BAIL_IF(version != 1, PHYSFS_ERR_UNSUPPORTED, 0);
@@ -320,7 +320,8 @@
 } /* parseVolumeDescriptor */
 
 
-static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, int forWriting)
+static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename,
+                                 int forWriting, int *claimed)
 {
     PHYSFS_uint64 rootpos = 0;
     PHYSFS_uint64 len = 0;
@@ -330,7 +331,9 @@
     assert(io != NULL);  /* shouldn't ever happen. */
 
     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
-    BAIL_IF_ERRPASS(!parseVolumeDescriptor(io, &rootpos, &len, &joliet), NULL);
+
+    if (!parseVolumeDescriptor(io, &rootpos, &len, &joliet, claimed))
+        return NULL;
 
     unpkarc = UNPK_openArchive(io);
     BAIL_IF_ERRPASS(!unpkarc, NULL);
--- a/src/physfs_archiver_mvl.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_mvl.c	Mon Aug 14 02:28:00 2017 -0400
@@ -53,7 +53,8 @@
 } /* mvlLoadEntries */
 
 
-static void *MVL_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *MVL_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint8 buf[4];
     PHYSFS_uint32 count = 0;
@@ -63,8 +64,10 @@
     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL);
     BAIL_IF(memcmp(buf, "DMVL", 4) != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
+
+    *claimed = 1;
+
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
-
     count = PHYSFS_swapULE32(count);
 
     unpkarc = UNPK_openArchive(io);
--- a/src/physfs_archiver_qpak.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_qpak.c	Mon Aug 14 02:28:00 2017 -0400
@@ -56,7 +56,8 @@
 } /* qpakLoadEntries */
 
 
-static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *QPAK_openArchive(PHYSFS_Io *io, const char *name,
+                              int forWriting, int *claimed)
 {
     PHYSFS_uint32 val = 0;
     PHYSFS_uint32 pos = 0;
@@ -71,6 +72,8 @@
     if (PHYSFS_swapULE32(val) != QPAK_SIG)
         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
 
+    *claimed = 1;
+
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
     pos = PHYSFS_swapULE32(val);  /* directory table offset. */
 
--- a/src/physfs_archiver_slb.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_slb.c	Mon Aug 14 02:28:00 2017 -0400
@@ -59,7 +59,8 @@
 } /* slbLoadEntries */
 
 
-static void *SLB_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *SLB_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint32 version;
     PHYSFS_uint32 count;
@@ -102,6 +103,8 @@
         return NULL;
     } /* if */
 
+    *claimed = 1;  /* oh well. */
+
     return unpkarc;
 } /* SLB_openArchive */
 
--- a/src/physfs_archiver_vdf.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_vdf.c	Mon Aug 14 02:28:00 2017 -0400
@@ -91,7 +91,8 @@
 } /* vdfLoadEntries */
 
 
-static void *VDF_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *VDF_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint8 ignore[16];
     PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH];
@@ -106,6 +107,15 @@
     BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL);
 
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL);
+
+    if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
+        (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
+    {
+        BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
+    } /* if */
+
+    *claimed = 1;
+
     BAIL_IF_ERRPASS(!readui32(io, &count), NULL);
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL);  /* numFiles */
     BAIL_IF_ERRPASS(!readui32(io, &timestamp), NULL);
@@ -115,12 +125,6 @@
 
     BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
 
-    if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
-        (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
-    {
-        BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
-    } /* if */
-
     BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL);
 
     unpkarc = UNPK_openArchive(io);
--- a/src/physfs_archiver_wad.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_wad.c	Mon Aug 14 02:28:00 2017 -0400
@@ -70,7 +70,8 @@
 } /* wadLoadEntries */
 
 
-static void *WAD_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *WAD_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     PHYSFS_uint8 buf[4];
     PHYSFS_uint32 count;
@@ -84,6 +85,8 @@
     if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0))
         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
 
+    *claimed = 1;
+
     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL);
     count = PHYSFS_swapULE32(count);
 
--- a/src/physfs_archiver_zip.c	Mon Aug 14 01:35:10 2017 -0400
+++ b/src/physfs_archiver_zip.c	Mon Aug 14 02:28:00 2017 -0400
@@ -1451,7 +1451,8 @@
 } /* ZIP_closeArchive */
 
 
-static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
+static void *ZIP_openArchive(PHYSFS_Io *io, const char *name,
+                             int forWriting, int *claimed)
 {
     ZIPinfo *info = NULL;
     ZIPentry *root = NULL;
@@ -1464,6 +1465,8 @@
     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
     BAIL_IF_ERRPASS(!isZip(io), NULL);
 
+    *claimed = 1;
+
     info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
     BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
     memset(info, '\0', sizeof (ZIPinfo));