diff --git a/src/d_main.c b/src/d_main.c index 89579ea91c2644baffcc4e5ed29306cfcefceebf..b4b668f4bc0c8b984800cae319862f7b2aa2fe7c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -944,13 +944,13 @@ static void D_AddFile(char **list, const char *file) static void D_AddFolder(char **list, const char *file) { - size_t pnumwadfiles, len = strlen(file); + size_t pnumwadfiles; char *newfile; for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) ; - newfile = malloc(len + 2); // NULL terminator + path separator + newfile = malloc(strlen(file) + 2); // Path delimiter + NULL terminator if (!newfile) I_Error("No more free memory to AddFolder %s",file); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ea0d9ca80684e8a741978325b8717c3a56af1484..1e4e2b464ab0073978d9570acbabf1d609f21d58 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1518,7 +1518,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { illegalMask &= ~(1 << i); } - + if ((p->availabilities & illegalMask) != 0) { kick = true; @@ -3416,9 +3416,10 @@ static void Command_Addfolder(void) for (curarg = 1; curarg < argc; curarg++) { const char *fn, *p; + char *fullpath; char buf[256]; char *buf_p = buf; - INT32 i; + INT32 i, stat; size_t ii; boolean folderadded = false; @@ -3461,6 +3462,13 @@ static void Command_Addfolder(void) break; ++p; + // Don't add an empty path. + if (M_IsStringEmpty(fn)) + { + CONS_Alert(CONS_WARNING, M_GetText("Folder name is empty, skipping\n")); + continue; + } + // check total packet size and no of files currently loaded // See W_InitFile in w_wad.c if ((numwadfiles >= MAX_WADFILES) @@ -3470,10 +3478,52 @@ static void Command_Addfolder(void) return; } - WRITESTRINGN(buf_p,p,240); + // Check if the path is valid. + stat = W_IsPathToFolderValid(fn); + + if (stat == 0) + { + CONS_Alert(CONS_WARNING, M_GetText("Path %s is invalid, skipping\n"), fn); + continue; + } + else if (stat < 0) + { +#ifndef AVOID_ERRNO + CONS_Alert(CONS_WARNING, M_GetText("Error accessing %s (%s), skipping\n"), fn, strerror(direrror)); +#else + CONS_Alert(CONS_WARNING, M_GetText("Error accessing %s, skipping\n"), fn); +#endif + continue; + } + + // Get the full path for this folder. + fullpath = W_GetFullFolderPath(fn); + + if (fullpath == NULL) + { + CONS_Alert(CONS_WARNING, M_GetText("Path %s is invalid, skipping\n"), fn); + continue; + } + + // Check if the folder is already added. + for (i = 0; i < numwadfiles; i++) + { + if (wadfiles[i]->type != RET_FOLDER) + continue; + + if (samepaths(wadfiles[i]->path, fullpath) > 0) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), fn); + continue; + } + } + + Z_Free(fullpath); addedfolders[numfoldersadded++] = fn; + WRITESTRINGN(buf_p,p,240); + if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file SendNetXCmd(XD_REQADDFOLDER, buf, buf_p - buf); else diff --git a/src/d_netfil.c b/src/d_netfil.c index 15f9f1ff5ce7f00d7ae367484f531a34e706d5db..12c5ee6a211640046b74bd1b7ade6fcb811e1ff0 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1584,20 +1584,23 @@ filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean complet return (badmd5 ? FS_MD5SUMBAD : FS_NOTFOUND); // md5 sum bad or file not found } +// Searches for a folder. +// This can be used with a full path, or an incomplete path. +// In the latter case, the function will try to find folders in +// srb2home, srb2path, and the current directory. filestatus_t findfolder(const char *path) { // Check the path by itself first. - if (checkfolderpath(path, NULL, true)) + if (concatpaths(path, NULL) == 1) return FS_FOUND; -#define checkpath(startpath) { \ - if (checkfolderpath(path, startpath, true)) \ - return FS_FOUND; \ - } +#define checkpath(startpath) \ + if (concatpaths(path, startpath) == 1) \ + return FS_FOUND - checkpath(srb2home) // Then, look in srb2home. - checkpath(srb2path) // Now, look in srb2path. - checkpath(".") // Finally, look in ".". + checkpath(srb2home); // Then, look in srb2home. + checkpath(srb2path); // Now, look in srb2path. + checkpath("."); // Finally, look in the current directory. #undef checkpath diff --git a/src/filesrch.c b/src/filesrch.c index f01fc0bd2e236a9b74625ecb089e2e131571969f..e5c9b21589f74d3cb39f0d88fa905f20f577d385 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -341,8 +341,8 @@ char *refreshdirname = NULL; size_t packetsizetally = 0; size_t mainwadstally = 0; -#define folderpathlen 1024 -#define maxfolderdepth 48 +#define dirpathlen 1024 +#define maxdirdepth 48 #define isuptree(dirent) ((dirent)[0]=='.' && ((dirent)[1]=='\0' || ((dirent)[1]=='.' && (dirent)[2]=='\0'))) @@ -448,182 +448,227 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want return retval; } -// Called from findfolder and ResGetLumpsFolder in w_wad.c. -// Call with cleanup true if the path has to be verified. -boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup) +#ifndef AVOID_ERRNO +int direrror = 0; +#endif + +// Checks if the specified path is a directory. +// Returns 1 if so, 0 if not, and -1 if an error occurred. +// direrror is set if there was an error. +INT32 pathisdirectory(const char *path) { - char folderpath[folderpathlen], basepath[folderpathlen], *fn = NULL; - DIR *dirhandle; + struct stat fsstat; - // Remove path separators from the filename, and don't try adding "/". - // See also the same code in W_InitFolder. - if (cleanup) + if (stat(path, &fsstat) < 0) { - const char *p = path + strlen(path); - size_t len; +#ifndef AVOID_ERRNO + direrror = errno; +#endif + return -1; + } + else if (S_ISDIR(fsstat.st_mode)) + return 1; - --p; - while (*p == '\\' || *p == '/' || *p == ':') - { - p--; - if (p < path) - return false; - } - ++p; + return 0; +} - // Allocate the new path name. - len = (p - path) + 1; - fn = ZZ_Alloc(len); - strlcpy(fn, path, len); - } +// Concatenates two paths, and checks if it is a directory that can be opened. +// Returns 1 if so, 0 if not, and -1 if an error occurred. +INT32 concatpaths(const char *path, const char *startpath) +{ + char dirpath[dirpathlen]; + DIR *dirhandle; + INT32 stat; if (startpath) { + char basepath[dirpathlen]; + snprintf(basepath, sizeof basepath, "%s" PATHSEP, startpath); + snprintf(dirpath, sizeof dirpath, "%s%s", basepath, path); - if (cleanup) - { - snprintf(folderpath, sizeof folderpath, "%s%s", basepath, fn); - Z_Free(fn); // Don't need this anymore. - } - else - snprintf(folderpath, sizeof folderpath, "%s%s", basepath, path); + // Base path and directory path are the same? Not valid. + stat = samepaths(basepath, dirpath); - // Home path and folder path are the same? Not valid. - if (!strcmp(basepath, folderpath)) - return false; + if (stat == 1) + return 0; + else if (stat < 0) + return -1; } - else if (cleanup) + else + snprintf(dirpath, sizeof dirpath, "%s", path); + + // Check if the path is a directory. + // Will return -1 if there was an error. + stat = pathisdirectory(dirpath); + if (stat == 0) + return 0; + else if (stat < 0) { - snprintf(folderpath, sizeof folderpath, "%s", fn); - Z_Free(fn); // Don't need this anymore. + // The path doesn't exist, so it can't be a directory. + if (direrror == ENOENT) + return 0; + + return -1; } - else - snprintf(folderpath, sizeof folderpath, "%s", path); - dirhandle = opendir(folderpath); + // Open the directory. + // Will return 0 if it couldn't be opened. + dirhandle = opendir(dirpath); if (dirhandle == NULL) - return false; + return 0; else closedir(dirhandle); - return true; -} - -INT32 pathisfolder(const char *path) -{ - struct stat fsstat; - - if (stat(path, &fsstat) < 0) - return -1; - else if (S_ISDIR(fsstat.st_mode)) - return 1; - - return 0; + return 1; } +// Checks if two paths are the same. Returns 1 if so, and 0 if not. +// Returns -1 if an error occurred with the first path, +// and returns -2 if an error occurred with the second path. +// direrror is set if there was an error. INT32 samepaths(const char *path1, const char *path2) { struct stat stat1; struct stat stat2; if (stat(path1, &stat1) < 0) + { +#ifndef AVOID_ERRNO + direrror = errno; +#endif return -1; + } if (stat(path2, &stat2) < 0) - return -1; + { +#ifndef AVOID_ERRNO + direrror = errno; +#endif + return -2; + } if (stat1.st_dev == stat2.st_dev) { #if !defined(_WIN32) return (stat1.st_ino == stat2.st_ino); #else + // The above doesn't work on NTFS or FAT. HANDLE file1 = CreateFileA(path1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); HANDLE file2 = CreateFileA(path2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); BY_HANDLE_FILE_INFORMATION file1info, file2info; - boolean ok = false; - if (file1 != INVALID_HANDLE_VALUE && file2 != INVALID_HANDLE_VALUE) + if (file1 == INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandle(file1, &file1info) && GetFileInformationByHandle(file2, &file2info)) - { - if (file1info.dwVolumeSerialNumber == file2info.dwVolumeSerialNumber - && file1info.nFileIndexLow == file2info.nFileIndexLow - && file1info.nFileIndexHigh == file2info.nFileIndexHigh) - ok = true; - } +#ifndef AVOID_ERRNO + direrror = ENOENT; +#endif + return -1; + } + else if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle(file1); +#ifndef AVOID_ERRNO + direrror = ENOENT; +#endif + return -2; + } + + // I have no idea why GetFileInformationByHandle would fail. + // Microsoft's documentation doesn't tell me. + // I'll just use EIO... + if (!GetFileInformationByHandle(file1, &file1info)) + { +#ifndef AVOID_ERRNO + direrror = EIO; +#endif + return -1; + } + else if (!GetFileInformationByHandle(file2, &file2info)) + { + CloseHandle(file1); + CloseHandle(file2); +#ifndef AVOID_ERRNO + direrror = EIO; +#endif + return -2; } - if (file1 != INVALID_HANDLE_VALUE) + if (file1info.dwVolumeSerialNumber == file2info.dwVolumeSerialNumber + && file1info.nFileIndexLow == file2info.nFileIndexLow + && file1info.nFileIndexHigh == file2info.nFileIndexHigh) + { CloseHandle(file1); - if (file2 != INVALID_HANDLE_VALUE) CloseHandle(file2); + return 1; + } - return ok; + return 0; #endif } - return false; + return 0; } // -// Folder loading +// Directory loading // -static void initfolderpath(char *folderpath, size_t *folderpathindex, int depthleft) +static void initdirpath(char *dirpath, size_t *dirpathindex, int depthleft) { - folderpathindex[depthleft] = strlen(folderpath) + 1; + dirpathindex[depthleft] = strlen(dirpath) + 1; - if (folderpath[folderpathindex[depthleft]-2] != PATHSEP[0]) + if (dirpath[dirpathindex[depthleft]-2] != PATHSEP[0]) { - folderpath[folderpathindex[depthleft]-1] = PATHSEP[0]; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]-1] = PATHSEP[0]; + dirpath[dirpathindex[depthleft]] = 0; } else - folderpathindex[depthleft]--; + dirpathindex[depthleft]--; } -lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders) { DIR **dirhandle; struct dirent *dent; struct stat fsstat; - int rootfolder = (maxfolderdepth - 1); - int depthleft = rootfolder; + int rootdir = (maxdirdepth - 1); + int depthleft = rootdir; - char folderpath[folderpathlen]; - size_t *folderpathindex; + char dirpath[dirpathlen]; + size_t *dirpathindex; lumpinfo_t *lumpinfo, *lump_p; - UINT16 i = 0, numlumps = (*nlmp); + UINT16 i = 0, numlumps = 0; + + boolean failure = false; - dirhandle = (DIR **)malloc(maxfolderdepth * sizeof (DIR*)); - folderpathindex = (size_t *)malloc(maxfolderdepth * sizeof(size_t)); + dirhandle = (DIR **)malloc(maxdirdepth * sizeof (DIR*)); + dirpathindex = (size_t *)malloc(maxdirdepth * sizeof(size_t)); // Open the root directory - strlcpy(folderpath, path, folderpathlen); - dirhandle[depthleft] = opendir(folderpath); + strlcpy(dirpath, path, dirpathlen); + dirhandle[depthleft] = opendir(dirpath); if (dirhandle[depthleft] == NULL) { free(dirhandle); - free(folderpathindex); + free(dirpathindex); return NULL; } - initfolderpath(folderpath, folderpathindex, depthleft); - (*nfiles) = 0; + initdirpath(dirpath, dirpathindex, depthleft); (*nfolders) = 0; // Count files and directories - while (depthleft < maxfolderdepth) + while (depthleft < maxdirdepth) { - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]] = 0; dent = readdir(dirhandle[depthleft]); if (!dent) { - if (depthleft != rootfolder) // Don't close the root directory + if (depthleft != rootdir) // Don't close the root directory closedir(dirhandle[depthleft]); depthleft++; continue; @@ -631,62 +676,67 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 else if (isuptree(dent->d_name)) continue; - strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + strcpy(&dirpath[dirpathindex[depthleft]], dent->d_name); - if (stat(folderpath, &fsstat) < 0) + if (stat(dirpath, &fsstat) < 0) ; else if (S_ISDIR(fsstat.st_mode) && depthleft) { - folderpathindex[--depthleft] = strlen(folderpath) + 1; - dirhandle[depthleft] = opendir(folderpath); + dirpathindex[--depthleft] = strlen(dirpath) + 1; + dirhandle[depthleft] = opendir(dirpath); if (dirhandle[depthleft]) - { - numlumps++; (*nfolders)++; - } else depthleft++; - folderpath[folderpathindex[depthleft]-1] = '/'; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]-1] = '/'; + dirpath[dirpathindex[depthleft]] = 0; } else - { numlumps++; - (*nfiles)++; - } - if (numlumps == (UINT16_MAX-1)) + // Failure: Too many files. + if (numlumps == UINT16_MAX) + { + (*nlmp) = UINT16_MAX; + failure = true; break; + } } // Failure: No files have been found. - if (!(*nfiles)) + if (!numlumps) { - (*nfiles) = UINT16_MAX; - free(folderpathindex); + (*nlmp) = 0; + failure = true; + } + + // Close any open directories and return if something went wrong. + if (failure) + { + free(dirpathindex); free(dirhandle); - for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + for (; depthleft < maxdirdepth; closedir(dirhandle[depthleft++])); return NULL; } // Create the files and directories as lump entries // It's possible to create lumps and count files at the same time, - // but I didn't to constantly have to reallocate memory for every lump. - rewinddir(dirhandle[rootfolder]); - depthleft = rootfolder; + // but I didn't want to have to reallocate memory for every lump. + rewinddir(dirhandle[rootdir]); + depthleft = rootdir; - strlcpy(folderpath, path, folderpathlen); - initfolderpath(folderpath, folderpathindex, depthleft); + strlcpy(dirpath, path, dirpathlen); + initdirpath(dirpath, dirpathindex, depthleft); lump_p = lumpinfo = Z_Calloc(numlumps * sizeof(lumpinfo_t), PU_STATIC, NULL); - while (depthleft < maxfolderdepth) + while (depthleft < maxdirdepth) { char *fullname, *trimname; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]] = 0; dent = readdir(dirhandle[depthleft]); if (!dent) @@ -697,29 +747,30 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 else if (isuptree(dent->d_name)) continue; - strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + strcpy(&dirpath[dirpathindex[depthleft]], dent->d_name); - if (stat(folderpath, &fsstat) < 0) + if (stat(dirpath, &fsstat) < 0) continue; else if (S_ISDIR(fsstat.st_mode) && depthleft) { - folderpathindex[--depthleft] = strlen(folderpath) + 1; - dirhandle[depthleft] = opendir(folderpath); + dirpathindex[--depthleft] = strlen(dirpath) + 1; + dirhandle[depthleft] = opendir(dirpath); - if (!dirhandle[depthleft]) + if (dirhandle[depthleft]) { - depthleft++; - continue; + dirpath[dirpathindex[depthleft]-1] = '/'; + dirpath[dirpathindex[depthleft]] = 0; } + else + depthleft++; - folderpath[folderpathindex[depthleft]-1] = '/'; - folderpath[folderpathindex[depthleft]] = 0; + continue; } - lump_p->diskpath = Z_StrDup(folderpath); // Path in the filesystem to the file + lump_p->diskpath = Z_StrDup(dirpath); // Path in the filesystem to the file lump_p->compression = CM_NOCOMPRESSION; // Lump is uncompressed - // Remove the folder path. + // Remove the directory's path. fullname = lump_p->diskpath; if (strstr(fullname, path)) fullname += strlen(path) + 1; @@ -747,7 +798,7 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 lump_p->longname = Z_Calloc(1, PU_STATIC, NULL); // The complete name of the file, with its extension, - // excluding the path of the folder where it resides. + // excluding the path of the directory where it resides. lump_p->fullname = Z_StrDup(fullname); lump_p++; @@ -755,12 +806,12 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 if (i > numlumps || i == (UINT16_MAX-1)) { - for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + for (; depthleft < maxdirdepth; closedir(dirhandle[depthleft++])); // Close any open directories. break; } } - free(folderpathindex); + free(dirpathindex); free(dirhandle); (*nlmp) = numlumps; diff --git a/src/filesrch.h b/src/filesrch.h index 92e3341f34675bd0ddb97ff3492a7daf6bba601a..e358d799356d99643aecaac6d39c56d2ac2906e3 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -29,11 +29,15 @@ extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_sh filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth); -INT32 pathisfolder(const char *path); -boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup); +INT32 pathisdirectory(const char *path); INT32 samepaths(const char *path1, const char *path2); +boolean concatpaths(const char *path, const char *startpath); -lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders); +#ifndef AVOID_ERRNO +extern int direrror; +#endif + +lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders); #define menudepth 20 diff --git a/src/m_misc.c b/src/m_misc.c index 61f0f8a5b652a23154548d6112761e4d15ad8690..59783d5d30dc8d195d5b732d2de474833459f816 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -2694,7 +2694,7 @@ boolean M_IsStringEmpty(const char *s) { const char *ch = s; - if (ch == NULL || (ch && strlen(ch) < 1)) + if (s == NULL || s[0] == '\0') return true; for (;;ch++) diff --git a/src/w_wad.c b/src/w_wad.c index e37c86bacde53017c1618b49a0877466e27c4bc0..3ff301117bca03d03868e7dead7bb5fa03e44c2b 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -687,11 +687,67 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) return lumpinfo; } +static INT32 CheckPathsNotEqual(const char *path1, const char *path2) +{ + INT32 stat = samepaths(path1, path2); + + if (stat == 1) + return 0; + else if (stat < 0) + return -1; + + return 1; +} + +// Returns 1 if the path is valid, 0 if not, and -1 if there was an error. +INT32 W_IsPathToFolderValid(const char *path) +{ + INT32 stat; + + // Remove path delimiters. + const char *p = path + (strlen(path) - 1); + while (*p == '\\' || *p == '/' || *p == ':') + { + p--; + if (p < path) + return 0; + } + + // Check if the path is a directory. + stat = pathisdirectory(path); + if (stat == 0) + return 0; + else if (stat < 0) + { + // The path doesn't exist, so it can't be a directory. + if (direrror == ENOENT) + return 0; + + return -1; + } + + // Don't add your home, you sodding tic tac. + stat = CheckPathsNotEqual(path, srb2home); + if (stat != 1) + return stat; + + // Do the same checks for SRB2's path, and the current directory. + stat = CheckPathsNotEqual(path, srb2path); + if (stat != 1) + return stat; + + stat = CheckPathsNotEqual(path, "."); + if (stat != 1) + return stat; + + return 1; +} + // Checks if the combination of the first path and the second path are valid. // If they are, the concatenated path is returned. -static char *W_CheckFolderPath(const char *startpath, const char *path) +static char *CheckConcatFolderPath(const char *startpath, const char *path) { - if (checkfolderpath(path, startpath, false)) + if (concatpaths(path, startpath) == 1) { char *fn; @@ -710,23 +766,23 @@ static char *W_CheckFolderPath(const char *startpath, const char *path) return NULL; } -// Returns the first valid path for a folder. -static char *W_GetFullFolderPath(const char *path) +// Looks for the first valid full path for a folder. +// Returns NULL if the folder doesn't exist, or it isn't valid. +char *W_GetFullFolderPath(const char *path) { // Check the path by itself first. - char *fn = W_CheckFolderPath(NULL, path); + char *fn = CheckConcatFolderPath(NULL, path); if (fn) return fn; -#define checkpath(startpath) { \ - fn = W_CheckFolderPath(startpath, path); \ +#define checkpath(startpath) \ + fn = CheckConcatFolderPath(startpath, path); \ if (fn) \ - return fn; \ -} \ + return fn - checkpath(srb2home) // Then, look in srb2home. - checkpath(srb2path) // Now, look in srb2path. - checkpath(".") // Finally, look in ".". + checkpath(srb2home); // Then, look in srb2home. + checkpath(srb2path); // Now, look in srb2path. + checkpath("."); // Finally, look in the current directory. #undef checkpath @@ -734,9 +790,9 @@ static char *W_GetFullFolderPath(const char *path) } // Loads files from a folder into a lumpinfo structure. -static lumpinfo_t *ResGetLumpsFolder(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +static lumpinfo_t *ResGetLumpsFolder(const char *path, UINT16 *nlmp, UINT16 *nfolders) { - return getfolderfiles(path, nlmp, nfiles, nfolders); + return getdirectoryfiles(path, nlmp, nfolders); } static UINT16 W_InitFileError (const char *filename, boolean exitworthy) @@ -908,7 +964,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) wadfile->type = type; wadfile->handle = handle; wadfile->numlumps = numlumps; - wadfile->filecount = wadfile->foldercount = 0; + wadfile->foldercount = 0; wadfile->lumpinfo = lumpinfo; wadfile->important = important; fseek(handle, 0, SEEK_END); @@ -966,11 +1022,12 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) lumpinfo_t *lumpinfo = NULL; wadfile_t *wadfile; UINT16 numlumps = 0; - UINT16 filecount, foldercount; + UINT16 foldercount; size_t i; char *fn, *fullpath; const char *p; int important; + INT32 stat; if (!(refreshdirmenu & REFRESHDIR_ADDFILE)) refreshdirmenu = REFRESHDIR_NORMAL|REFRESHDIR_ADDFILE; // clean out cons_alerts that happened earlier @@ -1006,16 +1063,15 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) packetsizetally = packetsize; } - // Remove path separators from the filename, and don't try adding "/". - p = path+strlen(path); - --p; + // Remove path delimiters. + p = path + (strlen(path) - 1); while (*p == '\\' || *p == '/' || *p == ':') { p--; if (p < path) { - CONS_Alert(CONS_ERROR, M_GetText("Path %s is prohibited\n"), path); + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), path); return W_InitFileError(path, startup); } } @@ -1026,6 +1082,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) fn = ZZ_Alloc(i); strlcpy(fn, path, i); + // Don't add an empty path. if (M_IsStringEmpty(fn)) { CONS_Alert(CONS_ERROR, M_GetText("Folder name is empty\n")); @@ -1037,14 +1094,36 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) return W_InitFileError("a folder", false); } - // Get the full path for this filename. + // Check if the path is valid. + stat = W_IsPathToFolderValid(fn); + + if (stat != 1) + { + if (stat == 0) + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), fn); + else if (stat < 0) + { +#ifndef AVOID_ERRNO + CONS_Alert(CONS_ERROR, M_GetText("Could not stat %s: %s\n"), fn, strerror(direrror)); +#else + CONS_Alert(CONS_ERROR, M_GetText("Could not stat %s\n"), fn); +#endif + } + + Z_Free(fn); + return W_InitFileError(path, startup); + } + + // Get the full path for this folder. fullpath = W_GetFullFolderPath(fn); if (fullpath == NULL) { + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), fn); Z_Free(fn); - return W_InitFileError(path, false); + return W_InitFileError(path, startup); } + // Check if the folder is already added. for (i = 0; i < numwadfiles; i++) { if (wadfiles[i]->type != RET_FOLDER) @@ -1061,11 +1140,16 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) } } - lumpinfo = ResGetLumpsFolder(fullpath, &numlumps, &filecount, &foldercount); + lumpinfo = ResGetLumpsFolder(fullpath, &numlumps, &foldercount); + if (lumpinfo == NULL) { - if (filecount == UINT16_MAX) + if (!numlumps) CONS_Alert(CONS_ERROR, M_GetText("Folder %s is empty\n"), path); + else if (numlumps == UINT16_MAX) + CONS_Alert(CONS_ERROR, M_GetText("Folder %s contains too many files\n"), path); + else + CONS_Alert(CONS_ERROR, M_GetText("Unknown error enumerating files from folder %s\n"), path); Z_Free(fn); Z_Free(fullpath); @@ -1082,7 +1166,6 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) wadfile->type = RET_FOLDER; wadfile->handle = NULL; wadfile->numlumps = numlumps; - wadfile->filecount = filecount; wadfile->foldercount = foldercount; wadfile->lumpinfo = lumpinfo; wadfile->important = important; @@ -1094,7 +1177,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) Z_Calloc(numlumps * sizeof (*wadfile->lumpcache), PU_STATIC, &wadfile->lumpcache); Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache); - CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, filecount, foldercount); + CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, numlumps, foldercount); wadfiles[numwadfiles] = wadfile; numwadfiles++; @@ -1506,12 +1589,24 @@ size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump) l = wadfiles[wad]->lumpinfo + lump; + // Open the external file for this lump, if the WAD is a folder. if (wadfiles[wad]->type == RET_FOLDER) { - INT32 stat = pathisfolder(l->diskpath); + // pathisdirectory calls stat, so if anything wrong has happened, + // this is the time to be aware of it. + INT32 stat = pathisdirectory(l->diskpath); if (stat < 0) - I_Error("W_LumpLengthPwad: could not stat %s", l->diskpath); + { +#ifndef AVOID_ERRNO + if (direrror == ENOENT) + I_Error("W_LumpLengthPwad: file %s doesn't exist", l->diskpath); + else + I_Error("W_LumpLengthPwad: could not stat %s: %s", l->diskpath, strerror(direrror)); +#else + I_Error("W_LumpLengthPwad: could not access %s", l->diskpath); +#endif + } else if (stat == 1) // Path is a folder. return 0; else @@ -1617,7 +1712,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si lumpinfo_t *l; FILE *handle = NULL; - if (!TestValidLump(wad,lump)) + if (!TestValidLump(wad, lump)) return 0; l = wadfiles[wad]->lumpinfo + lump; @@ -1625,10 +1720,21 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si // Open the external file for this lump, if the WAD is a folder. if (wadfiles[wad]->type == RET_FOLDER) { - INT32 stat = pathisfolder(l->diskpath); + // pathisdirectory calls stat, so if anything wrong has happened, + // this is the time to be aware of it. + INT32 stat = pathisdirectory(l->diskpath); if (stat < 0) - I_Error("W_ReadLumpHeaderPwad: could not stat %s", l->diskpath); + { +#ifndef AVOID_ERRNO + if (direrror == ENOENT) + I_Error("W_ReadLumpHeaderPwad: file %s doesn't exist", l->diskpath); + else + I_Error("W_ReadLumpHeaderPwad: could not stat %s: %s", l->diskpath, strerror(direrror)); +#else + I_Error("W_ReadLumpHeaderPwad: could not access %s", l->diskpath); +#endif + } else if (stat == 1) // Path is a folder. return 0; else diff --git a/src/w_wad.h b/src/w_wad.h index 25b4bffa8399eae0e6fd908b4b7c2515c35bd505..3958f7faff311da87b0d22ca320c39a8ae88f9cb 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -122,7 +122,7 @@ typedef struct wadfile_s lumpcache_t *lumpcache; lumpcache_t *patchcache; UINT16 numlumps; // this wad's number of resources - UINT16 filecount, foldercount; // file and folder count + UINT16 foldercount; // folder count FILE *handle; UINT32 filesize; // for network UINT8 md5sum[16]; @@ -152,6 +152,9 @@ void W_InitMultipleFiles(char **filenames); #define W_FileHasFolders(wadfile) ((wadfile)->type == RET_PK3 || (wadfile)->type == RET_FOLDER) +boolean W_IsPathToFolderValid(const char *path); +char *W_GetFullFolderPath(const char *path); + const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum);