From 3078acd1ebfe44bded06b807e132ddf2f3f03bf7 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 14 Aug 2017 02:28:00 -0400 Subject: [PATCH] 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 | 26 +++++++++++++++----------- src/physfs.h | 24 +++++++++++++++++------- src/physfs_archiver_7z.c | 11 ++++++++++- src/physfs_archiver_dir.c | 4 +++- src/physfs_archiver_grp.c | 5 ++++- src/physfs_archiver_hog.c | 5 ++++- src/physfs_archiver_iso9660.c | 17 ++++++++++------- src/physfs_archiver_mvl.c | 7 +++++-- src/physfs_archiver_qpak.c | 5 ++++- src/physfs_archiver_slb.c | 5 ++++- src/physfs_archiver_vdf.c | 18 +++++++++++------- src/physfs_archiver_wad.c | 5 ++++- src/physfs_archiver_zip.c | 5 ++++- 13 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/physfs.c b/src/physfs.c index bac20d15..1e10b965 100644 --- a/src/physfs.c +++ b/src/physfs.c @@ -838,7 +838,7 @@ static const char *find_filename_extension(const char *fname) 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 @@ static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs, 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 @@ static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting) 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 @@ static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting) 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 */ diff --git a/src/physfs.h b/src/physfs.h index 8af08bdb..0a1da128 100644 --- a/src/physfs.h +++ b/src/physfs.h @@ -3492,20 +3492,30 @@ typedef struct PHYSFS_Archiver /** * \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). diff --git a/src/physfs_archiver_7z.c b/src/physfs_archiver_7z.c index be05d32d..f35a0b10 100644 --- a/src/physfs_archiver_7z.c +++ b/src/physfs_archiver_7z.c @@ -210,14 +210,23 @@ static void SZIP_closeArchive(void *opaque) } /* 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); diff --git a/src/physfs_archiver_dir.c b/src/physfs_archiver_dir.c index a0aac085..c92ba3de 100644 --- a/src/physfs_archiver_dir.c +++ b/src/physfs_archiver_dir.c @@ -37,7 +37,8 @@ static char *cvtToDependent(const char *prepend, const char *path, -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 @@ static void *DIR_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); diff --git a/src/physfs_archiver_grp.c b/src/physfs_archiver_grp.c index dceeabc4..758475e4 100644 --- a/src/physfs_archiver_grp.c +++ b/src/physfs_archiver_grp.c @@ -56,7 +56,8 @@ static int grpLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) } /* 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 @@ static void *GRP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); diff --git a/src/physfs_archiver_hog.c b/src/physfs_archiver_hog.c index bead0bad..832209f8 100644 --- a/src/physfs_archiver_hog.c +++ b/src/physfs_archiver_hog.c @@ -61,7 +61,8 @@ static int hogLoadEntries(PHYSFS_Io *io, void *arc) } /* 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 @@ static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); diff --git a/src/physfs_archiver_iso9660.c b/src/physfs_archiver_iso9660.c index c152b177..e72a7ad9 100644 --- a/src/physfs_archiver_iso9660.c +++ b/src/physfs_archiver_iso9660.c @@ -215,11 +215,11 @@ static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet, 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 @@ static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos, 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 @@ static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos, } /* 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 @@ static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, int forWri 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); diff --git a/src/physfs_archiver_mvl.c b/src/physfs_archiver_mvl.c index 64077d33..78b59f1a 100644 --- a/src/physfs_archiver_mvl.c +++ b/src/physfs_archiver_mvl.c @@ -53,7 +53,8 @@ static int mvlLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) } /* 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 @@ static void *MVL_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); - BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); + *claimed = 1; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); count = PHYSFS_swapULE32(count); unpkarc = UNPK_openArchive(io); diff --git a/src/physfs_archiver_qpak.c b/src/physfs_archiver_qpak.c index 3d09f380..15a5f2df 100644 --- a/src/physfs_archiver_qpak.c +++ b/src/physfs_archiver_qpak.c @@ -56,7 +56,8 @@ static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) } /* 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 @@ static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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. */ diff --git a/src/physfs_archiver_slb.c b/src/physfs_archiver_slb.c index 9c649ed8..4fc28d40 100644 --- a/src/physfs_archiver_slb.c +++ b/src/physfs_archiver_slb.c @@ -59,7 +59,8 @@ static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) } /* 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 @@ static void *SLB_openArchive(PHYSFS_Io *io, const char *name, int forWriting) return NULL; } /* if */ + *claimed = 1; /* oh well. */ + return unpkarc; } /* SLB_openArchive */ diff --git a/src/physfs_archiver_vdf.c b/src/physfs_archiver_vdf.c index 5d2210f4..96446f98 100644 --- a/src/physfs_archiver_vdf.c +++ b/src/physfs_archiver_vdf.c @@ -91,7 +91,8 @@ static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, } /* 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 @@ static void *VDF_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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, ×tamp), NULL); @@ -115,12 +125,6 @@ static void *VDF_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); diff --git a/src/physfs_archiver_wad.c b/src/physfs_archiver_wad.c index 91e0adc0..b094c5b8 100644 --- a/src/physfs_archiver_wad.c +++ b/src/physfs_archiver_wad.c @@ -70,7 +70,8 @@ static int wadLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) } /* 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 @@ static void *WAD_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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); diff --git a/src/physfs_archiver_zip.c b/src/physfs_archiver_zip.c index 4edfe1af..df4de74f 100644 --- a/src/physfs_archiver_zip.c +++ b/src/physfs_archiver_zip.c @@ -1451,7 +1451,8 @@ static void ZIP_closeArchive(void *opaque) } /* 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 @@ static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) 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));