From a197f30eef0a01096e47f97fdf8edfef6e583515 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 7 Jul 2001 03:52:43 +0000 Subject: [PATCH] Tons of updates. Mostly implemented. Mostly compiling. --- physfs.c | 513 +++++++++++++++++++++++++++++----------------- physfs.h | 121 ++++++++--- physfs_internal.h | 121 +++++++++-- unix.c | 21 +- 4 files changed, 529 insertions(+), 247 deletions(-) diff --git a/physfs.c b/physfs.c index 49cf1404..c34188b9 100644 --- a/physfs.c +++ b/physfs.c @@ -25,16 +25,16 @@ typedef struct __PHYSFS_ERRMSGTYPE__ struct __PHYSFS_ERRMSGTYPE__ *next; } ErrMsg; -typedef struct __PHYSFS_SEARCHDIRINFO__ +typedef struct __PHYSFS_DIRINFO__ { char *dirName; - DirReader *reader; - struct __PHYSFS_SEARCHDIRINFO__ *next; -} SearchDirInfo; + DirHandle *dirHandle; + struct __PHYSFS_DIRINFO__ *next; +} DirInfo; typedef struct __PHYSFS_FILEHANDLELIST__ { - FileHandle *handle; + PHYSFS_file *handle; struct __PHYSFS_FILEHANDLELIST__ *next; } FileHandleList; @@ -73,12 +73,12 @@ static const DirFunctions *dirFunctions[] = static int initialized = 0; static ErrMsg *errorMessages = NULL; -static SearchDirInfo *searchPath = NULL; +static DirInfo *searchPath = NULL; +static DirInfo *writeDir = NULL; static FileHandleList *openWriteList = NULL; static FileHandleList *openReadList = NULL; static char *baseDir = NULL; static char *userDir = NULL; -static char *writeDir = NULL; static int allowSymLinks = 0; @@ -107,7 +107,12 @@ static ErrMsg *findErrorForCurrentThread(void) void __PHYSFS_setError(const char *str) { - ErrMsg *err = findErrorForCurrentThread(); + ErrMsg *err; + + if (str == NULL) + return; + + err = findErrorForCurrentThread(); if (err == NULL) { @@ -126,6 +131,19 @@ void __PHYSFS_setError(const char *str) } /* __PHYSFS_setError */ +static void freeErrorMessages(void) +{ + ErrMsg *i; + ErrMsg *next; + + for (i = errorMessages; i != NULL; i = next) + { + next = i; + free(i); + } /* for */ +} /* freeErrorMessages */ + + const char *PHYSFS_getLastError(void) { ErrMsg *err = findErrorForCurrentThread(); @@ -149,22 +167,88 @@ void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) } /* PHYSFS_getLinkedVersion */ -static const char *calculateUserDir(void) +static DirHandle *openDirectory(const char *d, int forWriting) +{ + const DirFunctions **i; + + for (i = dirFunctions; *i != NULL; i++) + { + if ((*i)->isArchive(d, forWriting)) + return( (*i)->openArchive(d, forWriting) ); + } /* for */ + + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + return(NULL); +} /* openDirectory */ + + +static DirInfo *buildDirInfo(const char *newDir, int forWriting) +{ + DirHandle *dirHandle = NULL; + DirInfo *di = NULL; + + BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0); + + dirHandle = openDirectory(newDir, forWriting); + BAIL_IF_MACRO(dirHandle == NULL, NULL, 0); + + di = (DirInfo *) malloc(sizeof (DirInfo)); + if (di == NULL) + dirHandle->funcs->close(dirHandle); + BAIL_IF_MACRO(di == NULL, ERR_OUT_OF_MEMORY, 0); + + di->dirName = (char *) malloc(strlen(newDir) + 1); + if (di->dirName == NULL) + { + free(di); + dirHandle->funcs->close(dirHandle); + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + return(0); + } /* if */ + + di->next = NULL; + di->dirHandle = dirHandle; + strcpy(di->dirName, newDir); + return(di); +} /* buildDirInfo */ + + +static int freeDirInfo(DirInfo *di, FileHandleList *openList) +{ + FileHandleList *i; + + if (di == NULL) + return(1); + + for (i = openList; i != NULL; i = i->next) + { + const DirHandle *h = ((FileHandle *) i->handle->opaque)->dirHandle; + BAIL_IF_MACRO(h == di->dirHandle, ERR_FILES_STILL_OPEN, 0); + } /* for */ + + di->dirHandle->funcs->close(di->dirHandle); + free(di->dirName); + free(di); + return(1); +} /* freeDirInfo */ + + +static char *calculateUserDir(void) { char *retval = NULL; const char *str = NULL; str = __PHYSFS_platformGetUserDir(); if (str != NULL) - retval = str; + retval = (char *) str; else { const char *dirsep = PHYSFS_getDirSeparator(); const char *uname = __PHYSFS_platformGetUserName(); str = (uname != NULL) ? uname : "default"; - retval = malloc(strlen(baseDir) + strlen(str) + - (strlen(dirsep) * 2) + 6); + retval = (char *) malloc(strlen(baseDir) + strlen(str) + + (strlen(dirsep) * 2) + 6); if (retval == NULL) __PHYSFS_setError(ERR_OUT_OF_MEMORY); @@ -172,21 +256,26 @@ static const char *calculateUserDir(void) sprintf(retval, "%s%susers%s%s", baseDir, dirsep, dirsep, str); if (uname != NULL) - free(uname); + free((void *) uname); } /* else */ return(retval); } /* calculateUserDir */ +static char *calculateBaseDir(const char *argv0) +{ +assert(0); return(NULL); +} /* calculateBaseDir */ + + int PHYSFS_init(const char *argv0) { BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); BAIL_IF_MACRO(argv0 == NULL, ERR_INVALID_ARGUMENT, 0); baseDir = calculateBaseDir(argv0); - if (baseDir == NULL) - return(0); + BAIL_IF_MACRO(baseDir == NULL, NULL, 0); userDir = calculateUserDir(); if (userDir == NULL) @@ -201,42 +290,35 @@ int PHYSFS_init(const char *argv0) } /* PHYSFS_init */ -static void freeSearchDir(SearchDirInfo *sdi) -{ - FileHandleList *i; - - assert(sdi != NULL); - for (i = openReadList; i != NULL; i = i->next) - { - BAIL_IF_MACRO(i->handle->dirReader == sdi->reader, - ERR_FILES_OPEN_READ, 0); - } /* for */ - - sdi->reader->close(sdi->reader); - free(sdi->dirName); - free(sdi); -} /* freeSearchDir */ - - -static void closeFileHandleList(FileHandleList **list) +static int closeFileHandleList(FileHandleList **list) { FileHandleList *i; FileHandleList *next = NULL; + FileHandle *h; for (i = *list; i != NULL; i = next) { next = i->next; - i->handle->close(i->handle); + h = (FileHandle *) (i->handle->opaque); + if (!h->funcs->close(i->handle->opaque)) + { + *list = i; + return(0); + } /* if */ + + free(i->handle); + free(i); } /* for */ *list = NULL; -} /* closeAllFiles */ + return(1); +} /* closeFileHandleList */ static void freeSearchPath(void) { - SearchDirInfo *i; - SearchDirInfo *next = NULL; + DirInfo *i; + DirInfo *next = NULL; closeFileHandleList(&openReadList); @@ -245,19 +327,20 @@ static void freeSearchPath(void) for (i = searchPath; i != NULL; i = next) { next = i; - freeSearchDir(i); + freeDirInfo(i, openReadList); } /* for */ searchPath = NULL; } /* if */ } /* freeSearchPath */ -void PHYSFS_deinit(void) +int PHYSFS_deinit(void) { BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); closeFileHandleList(&openWriteList); - PHYSFS_setWriteDir(NULL); + BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0); + freeSearchPath(); freeErrorMessages(); @@ -297,7 +380,7 @@ void PHYSFS_freeList(void *list) const char *PHYSFS_getDirSeparator(void) { - return(__PHYSFS_pathSeparator); + return(__PHYSFS_platformDirSeparator); } /* PHYSFS_getDirSeparator */ @@ -321,96 +404,55 @@ const char *PHYSFS_getUserDir(void) const char *PHYSFS_getWriteDir(void) { - return(writeDir); + if (writeDir == NULL) + return(NULL); + + return(writeDir->dirName); } /* PHYSFS_getWriteDir */ int PHYSFS_setWriteDir(const char *newDir) { - BAIL_IF_MACRO(openWriteList != NULL, ERR_FILES_OPEN_WRITE, 0); - if (writeDir != NULL) { - free(writeDir); + BAIL_IF_MACRO(!freeDirInfo(writeDir, openWriteList), NULL, 0); writeDir = NULL; } /* if */ if (newDir != NULL) { - BAIL_IF_MACRO(!createDirs_dependent(newDir), ERR_NO_DIR_CREATE, 0); - - writeDir = malloc(strlen(newDir) + 1); - BAIL_IF_MACRO(writeDir == NULL, ERR_OUT_OF_MEMORY, 0); - - strcpy(writeDir, newDir); + writeDir = buildDirInfo(newDir, 1); + return(writeDir != NULL); } /* if */ return(1); } /* PHYSFS_setWriteDir */ -static DirReader *getDirReader(const char *d) -{ - DirFunctions **i; - - for (i = dirFunctions; *i != NULL; i++) - { - if ((*i)->isArchive(d)) - return( (*i)->openArchive(d) ); - } /* for */ - - __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); - return(NULL); -} /* getDirReader */ - - int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) { - char *str = NULL; - SearchDirInfo *sdi = NULL; - DirReader *dirReader = NULL; - - BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0); - - reader = getDirReader(newDir); /* This sets the error message. */ - if (reader == NULL) - return(0); - - sdi = (SearchDirInfo *) malloc(sizeof (SearchDirInfo)); - if (sdi == NULL) - reader->close(reader); - BAIL_IF_MACRO(sdi == NULL, ERR_OUT_OF_MEMORY, 0); + DirInfo *di = buildDirInfo(newDir, 0); - sdi->dirName = (char *) malloc(strlen(newDir) + 1); - if (sdi->dirName == NULL) - { - free(sdi); - reader->close(reader); - __PHYSFS_setError(ERR_OUT_OF_MEMORY); - return(0); - } /* if */ - - sdi->dirReader = dirReader; - strcpy(sdi->dirName, newDir); + BAIL_IF_MACRO(di == NULL, NULL, 0); if (appendToPath) { - sdi->next = searchPath; - searchPath = sdi; + di->next = searchPath; + searchPath = di; } /* if */ else { - SearchDirInfo *i = searchPath; - SearchDirInfo *prev = NULL; + DirInfo *i = searchPath; + DirInfo *prev = NULL; - sdi->next = NULL; + di->next = NULL; while (i != NULL) prev = i; if (prev == NULL) - searchPath = sdi; + searchPath = di; else - prev->next = sdi; + prev->next = di; } /* else */ return(1); @@ -419,9 +461,9 @@ int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) int PHYSFS_removeFromSearchPath(const char *oldDir) { - SearchDirInfo *i; - SearchDirInfo *prev = NULL; - SearchDirInfo *next = NULL; + DirInfo *i; + DirInfo *prev = NULL; + DirInfo *next = NULL; BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0); @@ -430,8 +472,7 @@ int PHYSFS_removeFromSearchPath(const char *oldDir) if (strcmp(i->dirName, oldDir) == 0) { next = i->next; - if (!freeSearchDir(i)) - return(0); + BAIL_IF_MACRO(!freeDirInfo(i, openReadList), NULL, 0); if (prev == NULL) searchPath = next; @@ -452,7 +493,7 @@ char **PHYSFS_getSearchPath(void) { int count = 1; int x; - SearchDirInfo *i; + DirInfo *i; char **retval; for (i = searchPath; i != NULL; i = i->next) @@ -501,13 +542,12 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt, BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0); sprintf(str, "%s%s.%s", userdir, dirsep, appName); rc = PHYSFS_setWriteDir(str); - if (!rc) - return(0); /* error set by PHYSFS_setWriteDir() ... */ + BAIL_IF_MACRO(!rc, NULL, 0); /* Put write dir related dirs on search path... */ PHYSFS_addToSearchPath(str, 1); PHYSFS_mkdir(appName); /* don't care if this fails. */ - strcat(str, dirSep); + strcat(str, dirsep); strcat(str, appName); PHYSFS_addToSearchPath(str, 1); free(str); @@ -530,12 +570,12 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt, char **i; for (i = cds; *i != NULL; i++) { - PHYSFS_addToSearchPath(*i); + PHYSFS_addToSearchPath(*i, 1); str = malloc(strlen(*i) + strlen(appName) + strlen(dirsep) + 1); if (str != NULL) { sprintf(str, "%s%s%s", *i, dirsep, appName); - PHYSFS_addToSearchPath(str); + PHYSFS_addToSearchPath(str, 1); free(str); } /* if */ } /* for */ @@ -563,7 +603,7 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt, if (str != NULL) { sprintf(str, "%s%s%s", d, dirsep, *i); - PHYSFS_addToSearchPath(d, str); + PHYSFS_addToSearchPath(str, archivesFirst == 0); free(str); } /* if */ } /* if */ @@ -577,11 +617,16 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt, } /* PHYSFS_setSaneConfig */ +void PHYSFS_permitSymbolicLinks(int allow) +{ + allowSymLinks = allow; +} /* PHYSFS_permitSymbolicLinks */ + + /* string manipulation in C makes my ass itch. */ -/* be sure to free this crap after you're done with it. */ -static char *convertToDependentNotation(const char *prepend, - const char *dirName, - const char *append) +char *__PHYSFS_convertToDependentNotation(const char *prepend, + const char *dirName, + const char *append) { const char *dirsep = PHYSFS_getDirSeparator(); int sepsize = strlen(dirsep); @@ -590,7 +635,7 @@ static char *convertToDependentNotation(const char *prepend, char *i2; size_t allocSize; - allocSize = strlen(dirName) + strlen(writeDir) + sepsize + 1; + allocSize = strlen(dirName) + 1; if (prepend != NULL) allocSize += strlen(prepend) + sepsize; if (append != NULL) @@ -599,11 +644,16 @@ static char *convertToDependentNotation(const char *prepend, /* make sure there's enough space if the dir separator is bigger. */ if (sepsize > 1) { - for (str = dirName; *str != '\0'; str++) + str = (char *) dirName; + do { - if (*str == '/') + str = strchr(str, '/'); + if (str != NULL) + { allocSize += (sepsize - 1); - } /* for */ + str++; + } /* if */ + } while (str != NULL); } /* if */ str = (char *) malloc(allocSize); @@ -616,7 +666,7 @@ static char *convertToDependentNotation(const char *prepend, strcat(str, dirsep); } /* if */ - for (i1 = dirName, i2 = str + strlen(str); *i1 != '\0'; i1++, i2++) + for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++) { if (*i1 == '/') { @@ -637,78 +687,123 @@ static char *convertToDependentNotation(const char *prepend, } /* if */ return(str); -} /* convertToDependentNotation */ +} /* __PHYSFS_convertToDependentNotation */ -int PHYSFS_mkdir(const char *dirName) +int __PHYSFS_verifySecurity(DirHandle *h, const char *fname) { + int retval = 1; + char *start; + char *end; char *str; - int rc; - BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL); + start = str = malloc(strlen(fname) + 1); + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0); + strcpy(str, fname); - str = convertToDependentNotation(writeDir, dirName, NULL); - if (str == NULL) /* __PHYSFS_setError is called in convert call. */ - return(0); + while (1) + { + end = strchr(start, '/'); + if (end != NULL) + *end = '\0'; + + if ( (strcmp(start, ".") == 0) || + (strcmp(start, "..") == 0) || + (strchr(start, ':') != NULL) ) + { + __PHYSFS_setError(ERR_INSECURE_FNAME); + retval = 0; + break; + } /* if */ + + if ((!allowSymLinks) && (h->funcs->isSymLink(h, str))) + { + __PHYSFS_setError(ERR_SYMLINK_DISALLOWED); + retval = 0; + break; + } /* if */ + + if (end == NULL) + break; + + *end = '/'; + start = end + 1; + } /* while */ - rc = createDirs_dependent(str); free(str); - return(rc); -} /* PHYSFS_mkdir */ + return(retval); +} /* __PHYSFS_verifySecurity */ -int PHYSFS_delete(const char *filename) +int PHYSFS_mkdir(const char *dirName) { + DirHandle *h; char *str; - int rc; + char *start; + char *end; + int retval = 0; - BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL); + BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0); + h = writeDir->dirHandle; + BAIL_IF_MACRO(h->funcs->mkdir == NULL, ERR_NOT_SUPPORTED, 0); + BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, dirName), NULL, 0); - str = convertToDependentNotation(writeDir, fileName, NULL); - if (str == NULL) /* __PHYSFS_setError is called in convert call. */ - return(0); + start = str = malloc(strlen(dirName) + 1); + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0); + strcpy(str, dirName); - rc = remove(str); - free(str); + while (1) + { + end = strchr(start, '/'); + if (end != NULL) + *end = '\0'; - rc = (rc == 0); - if (!rc) - __PHYSFS_setError(strerror(errno)); + retval = h->funcs->mkdir(h, str); + if (!retval) + break; - return(rc); -} /* PHYSFS_delete */ + if (end == NULL) + break; + *end = '/'; + start = end + 1; + } /* while */ -void PHYSFS_permitSymbolicLinks(int allow) + free(str); + return(retval); +} /* PHYSFS_mkdir */ + + +int PHYSFS_delete(const char *fname) { - allowSymLinks = allow; -} /* PHYSFS_permitSymbolicLinks */ + DirHandle *h; + BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0); + h = writeDir->dirHandle; + BAIL_IF_MACRO(h->funcs->remove == NULL, ERR_NOT_SUPPORTED, 0); + BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, fname), NULL, 0); + return(h->funcs->remove(h, fname)); +} /* PHYSFS_delete */ -/** - * Figure out where in the search path a file resides. The file is specified - * in platform-independent notation. The returned filename will be the - * element of the search path where the file was found, which may be a - * directory, or an archive. Even if there are multiple matches in different - * parts of the search path, only the first one found is used, just like - * when opening a file. - * - * So, if you look for "maps/level1.map", and C:\mygame is in your search - * path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned. - * - * If a match is a symbolic link, and you've not explicitly permitted symlinks, - * then it will be ignored, and the search for a match will continue. - * - * @param filename file to look for. - * @return READ ONLY string of element of search path containing the - * the file in question. NULL if not found. - */ const char *PHYSFS_getRealDir(const char *filename) { + DirInfo *i; + + for (i = searchPath; i != NULL; i = i->next) + { + DirHandle *h = i->dirHandle; + if (__PHYSFS_verifySecurity(h, filename)) + { + if (h->funcs->exists(h, filename)) + return(i->dirName); + } /* if */ + } /* for */ + + return(NULL); } /* PHYSFS_getRealDir */ -static void countList(LinkedStringList *list) +static int countList(LinkedStringList *list) { int retval = 0; LinkedStringList *i; @@ -797,16 +892,19 @@ static void interpolateStringLists(LinkedStringList **final, char **PHYSFS_enumerateFiles(const char *path) { - SearchDirInfo *i; + DirInfo *i; char **retval = NULL; LinkedStringList *rc; LinkedStringList *finalList = NULL; for (i = searchPath; i != NULL; i = i->next) { - assert(i->reader->funcs->enumerateFiles != NULL); - rc = i->reader->funcs->enumerateFiles(path); - interpolateStringLists(&finalList, rc); + DirHandle *h = i->dirHandle; + if (__PHYSFS_verifySecurity(h, path)) + { + rc = h->funcs->enumerateFiles(h, path); + interpolateStringLists(&finalList, rc); + } /* if */ } /* for */ retval = convertStringListToPhysFSList(finalList); @@ -814,6 +912,50 @@ char **PHYSFS_enumerateFiles(const char *path) } /* PHYSFS_enumerateFiles */ +int PHYSFS_exists(const char *fname) +{ + return(PHYSFS_getRealDir(fname) != NULL); +} /* PHYSFS_exists */ + + +int PHYSFS_isDirectory(const char *fname) +{ + DirInfo *i; + + for (i = searchPath; i != NULL; i = i->next) + { + DirHandle *h = i->dirHandle; + if (__PHYSFS_verifySecurity(h, fname)) + { + if (h->funcs->exists(h, fname)) + return(h->funcs->isDirectory(h, fname)); + } /* if */ + } /* for */ + + return(0); +} /* PHYSFS_isDirectory */ + + +int PHYSFS_isSymbolicLink(const char *fname) +{ + DirInfo *i; + + if (!allowSymLinks) + return(0); + + for (i = searchPath; i != NULL; i = i->next) + { + DirHandle *h = i->dirHandle; + if (__PHYSFS_verifySecurity(h, fname)) + { + if (h->funcs->exists(h, fname)) + return(h->funcs->isSymLink(h, fname)); + } /* if */ + } /* for */ + + return(0); +} /* PHYSFS_isSymbolicLink */ + /** * Open a file for writing, in platform-independent notation and in relation * to the write path as the root of the writable filesystem. The specified @@ -826,6 +968,7 @@ char **PHYSFS_enumerateFiles(const char *path) */ PHYSFS_file *PHYSFS_openWrite(const char *filename) { +return NULL; } /* PHYSFS_openWrite */ @@ -842,6 +985,7 @@ PHYSFS_file *PHYSFS_openWrite(const char *filename) */ PHYSFS_file *PHYSFS_openAppend(const char *filename) { +return NULL; } /* PHYSFS_openAppend */ @@ -857,44 +1001,31 @@ PHYSFS_file *PHYSFS_openAppend(const char *filename) */ PHYSFS_file *PHYSFS_openRead(const char *filename) { +return NULL; } /* PHYSFS_openRead */ -/** - * Close a PhysicsFS filehandle. This call is capable of failing if the - * operating system was buffering writes to this file, and (now forced to - * write those changes to physical media) can not store the data for any - * reason. In such a case, the filehandle stays open. A well-written program - * should ALWAYS check the return value from the close call in addition to - * every writing call! - * - * @param handle handle returned from PHYSFS_open*(). - * @return nonzero on success, zero on error. Specifics of the error can be - * gleaned from PHYSFS_getLastError(). - */ int PHYSFS_close(PHYSFS_file *handle) { FileHandle *h = (FileHandle *) handle->opaque; FileHandleList *i; - FileHandleList **lists[] = { &openWriteList, &openReadList, NULL }; + FileHandleList *prev; + FileHandleList **_lists[] = { &openWriteList, &openReadList, NULL }; + FileHandleList ***lists = _lists; /* gay. */ int rc; - assert(h != NULL); - assert(h->funcs != NULL); - assert(h->funcs->close != NULL); - while (lists != NULL) { - for (i = *(*lists); i != NULL; i = i->next) + for (i = *(*lists), prev = NULL; i != NULL; prev = i, i = i->next) { - if (i->handle == h) + if (((FileHandle *) i->handle->opaque) == h) { - rc = h->close(h); + rc = h->funcs->close(h); if (!rc) return(0); if (prev == NULL) - *lists = i->next; + *(*lists) = i->next; else prev->next = i->next; free(i); @@ -905,7 +1036,8 @@ int PHYSFS_close(PHYSFS_file *handle) lists++; } /* while */ - assert(0); /* shouldn't EVER hit this. */ + __PHYSFS_setError(ERR_NOT_A_HANDLE); + return(0); } /* PHYSFS_close */ @@ -960,5 +1092,6 @@ int PHYSFS_seek(PHYSFS_file *handle, int pos) return(h->funcs->seek(h, pos)); } /* PHYSFS_seek */ + /* end of physfs.c ... */ diff --git a/physfs.h b/physfs.h index 116e64a3..17b70095 100644 --- a/physfs.h +++ b/physfs.h @@ -211,16 +211,22 @@ int PHYSFS_init(const char *argv0); * Shutdown PhysicsFS. This closes any files opened via PhysicsFS, blanks the * search/write paths, frees memory, and invalidates all of your handles. * - * Once deinitialized, PHYSFS_init() can be called again to restart the - * subsystem. + * Note that this call can FAIL if there's a file open for writing that + * refuses to close (for example, the underlying operating system was + * buffering writes to network filesystem, and the fileserver has crashed, + * or a hard drive has failed, etc). It is usually best to close all write + * handles yourself before calling this function, so that you can gracefully + * handle a specific failure. * - * This function can be used with atexit(), if you feel it's prudent to do so. + * Once successfully deinitialized, PHYSFS_init() can be called again to + * restart the subsystem. All defaults API states are restored at this + * point. * * @return nonzero on success, zero on error. Specifics of the error can be * gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is * undefined, and probably badly screwed up. */ -void PHYSFS_deinit(void); +int PHYSFS_deinit(void); /** @@ -286,6 +292,30 @@ const char *PHYSFS_getLastError(void); const char *PHYSFS_getDirSeparator(void); +/** + * Enable symbolic links. Some physical filesystems and archives contain + * files that are just pointers to other files. On the physical filesystem, + * opening such a link will (transparently) open the file that is pointed to. + * + * By default, PhysicsFS will check if a file is really a symlink during open + * calls and fail if it is. Otherwise, the link could take you outside the + * write and search paths, and compromise security. + * + * If you want to take that risk, call this function with a non-zero parameter. + * Note that this is more for sandboxing a program's scripting language, in + * case untrusted scripts try to compromise the system. Generally speaking, + * a user could very well have a legitimate reason to set up a symlink, so + * unless you feel there's a specific danger in allowing them, you should + * permit them. + * + * Symbolic link permission can be enabled or disabled at any time, and is + * disabled by default. + * + * @param allow nonzero to permit symlinks, zero to deny linking. + */ +void PHYSFS_permitSymbolicLinks(int allow); + + /** * Get an array of dirs to available CD-ROM drives. * @@ -528,30 +558,6 @@ int PHYSFS_mkdir(const char *dirName); int PHYSFS_delete(const char *filename); -/** - * Enable symbolic links. Some physical filesystems and archives contain - * files that are just pointers to other files. On the physical filesystem, - * opening such a link will (transparently) open the file that is pointed to. - * - * By default, PhysicsFS will check if a file is really a symlink during open - * calls and fail if it is. Otherwise, the link could take you outside the - * write and search paths, and compromise security. - * - * If you want to take that risk, call this function with a non-zero parameter. - * Note that this is more for sandboxing a program's scripting language, in - * case untrusted scripts try to compromise the system. Generally speaking, - * a user could very well have a legitimate reason to set up a symlink, so - * unless you feel there's a specific danger in allowing them, you should - * permit them. - * - * Symbolic link permission can be enabled or disabled at any time, and is - * disabled by default. - * - * @param allow nonzero to permit symlinks, zero to deny linking. - */ -void PHYSFS_permitSymbolicLinks(int allow); - - /** * Figure out where in the search path a file resides. The file is specified * in platform-independent notation. The returned filename will be the @@ -563,8 +569,9 @@ void PHYSFS_permitSymbolicLinks(int allow); * So, if you look for "maps/level1.map", and C:\mygame is in your search * path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned. * - * If a match is a symbolic link, and you've not explicitly permitted symlinks, - * then it will be ignored, and the search for a match will continue. + * If a any part of a match is a symbolic link, and you've not explicitly + * permitted symlinks, then it will be ignored, and the search for a match + * will continue. * * @param filename file to look for. * @return READ ONLY string of element of search path containing the @@ -612,12 +619,58 @@ const char *PHYSFS_getRealDir(const char *filename); char **PHYSFS_enumerateFiles(const char *dir); +/** + * Determine if there is an entry anywhere in the search path by the + * name of (fname). + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * @param fname filename in platform-independent notation. + * @return non-zero if filename exists. zero otherwise. + */ +int PHYSFS_exists(const char *fname); + + +/** + * Determine if the first occurence of (fname) in the search path is + * really a directory entry. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * @param fname filename in platform-independent notation. + * @return non-zero if filename exists and is a directory. zero otherwise. + */ +int PHYSFS_isDirectory(const char *fname); + + +/** + * Determine if the first occurence of (fname) in the search path is + * really a symbolic link. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such, + * this function will always return 0 in that case. + * + * @param fname filename in platform-independent notation. + * @return non-zero if filename exists and is a symlink. zero otherwise. + */ +int PHYSFS_isSymbolicLink(const char *fname); + + /** * Open a file for writing, in platform-independent notation and in relation * to the write dir as the root of the writable filesystem. The specified * file is created if it doesn't exist. If it does exist, it is truncated to * zero bytes, and the writing offset is set to the start. * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * * @param filename File to open. * @return A valid PhysicsFS filehandle on success, NULL on error. Specifics * of the error can be gleaned from PHYSFS_getLastError(). @@ -632,6 +685,10 @@ PHYSFS_file *PHYSFS_openWrite(const char *filename); * is set to the end of the file, so the first write will be the byte after * the end. * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * * @param filename File to open. * @return A valid PhysicsFS filehandle on success, NULL on error. Specifics * of the error can be gleaned from PHYSFS_getLastError(). @@ -645,6 +702,10 @@ PHYSFS_file *PHYSFS_openAppend(const char *filename); * abstract filehandle is associated with it, and reading may be done. * The reading offset is set to the first byte of the file. * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * * @param filename File to open. * @return A valid PhysicsFS filehandle on success, NULL on error. Specifics * of the error can be gleaned from PHYSFS_getLastError(). diff --git a/physfs_internal.h b/physfs_internal.h index 1fa2ce33..7221063f 100644 --- a/physfs_internal.h +++ b/physfs_internal.h @@ -14,7 +14,7 @@ #error Do not include this header from your applications. #endif -struct __PHYSFS_DIRREADER__; +struct __PHYSFS_DIRHANDLE__; struct __PHYSFS_FILEFUNCTIONS__; typedef struct __PHYSFS_FILEHANDLE__ @@ -27,7 +27,7 @@ typedef struct __PHYSFS_FILEHANDLE__ /* * This should be the DirHandle that created this FileHandle. */ - const struct __PHYSFS_DIRREADER__ *dirReader; + const struct __PHYSFS_DIRHANDLE__ *dirHandle; /* * Pointer to the file i/o functions for this filehandle. @@ -40,6 +40,9 @@ typedef struct __PHYSFS_FILEFUNCTIONS__ { /* * Read more from the file. + * Returns number of objects of (objSize) bytes read from file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). */ int (*read)(FileHandle *handle, void *buffer, unsigned int objSize, unsigned int objCount); @@ -47,6 +50,9 @@ typedef struct __PHYSFS_FILEFUNCTIONS__ /* * Write more to the file. Archives don't have to implement this. * (Set it to NULL if not implemented). + * Returns number of objects of (objSize) bytes written to file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). */ int (*write)(FileHandle *handle, void *buffer, unsigned int objSize, unsigned int objCount); @@ -64,17 +70,20 @@ typedef struct __PHYSFS_FILEFUNCTIONS__ /* * Move read/write pointer to byte offset from start of file. * Returns non-zero on success, zero on error. + * On failure, call __PHYSFS_setError(). */ int (*seek)(FileHandle *handle, int offset); /* * Close the file, and free the FileHandle structure (including "opaque"). + * returns non-zero on success, zero if can't close file. + * On failure, call __PHYSFS_setError(). */ int (*close)(FileHandle *handle); } FileFunctions; -typedef struct __PHYSFS_DIRREADER__ +typedef struct __PHYSFS_DIRHANDLE__ { /* * This is reserved for the driver to store information. @@ -82,7 +91,7 @@ typedef struct __PHYSFS_DIRREADER__ void *opaque; /* - * Pointer to the directory i/o functions for this reader. + * Pointer to the directory i/o functions for this handle. */ const struct __PHYSFS_DIRFUNCTIONS__ *funcs; } DirHandle; @@ -104,16 +113,21 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ /* * Returns non-zero if (filename) is a valid archive that this * driver can handle. This filename is in platform-dependent - * notation. + * notation. forWriting 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. */ - int (*isArchive)(const char *filename); + int (*isArchive)(const char *filename, int forWriting); /* * Return a DirHandle for dir/archive (name). * This filename is in platform-dependent notation. - * return (NULL) on error. + * forWriting 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. + * Returns NULL on failure, and calls __PHYSFS_setError(). */ - DirHandle *(*openArchive)(const char *name); + DirHandle *(*openArchive)(const char *name, int forWriting); /* * Returns a list of all files in dirname. Each element of this list @@ -123,29 +137,32 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ * If you have a memory failure, return as much as you can. * This dirname is in platform-independent notation. */ - LinkedStringList **(*enumerateFiles)(DirHandle *r, const char *dirname); + LinkedStringList *(*enumerateFiles)(DirHandle *r, const char *dirname); /* - * Returns non-zero if filename is really a directory. + * Returns non-zero if filename can be opened for reading. * This filename is in platform-independent notation. */ - int (*isDirectory)(DirHandle *r, const char *name); + int (*exists)(DirHandle *r, const char *name); /* - * Returns non-zero if filename is really a symlink. + * Returns non-zero if filename is really a directory. * This filename is in platform-independent notation. */ - int (*isSymLink)(DirHandle *r, const char *name); + int (*isDirectory)(DirHandle *r, const char *name); /* - * Returns non-zero if filename can be opened for reading. + * Returns non-zero if filename is really a symlink. * This filename is in platform-independent notation. */ - int (*isOpenable)(DirHandle *r, const char *name); + int (*isSymLink)(DirHandle *r, const char *name); /* * Open file for reading, and return a FileHandle. * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). */ FileHandle *(*openRead)(DirHandle *r, const char *filename); @@ -153,6 +170,9 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ * Open file for writing, and return a FileHandle. * This filename is in platform-independent notation. * This method may be NULL. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). */ FileHandle *(*openWrite)(DirHandle *r, const char *filename); @@ -160,9 +180,33 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ * Open file for appending, and return a FileHandle. * This filename is in platform-independent notation. * This method may be NULL. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). */ FileHandle *(*openAppend)(DirHandle *r, const char *filename); + /* + * Delete a file in the archive/directory. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*remove)(DirHandle *r, const char *filename); + + /* + * Create a directory in the archive/directory. + * If the application is trying to make multiple dirs, PhysicsFS + * will split them up into multiple calls before passing them to + * your driver. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*mkdir)(DirHandle *r, const char *filename); + /* * Close directories/archives, and free the handle, including * the "opaque" entry. This should assume that it won't be called if @@ -176,25 +220,59 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ #define ERR_IS_INITIALIZED "Already initialized" #define ERR_NOT_INITIALIZED "Not initialized" #define ERR_INVALID_ARGUMENT "Invalid argument" -#define ERR_FILES_OPEN_READ "Files still open for reading" -#define ERR_FILES_OPEN_WRITE "Files still open for writing" +#define ERR_FILES_STILL_OPEN "Files still open" #define ERR_NO_DIR_CREATE "Failed to create directories" #define ERR_OUT_OF_MEMORY "Out of memory" #define ERR_NOT_IN_SEARCH_PATH "No such entry in search path" #define ERR_NOT_SUPPORTED "Operation not supported" #define ERR_UNSUPPORTED_ARCHIVE "Archive type unsupported" +#define ERR_NOT_A_HANDLE "Not a file handle" +#define ERR_INSECURE_FNAME "Insecure filename" +#define ERR_SYMLINK_DISALLOWED "Symbolic links are disabled" +#define ERR_NO_WRITE_DIR "Write directory is not set" /* * Call this to set the message returned by PHYSFS_getLastError(). * Please only use the ERR_* constants above, or add new constants to the * above group, but I want these all in one place. + * + * Calling this with a NULL argument is a safe no-op. */ void __PHYSFS_setError(const char *err); +/* + * Convert (dirName) to platform-dependent notation, then prepend (prepend) + * and append (append) to the converted string. + * + * So, on Win32, calling: + * __PHYSFS_convertToDependentNotation("C:\", "my/files", NULL); + * ...will return the string "C:\my\files". + * + * This is a convenience function; you might want to hack something out that + * is less generic (and therefore more efficient). + * + * Be sure to free() the return value when done with it. + */ +char *__PHYSFS_convertToDependentNotation(const char *prepend, + const char *dirName, + const char *append); + +/* + * 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. + * + * Returns non-zero if string is safe, zero if there's a security issue. + * PHYSFS_getLastError() will specify what was wrong. + */ +int __PHYSFS_verifySecurity(DirHandle *h, const char *fname); + + /* This gets used all over for lessening code clutter. */ -#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return(r); } +#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; } @@ -214,7 +292,7 @@ void __PHYSFS_setError(const char *err); * The dir separator; "/" on unix, "\\" on win32, ":" on MacOS, etc... * Obviously, this isn't a function, but it IS a null-terminated string. */ -extern const char *__PHYSFS_PlatformDirSeparator; +extern const char *__PHYSFS_platformDirSeparator; /* * Platform implementation of PHYSFS_getCdRomDirs()... @@ -262,6 +340,11 @@ int __PHYSFS_platformStricmp(const char *str1, const char *str2); */ int __PHYSFS_platformIsSymlink(const char *fname); +/* + * Return non-zero if filename (in platform-dependent notation) is a symlink. + */ +int __PHYSFS_platformIsDirectory(const char *fname); + #ifdef __cplusplus extern "C" { diff --git a/unix.c b/unix.c index 89dd4db3..487ba3ca 100644 --- a/unix.c +++ b/unix.c @@ -14,7 +14,7 @@ #include "physfs_internal.h" -const char *__PHYSFS_PlatformDirSeparator = "/"; +const char *__PHYSFS_platformDirSeparator = "/"; char **__PHYSFS_platformDetectAvailableCDs(void) { @@ -27,6 +27,16 @@ char *__PHYSFS_platformCalcBaseDir(char *argv0) } /* __PHYSFS_platformCalcBaseDir */ +char *__PHYSFS_platformGetUserName(void) +{ +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ +} /* __PHYSFS_platformGetUserDir */ + + int __PHYSFS_platformGetThreadID(void) { return((int) pthread_self()); @@ -44,14 +54,9 @@ int __PHYSFS_platformIsSymlink(const char *fname) } /* __PHYSFS_platformIsSymlink */ -char *__PHYSFS_platformGetUserName(void) +int __PHYSFS_platformIsDirectory(const char *fname) { -} /* __PHYSFS_platformGetUserName */ - - -char *__PHYSFS_platformGetUserDir(void); -{ -} /* __PHYSFS_platformGetUserDir */ +} /* __PHYSFS_platformIsDirectory */ /* end of unix.c ... */