First chunk of PHYSFS_mount() implementation. Incomplete!
authorRyan C. Gordon <icculus@icculus.org>
Sun, 13 Mar 2005 09:09:26 +0000
changeset 679 ae75b5548364
parent 678 73a2641375a0
child 680 413803699662
First chunk of PHYSFS_mount() implementation. Incomplete!
TODO
physfs.c
physfs.h
--- a/TODO	Sun Mar 13 03:33:11 2005 +0000
+++ b/TODO	Sun Mar 13 09:09:26 2005 +0000
@@ -45,6 +45,8 @@
 - Should file enumeration return an error or set error state?
 - Ryanify pocketpc.c ...
 - Update internal zlib?
+- Split verifySecurity() off into sanitizePath() and hook it into mount point
+  initialization.
 - Get svn hooks working.
 - maybe other stuff.
 
--- a/physfs.c	Sun Mar 13 03:33:11 2005 +0000
+++ b/physfs.c	Sun Mar 13 09:09:26 2005 +0000
@@ -25,6 +25,7 @@
 {
     void *opaque;  /* Instance data unique to the archiver. */
     char *dirName;  /* Path to archive in platform-dependent notation. */
+    char *mountPoint; /* Mountpoint in virtual file tree. */
     const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
     struct __PHYSFS_DIRHANDLE__ *next;  /* linked list stuff. */
 } DirHandle;
@@ -440,6 +441,7 @@
             else
             {
                 memset(retval, '\0', sizeof (DirHandle));
+                retval->mountPoint = NULL;
                 retval->funcs = funcs;
                 retval->opaque = opaque;
             } /* else */
@@ -487,25 +489,41 @@
 } /* openDirectory */
 
 
-static DirHandle *createDirHandle(const char *newDir, int forWriting)
+static DirHandle *createDirHandle(const char *newDir,
+                                  const char *mountPoint,
+                                  int forWriting)
 {
     DirHandle *dirHandle = NULL;
-
-    BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, NULL);
+    GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle);
 
     dirHandle = openDirectory(newDir, forWriting);
-    BAIL_IF_MACRO(dirHandle == NULL, NULL, NULL);
+    GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle);
 
     dirHandle->dirName = (char *) malloc(strlen(newDir) + 1);
-    if (dirHandle->dirName == NULL)
+    GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
+    strcpy(dirHandle->dirName, newDir);
+
+    if ((mountPoint != NULL) && (*mountPoint != '\0'))
+    {
+        /* !!! FIXME: Sanitize the string here. */
+        dirHandle->mountPoint = (char *) malloc(strlen(mountPoint) + 2);
+        GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle);
+        strcpy(dirHandle->mountPoint, mountPoint);
+        strcat(dirHandle->mountPoint, "/");
+    } /* if */
+
+    return(dirHandle);
+
+badDirHandle:
+    if (dirHandle != NULL)
     {
         dirHandle->funcs->dirClose(dirHandle->opaque);
+        free(dirHandle->dirName);
+        free(dirHandle->mountPoint);
         free(dirHandle);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
     } /* if */
 
-    strcpy(dirHandle->dirName, newDir);
-    return(dirHandle);
+    return(NULL);
 } /* createDirHandle */
 
 
@@ -859,7 +877,7 @@
 
     if (newDir != NULL)
     {
-        writeDir = createDirHandle(newDir, 1);
+        writeDir = createDirHandle(newDir, NULL, 1);
         retval = (writeDir != NULL);
     } /* if */
 
@@ -869,12 +887,15 @@
 } /* PHYSFS_setWriteDir */
 
 
-int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
 {
     DirHandle *dh;
     DirHandle *prev = NULL;
     DirHandle *i;
 
+    BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(mountPoint == NULL, ERR_INVALID_ARGUMENT, 0);
+
     __PHYSFS_platformGrabMutex(stateLock);
 
     for (i = searchPath; i != NULL; i = i->next)
@@ -884,7 +905,7 @@
         prev = i;
     } /* for */
 
-    dh = createDirHandle(newDir, 0);
+    dh = createDirHandle(newDir, mountPoint, 0);
     BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0);
 
     if (appendToPath)
@@ -902,6 +923,12 @@
 
     __PHYSFS_platformReleaseMutex(stateLock);
     return(1);
+} /* PHYSFS_mount */
+
+
+int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+{
+    return(PHYSFS_mount(newDir, "/", appendToPath));
 } /* PHYSFS_addToSearchPath */
 
 
@@ -1130,7 +1157,8 @@
  * Verify that (fname) (in platform-independent notation), in relation
  *  to (h) is secure. That means that each element of fname is checked
  *  for symlinks (if they aren't permitted). Also, elements such as
- *  ".", "..", or ":" are flagged.
+ *  ".", "..", or ":" are flagged. This also allows for quick rejection of
+ *  files that exist outside an archive's mountpoint.
  *
  * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs
  *  at a time), you should always pass zero for "allowMissing" for efficiency.
@@ -1148,6 +1176,17 @@
     if (*fname == '\0')  /* quick rejection. */
         return(1);
 
+    if (h->mountPoint != NULL)  /* NULL mountpoint means "/". */
+    {
+        /* !!! FIXME: Case insensitive? */
+        size_t mntpntlen = strlen(h->mountPoint);
+        assert(mntpntlen > 1); /* root mount points should be NULL. */
+        if (strncmp(h->mountPoint, fname, mntpntlen) != 0)
+            return(0);  /* not under the mountpoint, so skip this archive. */
+
+        fname += mntpntlen;  /* move to start of actual archive path. */
+    } /* if */
+
     /* !!! FIXME: Can we ditch this malloc()? */
     start = str = malloc(strlen(fname) + 1);
     BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
--- a/physfs.h	Sun Mar 13 03:33:11 2005 +0000
+++ b/physfs.h	Sun Mar 13 09:09:26 2005 +0000
@@ -78,13 +78,13 @@
  *  it correctly.
  *
  * Files opened through PhysicsFS may NOT contain "." or ".." or ":" as dir
- *  elements. Not only are these meaningless on MacOS and/or Unix, they are a
- *  security hole. Also, symbolic links (which can be found in some archive
- *  types and directly in the filesystem on Unix platforms) are NOT followed
- *  until you call PHYSFS_permitSymbolicLinks(). That's left to your own
- *  discretion, as following a symlink can allow for access outside the write
- *  dir and search paths. There is no mechanism for creating new symlinks in
- *  PhysicsFS.
+ *  elements. Not only are these meaningless on MacOS Classic and/or Unix,
+ *  they are a security hole. Also, symbolic links (which can be found in
+ *  some archive types and directly in the filesystem on Unix platforms) are
+ *  NOT followed until you call PHYSFS_permitSymbolicLinks(). That's left to
+ *  your own discretion, as following a symlink can allow for access outside
+ *  the write dir and search paths. For portability, there is no mechanism for
+ *  creating new symlinks in PhysicsFS.
  *
  * The write dir is not included in the search path unless you specifically
  *  add it. While you CAN change the write dir as many times as you like,
@@ -110,6 +110,18 @@
  *  PHYSFS_getBaseDir(), and PHYSFS_getUserDir() for info on what those
  *  are and how they can help you determine an optimal search path.
  *
+ * PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points
+ *  in the search path. If a zipfile contains "maps/level.map" and you mount
+ *  that archive at "mods/mymod", then you would have to open
+ *  "mods/mymod/maps/level.map" to access the file, even though "mods/mymod"
+ *  isn't actually specified in the .zip file. Unlike the Unix mentality of
+ *  mounting a filesystem, "mods/mymod" doesn't actually have to exist when
+ *  mounting the zipfile. It's a "virtual" directory. The mounting mechanism
+ *  allows the developer to seperate archives in the tree and avoid trampling
+ *  over files when added new archives, such as including mod support in a
+ *  game...keeping external content on a tight leash in this manner can be of
+ *  utmost importance to some applications.
+ *
  * PhysicsFS is mostly thread safe. The error messages returned by
  *  PHYSFS_getLastError are unique by thread, and library-state-setting
  *  functions are mutex'd. For efficiency, individual file accesses are 
@@ -124,7 +136,7 @@
  * Note that archives need not be named as such: if you have a ZIP file and
  *  rename it with a .PKG extension, the file will still be recognized as a
  *  ZIP archive by PhysicsFS; the file's contents are used to determine its
- *  type.
+ *  type where possible.
  *
  * Currently supported archive types:
  *   - .ZIP (pkZip/WinZip/Info-ZIP compatible)
@@ -133,12 +145,13 @@
  *   - .HOG (Descent I/II HOG file archives)
  *   - .MVL (Descent II movielib archives)
  *   - .WAD (DOOM engine archives)
+ *   - .MIX (Older Westwood games archives)
  *
  * Please see the file LICENSE in the source's root directory for licensing
  *  and redistribution rights.
  *
- * Please see the file CREDITS in the source's root directory for a complete
- *  list of who's responsible for this.
+ * Please see the file CREDITS in the source's root directory for a more or
+ *  less complete list of who's responsible for this.
  *
  *  \author Ryan C. Gordon.
  */
@@ -656,19 +669,52 @@
 
 
 /**
- * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+ * \fn int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath);
  * \brief Add an archive or directory to the search path.
  *
  * If this is a duplicate, the entry is not added again, even though the
- *  function succeeds.
+ *  function succeeds. You may not add the same archive to two different
+ *  mountpoints: duplicate checking is done against the archive and not the
+ *  mountpoint.
+ *
+ * When you mount an archive, it is added to a virtual file system...all files
+ *  in all of the archives are interpolated into a single hierachical file
+ *  tree. Two archives mounted at the same place (or an archive with files
+ *  overlapping another mountpoint) may have overlapping files: in such a case,
+ *  the file earliest in the search path is selected, and the other files are
+ *  inaccessible to the application. This allows archives to be used to
+ *  override previous revisions; you can use the mounting mechanism to place
+ *  archives at a specific point in the file tree and prevent overlap; this
+ *  is useful for downloadable mods that might trample over application data
+ *  or each other, for example.
+ *
+ * The mountpoint does not need to exist prior to mounting, which is different
+ *  than those familiar with the Unix concept of "mounting" may not expect.
+ *  As well, more than one archive can be mounted to the same mountpoint, or
+ *  mountpoints and archive contents can overlap...the interpolation mechanism
+ *  still functions as usual.
  *
  *   \param newDir directory or archive to add to the path, in
  *                   platform-dependent notation.
+ *   \param mountPoint Location in the interpolated tree that this archive
+ *                     will be "mounted", in platform-independent notation.
  *   \param appendToPath nonzero to append to search path, zero to prepend.
  *  \return nonzero if added to path, zero on failure (bogus archive, dir
  *                   missing, etc). Specifics of the error can be
  *                   gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath);
+
+
+/**
+ * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+ * \brief Add an archive or directory to the search path.
  *
+ * This is a legacy call, equivalent to:
+ *     PHYSFS_mount(newDir, "/", appendToPath);
+ *
+ * \sa PHYSFS_mount
+ * \sa PHYSFS_unmount
  * \sa PHYSFS_removeFromSearchPath
  * \sa PHYSFS_getSearchPath
  */