diff --git a/external/fs/fs.c b/external/fs/fs.c index 17a67a5b..ae23e53f 100644 --- a/external/fs/fs.c +++ b/external/fs/fs.c @@ -2105,8 +2105,15 @@ FS_API fs_result fs_mkdir(fs* pFS, const char* pPath) char* pRunningPath = pRunningPathStack; size_t runningPathLen = 0; fs_path_iterator iSegment; + const fs_backend* pBackend; - if (pFS == NULL || pPath == NULL) { + pBackend = fs_get_backend_or_default(pFS); + + if (pBackend == NULL) { + return FS_INVALID_ARGS; + } + + if (pPath == NULL) { return FS_INVALID_ARGS; } @@ -2144,7 +2151,7 @@ FS_API fs_result fs_mkdir(fs* pFS, const char* pPath) runningPathLen += iSegment.segmentLength; pRunningPath[runningPathLen] = '\0'; - result = fs_backend_mkdir(pFS->pBackend, pFS, pRunningPath); + result = fs_backend_mkdir(pBackend, pFS, pRunningPath); /* We just pretend to be successful if the directory already exists. */ if (result == FS_ALREADY_EXISTS) { @@ -3040,94 +3047,110 @@ FS_API fs_result fs_file_open_or_info(fs* pFS, const char* pFilePath, int openMo We'll need to iterate over every mount point and keep track of the mount point with the longest prefix that matches the start of the file path. */ - fs_mount_list_iterator iMountPoint; - fs_mount_point* pBestMountPoint = NULL; - const char* pBestMountPointPath = NULL; - const char* pBestMountPointFileSubPath = NULL; - - for (mountPointIerationResult = fs_mount_list_first(pFS->pWriteMountPoints, &iMountPoint); mountPointIerationResult == FS_SUCCESS; mountPointIerationResult = fs_mount_list_next(&iMountPoint)) { - const char* pFileSubPath = fs_path_trim_base(pFilePath, FS_NULL_TERMINATED, iMountPoint.pMountPointPath, FS_NULL_TERMINATED); - if (pFileSubPath == NULL) { - continue; /* The file path doesn't start with this mount point so skip. */ - } - - if (pBestMountPointFileSubPath == NULL || strlen(pFileSubPath) < strlen(pBestMountPointFileSubPath)) { - pBestMountPoint = iMountPoint.internal.pMountPoint; - pBestMountPointPath = iMountPoint.pPath; - pBestMountPointFileSubPath = pFileSubPath; - } - } - - if (pBestMountPoint != NULL) { - char pActualPathStack[1024]; - char* pActualPathHeap = NULL; - char* pActualPath; - int actualPathLen; - char pActualPathCleanStack[1024]; - char* pActualPathCleanHeap = NULL; - char* pActualPathClean; - int actualPathCleanLen; - unsigned int cleanOptions = (openMode & FS_NO_ABOVE_ROOT_NAVIGATION); - - /* If the mount point starts with a root segment, i.e. "/", we cannot allow navigation above that. */ - if (pBestMountPointPath[0] == '/' || pBestMountPointPath[0] == '\\') { - cleanOptions |= FS_NO_ABOVE_ROOT_NAVIGATION; - } - - - /* Here is where we append the cleaned sub-path to the mount points actual path. */ - actualPathLen = fs_path_append(pActualPathStack, sizeof(pActualPathStack), pBestMountPointPath, pBestMountPoint->pathLen, pBestMountPointFileSubPath, FS_NULL_TERMINATED); - if (actualPathLen > 0 && (size_t)actualPathLen >= sizeof(pActualPathStack)) { - /* Not enough room on the stack. Allocate on the heap. */ - pActualPathHeap = (char*)fs_malloc(actualPathLen + 1, fs_get_allocation_callbacks(pFS)); - if (pActualPathHeap == NULL) { - return FS_OUT_OF_MEMORY; + if (pFS != NULL) { + fs_mount_list_iterator iMountPoint; + fs_mount_point* pBestMountPoint = NULL; + const char* pBestMountPointPath = NULL; + const char* pBestMountPointFileSubPath = NULL; + + for (mountPointIerationResult = fs_mount_list_first(pFS->pWriteMountPoints, &iMountPoint); mountPointIerationResult == FS_SUCCESS; mountPointIerationResult = fs_mount_list_next(&iMountPoint)) { + const char* pFileSubPath = fs_path_trim_base(pFilePath, FS_NULL_TERMINATED, iMountPoint.pMountPointPath, FS_NULL_TERMINATED); + if (pFileSubPath == NULL) { + continue; /* The file path doesn't start with this mount point so skip. */ } - fs_path_append(pActualPathHeap, actualPathLen + 1, pBestMountPointPath, pBestMountPoint->pathLen, pBestMountPointFileSubPath, FS_NULL_TERMINATED); /* <-- This should never fail. */ - pActualPath = pActualPathHeap; - } else { - pActualPath = pActualPathStack; + if (pBestMountPointFileSubPath == NULL || strlen(pFileSubPath) < strlen(pBestMountPointFileSubPath)) { + pBestMountPoint = iMountPoint.internal.pMountPoint; + pBestMountPointPath = iMountPoint.pPath; + pBestMountPointFileSubPath = pFileSubPath; + } } + if (pBestMountPoint != NULL) { + char pActualPathStack[1024]; + char* pActualPathHeap = NULL; + char* pActualPath; + int actualPathLen; + char pActualPathCleanStack[1024]; + char* pActualPathCleanHeap = NULL; + char* pActualPathClean; + int actualPathCleanLen; + unsigned int cleanOptions = (openMode & FS_NO_ABOVE_ROOT_NAVIGATION); - /* Now we need to clean the path. */ - actualPathCleanLen = fs_path_normalize(pActualPathCleanStack, sizeof(pActualPathCleanStack), pActualPath, FS_NULL_TERMINATED, cleanOptions); - if (actualPathCleanLen < 0) { - fs_free(pActualPathHeap, fs_get_allocation_callbacks(pFS)); - return FS_INVALID_OPERATION; /* Most likely violating FS_NO_ABOVE_ROOT_NAVIGATION. */ - } + /* If the mount point starts with a root segment, i.e. "/", we cannot allow navigation above that. */ + if (pBestMountPointPath[0] == '/' || pBestMountPointPath[0] == '\\') { + cleanOptions |= FS_NO_ABOVE_ROOT_NAVIGATION; + } - if (actualPathCleanLen >= (int)sizeof(pActualPathCleanStack)) { - pActualPathCleanHeap = (char*)fs_malloc(actualPathCleanLen + 1, fs_get_allocation_callbacks(pFS)); - if (pActualPathCleanHeap == NULL) { + + /* Here is where we append the cleaned sub-path to the mount points actual path. */ + actualPathLen = fs_path_append(pActualPathStack, sizeof(pActualPathStack), pBestMountPointPath, pBestMountPoint->pathLen, pBestMountPointFileSubPath, FS_NULL_TERMINATED); + if (actualPathLen > 0 && (size_t)actualPathLen >= sizeof(pActualPathStack)) { + /* Not enough room on the stack. Allocate on the heap. */ + pActualPathHeap = (char*)fs_malloc(actualPathLen + 1, fs_get_allocation_callbacks(pFS)); + if (pActualPathHeap == NULL) { + return FS_OUT_OF_MEMORY; + } + + fs_path_append(pActualPathHeap, actualPathLen + 1, pBestMountPointPath, pBestMountPoint->pathLen, pBestMountPointFileSubPath, FS_NULL_TERMINATED); /* <-- This should never fail. */ + pActualPath = pActualPathHeap; + } else { + pActualPath = pActualPathStack; + } + + + /* Now we need to clean the path. */ + actualPathCleanLen = fs_path_normalize(pActualPathCleanStack, sizeof(pActualPathCleanStack), pActualPath, FS_NULL_TERMINATED, cleanOptions); + if (actualPathCleanLen < 0) { fs_free(pActualPathHeap, fs_get_allocation_callbacks(pFS)); - return FS_OUT_OF_MEMORY; + return FS_INVALID_OPERATION; /* Most likely violating FS_NO_ABOVE_ROOT_NAVIGATION. */ } - fs_path_normalize(pActualPathCleanHeap, actualPathCleanLen + 1, pActualPath, FS_NULL_TERMINATED, cleanOptions); /* <-- This should never fail. */ - pActualPathClean = pActualPathCleanHeap; + if (actualPathCleanLen >= (int)sizeof(pActualPathCleanStack)) { + pActualPathCleanHeap = (char*)fs_malloc(actualPathCleanLen + 1, fs_get_allocation_callbacks(pFS)); + if (pActualPathCleanHeap == NULL) { + fs_free(pActualPathHeap, fs_get_allocation_callbacks(pFS)); + return FS_OUT_OF_MEMORY; + } + + fs_path_normalize(pActualPathCleanHeap, actualPathCleanLen + 1, pActualPath, FS_NULL_TERMINATED, cleanOptions); /* <-- This should never fail. */ + pActualPathClean = pActualPathCleanHeap; + } else { + pActualPathClean = pActualPathCleanStack; + } + + fs_free(pActualPathHeap, fs_get_allocation_callbacks(pFS)); + pActualPathHeap = NULL; + + + /* We now have enough information to open the file. */ + result = fs_file_alloc_if_necessary_and_open_or_info(pFS, pActualPathClean, openMode, ppFile, pInfo); + + fs_free(pActualPathCleanHeap, fs_get_allocation_callbacks(pFS)); + pActualPathCleanHeap = NULL; + + if (result == FS_SUCCESS) { + return FS_SUCCESS; + } else { + return FS_DOES_NOT_EXIST; /* Couldn't find the file from the best mount point. */ + } } else { - pActualPathClean = pActualPathCleanStack; - } - - fs_free(pActualPathHeap, fs_get_allocation_callbacks(pFS)); - pActualPathHeap = NULL; - - - /* We now have enough information to open the file. */ - result = fs_file_alloc_if_necessary_and_open_or_info(pFS, pActualPathClean, openMode, ppFile, pInfo); - - fs_free(pActualPathCleanHeap, fs_get_allocation_callbacks(pFS)); - pActualPathCleanHeap = NULL; - - if (result == FS_SUCCESS) { - return FS_SUCCESS; - } else { - return FS_DOES_NOT_EXIST; /* Couldn't find the file from the best mount point. */ + return FS_DOES_NOT_EXIST; /* Couldn't find an appropriate mount point. */ } } else { - return FS_DOES_NOT_EXIST; /* Couldn't find an appropriate mount point. */ + /* + No "fs" object was supplied. Open using the default backend without using mount points. This is as if you were + opening a file using `fopen()`. + */ + if ((openMode & FS_ONLY_MOUNTS) == 0) { + return fs_file_alloc_if_necessary_and_open_or_info(pFS, pFilePath, openMode, ppFile, pInfo); + } else { + /* + Getting here means only the mount points can be used to open the file (cannot open straight from + the file system natively). + */ + return FS_DOES_NOT_EXIST; + } } } else { /* Opening in read mode. */ diff --git a/external/fs/fs.h b/external/fs/fs.h index 5d08c4cc..ecebf390 100644 --- a/external/fs/fs.h +++ b/external/fs/fs.h @@ -267,9 +267,14 @@ be saved: ```c fs_file_open(pFS, "config/game.cfg", FS_WRITE, &pFile); // Prefixed with "config", so will use the "config" mount point. -fs_file_open(pFs, "saves/save0.sav", FS_WRITE, &pFile); // Prefixed with "saves", so will use the "saves" mount point. +fs_file_open(pFS, "saves/save0.sav", FS_WRITE, &pFile); // Prefixed with "saves", so will use the "saves" mount point. ``` +When opening a file for writing, if you pass in NULL for the `pFS` parameter it will open the file +like normal using the standard file system. That is it'll work exactly as if you were using stdio +`fopen()` and you will not be able use mount points. Keep in mind that there is no notion of a +"current directory" in this library so you'll be stuck with the initial working directory. + By default, you can move outside the mount point with ".." segments. If you want to disable this functionality, you can use the `FS_NO_ABOVE_ROOT_NAVIGATION` flag: