X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=218a85e4b6dd3410e9617e4fea7d7f6d37b4b32c;hb=8d7671158db57358a5f7eec510adbd05cdf62189;hp=4bf26d4d3ab1949ff89d27235a9e84cb1e8dd98d;hpb=acfcae9e93eeefbfea08b6b39b0a67dafd9024be;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index 4bf26d4d..218a85e4 100644 --- a/fs.c +++ b/fs.c @@ -25,8 +25,6 @@ #include "quakedef.h" -#include -#include #include #include @@ -45,6 +43,16 @@ #include "fs.h" +// use syscalls instead of f* functions +#define FS_USESYSCALLS + +// Win32 requires us to add O_BINARY, but the other OSes don't have it +#ifdef FS_USESYSCALLS +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + /* @@ -152,7 +160,11 @@ typedef struct struct qfile_s { fs_flags_t flags; +#ifdef FS_USESYSCALLS + int stream; +#else FILE* stream; +#endif size_t length; // file size on disk (PACKED only) size_t offset; // offset into a package (PACKED only) size_t position; // current position in the file (PACKED only) @@ -212,7 +224,11 @@ typedef struct typedef struct pack_s { char filename [MAX_OSPATH]; +#ifdef FS_USESYSCALLS + int handle; +#else FILE *handle; +#endif int ignorecase; // PK3 ignores case int numfiles; packfile_t *files; @@ -338,6 +354,8 @@ qboolean PK3_OpenLibrary (void) #ifdef WIN32 dllname = "zlib.dll"; +#elif defined(MACOSX) + dllname = "libz.dylib"; #else dllname = "libz.so"; #endif @@ -361,15 +379,23 @@ PK3_GetEndOfCentralDir Extract the end of the central directory from a PK3 package ==================== */ +#ifdef FS_USESYSCALLS +qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd) +#else qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd) +#endif { long filesize, maxsize; qbyte *buffer, *ptr; int ind; // Get the package size +#ifdef FS_USESYSCALLS + filesize = lseek (packhandle, 0, SEEK_END); +#else fseek (packhandle, 0, SEEK_END); - filesize = ftell (packhandle); + filesize = ftell(packhandle); +#endif if (filesize < ZIP_END_CDIR_SIZE) return false; @@ -379,8 +405,13 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_end else maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE; buffer = Mem_Alloc (tempmempool, maxsize); +#ifdef FS_USESYSCALLS + lseek (packhandle, filesize - maxsize, SEEK_SET); + if (read (packhandle, buffer, maxsize) != (ssize_t) maxsize) +#else fseek (packhandle, filesize - maxsize, SEEK_SET); - if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize) + if (fread (buffer, 1, maxsize, packhandle) != (size_t) maxsize) +#endif { Mem_Free (buffer); return false; @@ -433,8 +464,13 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) // Load the central directory in memory central_dir = Mem_Alloc (tempmempool, eocd->cdir_size); +#ifdef FS_USESYSCALLS + lseek (pack->handle, eocd->cdir_offset, SEEK_SET); + read (pack->handle, central_dir, eocd->cdir_size); +#else fseek (pack->handle, eocd->cdir_offset, SEEK_SET); fread (central_dir, 1, eocd->cdir_size, pack->handle); +#endif // Extract the files properties // The parsing is done "by hand" because some fields have variable sizes and @@ -509,7 +545,9 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) remaining -= count; } - Mem_Free (central_dir); + // If the package is empty, central_dir is NULL here + if (central_dir != NULL) + Mem_Free (central_dir); return pack->numfiles; } @@ -523,14 +561,24 @@ Create a package entry associated with a PK3 file */ pack_t *FS_LoadPackPK3 (const char *packfile) { +#ifdef FS_USESYSCALLS + int packhandle; +#else FILE *packhandle; +#endif pk3_endOfCentralDir_t eocd; pack_t *pack; int real_nb_files; +#ifdef FS_USESYSCALLS + packhandle = open (packfile, O_RDONLY | O_BINARY); + if (packhandle < 0) + return NULL; +#else packhandle = fopen (packfile, "rb"); if (!packhandle) return NULL; +#endif if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd)) Sys_Error ("%s is not a PK3 file", packfile); @@ -541,10 +589,10 @@ pack_t *FS_LoadPackPK3 (const char *packfile) // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535 // since eocd.nbentries is an unsigned 16 bits integer - #if MAX_FILES_IN_PACK < 65535 +#if MAX_FILES_IN_PACK < 65535 if (eocd.nbentries > MAX_FILES_IN_PACK) Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries); - #endif +#endif // Create a package structure in memory pack = Mem_Alloc (pak_mempool, sizeof (pack_t)); @@ -552,13 +600,13 @@ pack_t *FS_LoadPackPK3 (const char *packfile) strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = eocd.nbentries; - pack->mempool = Mem_AllocPool (packfile); + pack->mempool = Mem_AllocPool (packfile, 0, NULL); pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t)); pack->next = packlist; packlist = pack; real_nb_files = PK3_BuildFileList (pack, &eocd); - if (real_nb_files <= 0) + if (real_nb_files < 0) Sys_Error ("%s is not a valid PK3 file", packfile); Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files); @@ -583,8 +631,13 @@ void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack) return; // Load the local file description +#ifdef FS_USESYSCALLS + lseek (pack->handle, file->offset, SEEK_SET); + count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE); +#else fseek (pack->handle, file->offset, SEEK_SET); count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle); +#endif if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER) Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename); @@ -616,28 +669,29 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, size_t realsize, file_flags_t flags) { int (*strcmp_funct) (const char* str1, const char* str2); - size_t left, right, middle; - int diff; + int left, right, middle; packfile_t *file; strcmp_funct = pack->ignorecase ? strcasecmp : strcmp; // Look for the slot we should put that file into (binary search) left = 0; - right = pack->numfiles; - while (left != right) + right = pack->numfiles - 1; + while (left <= right) { - middle = (left + right - 1) / 2; + int diff; + + middle = (left + right) / 2; diff = strcmp_funct (pack->files[middle].name, name); // If we found the file, there's a problem if (!diff) - Sys_Error ("Package %s contains several time the file %s\n", + Sys_Error ("Package %s contains the file %s several times\n", pack->filename, name); // If we're too far in the list if (diff > 0) - right = middle; + right = middle - 1; else left = middle + 1; } @@ -719,15 +773,25 @@ pack_t *FS_LoadPackPAK (const char *packfile) { dpackheader_t header; int i, numpackfiles; +#ifdef FS_USESYSCALLS + int packhandle; +#else FILE *packhandle; +#endif pack_t *pack; dpackfile_t *info; // temporary alloc, allowing huge pack directories +#ifdef FS_USESYSCALLS + packhandle = open (packfile, O_RDONLY | O_BINARY); + if (packhandle < 0) + return NULL; + read (packhandle, (void *)&header, sizeof(header)); +#else packhandle = fopen (packfile, "rb"); if (!packhandle) return NULL; - fread ((void *)&header, 1, sizeof(header), packhandle); +#endif if (memcmp(header.id, "PACK", 4)) Sys_Error ("%s is not a packfile", packfile); header.dirofs = LittleLong (header.dirofs); @@ -746,14 +810,19 @@ pack_t *FS_LoadPackPAK (const char *packfile) strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = 0; - pack->mempool = Mem_AllocPool(packfile); + pack->mempool = Mem_AllocPool(packfile, 0, NULL); pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t)); pack->next = packlist; packlist = pack; info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles); +#ifdef FS_USESYSCALLS + lseek (packhandle, header.dirofs, SEEK_SET); + read (packhandle, (void *)info, header.dirlen); +#else fseek (packhandle, header.dirofs, SEEK_SET); fread ((void *)info, 1, header.dirlen, packhandle); +#endif // parse the directory for (i = 0;i < numpackfiles;i++) @@ -788,14 +857,6 @@ void FS_AddGameDirectory (char *dir) strlcpy (fs_gamedir, dir, sizeof (fs_gamedir)); -#ifndef AKVERSION - // add the directory to the search path - search = Mem_Alloc(pak_mempool, sizeof(searchpath_t)); - strlcpy (search->filename, dir, sizeof (search->filename)); - search->next = fs_searchpaths; - fs_searchpaths = search; -#endif - list = listdirectory(dir); // add any PAK package in the directory @@ -837,14 +898,12 @@ void FS_AddGameDirectory (char *dir) } freedirectory(list); -// Unpacked files have the priority over packed files if AKVERSION is defined -#ifdef AKVERSION - // add the directory to the search path + // Add the directory to the search path + // (unpacked files have the priority over packed files) search = Mem_Alloc(pak_mempool, sizeof(searchpath_t)); strlcpy (search->filename, dir, sizeof (search->filename)); search->next = fs_searchpaths; fs_searchpaths = search; -#endif } @@ -888,8 +947,8 @@ void FS_Init (void) int i; searchpath_t *search; - fs_mempool = Mem_AllocPool("file management"); - pak_mempool = Mem_AllocPool("paks"); + fs_mempool = Mem_AllocPool("file management", 0, NULL); + pak_mempool = Mem_AllocPool("paks", 0, NULL); Cvar_RegisterVariable (&scr_screenshot_name); @@ -904,6 +963,7 @@ void FS_Init (void) // -basedir // Overrides the system supplied base directory (under GAMENAME) +// COMMANDLINEOPTION: Filesystem: -basedir chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1) i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) { @@ -915,6 +975,7 @@ void FS_Init (void) // -path [] ... // Fully specifies the exact search path, overriding the generated one +// COMMANDLINEOPTION: Filesystem: -path specifies the full search path manually, overriding the generated one, example: -path c:\quake\id1 c:\quake\pak0.pak c:\quake\pak1.pak (not recommended) i = COM_CheckParm ("-path"); if (i) { @@ -974,6 +1035,10 @@ void FS_Init (void) Cvar_SetQuick (&scr_screenshot_name, com_modname); } } + + // If "-condebug" is in the command line, remove the previous log file + if (COM_CheckParm ("-condebug") != 0) + unlink (va("%s/qconsole.log", fs_gamedir)); } @@ -991,12 +1056,28 @@ static qfile_t* FS_SysOpen (const char* filepath, const char* mode) file = Mem_Alloc (fs_mempool, sizeof (*file)); memset (file, 0, sizeof (*file)); +#ifdef FS_USESYSCALLS + if (strchr(mode, 'r')) + file->stream = open (filepath, O_RDONLY | O_BINARY); + else if (strchr(mode, 'w')) + file->stream = open (filepath, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666); + else if (strchr(mode, 'a')) + file->stream = open (filepath, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0666); + else + file->stream = -1; + if (file->stream < 0) + { + Mem_Free (file); + return NULL; + } +#else file->stream = fopen (filepath, mode); if (!file->stream) { Mem_Free (file); return NULL; } +#endif return file; } @@ -1022,14 +1103,23 @@ qfile_t *FS_OpenRead (const char *path, int offs, int len) if (offs < 0 || len < 0) { // We set fs_filesize here for normal files +#ifdef FS_USESYSCALLS + fs_filesize = lseek (file->stream, 0, SEEK_END); + lseek (file->stream, 0, SEEK_SET); +#else fseek (file->stream, 0, SEEK_END); fs_filesize = ftell (file->stream); fseek (file->stream, 0, SEEK_SET); +#endif } // Packed file else { +#ifdef FS_USESYSCALLS + lseek (file->stream, offs, SEEK_SET); +#else fseek (file->stream, offs, SEEK_SET); +#endif file->flags |= FS_FLAG_PACKED; file->length = len; @@ -1040,6 +1130,39 @@ qfile_t *FS_OpenRead (const char *path, int offs, int len) return file; } +/* +==================== +FS_CheckNastyPath + +Return true if the path should be rejected due to one of the following: +1: path elements that are non-portable +2: path elements that would allow access to files outside the game directory, + or are just not a good idea for a mod to be using. +==================== +*/ +int FS_CheckNastyPath (const char *path) +{ + // Windows: don't allow \ in filenames (windows-only), period. + // (on Windows \ is a directory separator, but / is also supported) + if (strstr(path, "\\")) + return 1; // non-portable + // Mac: don't allow Mac-only filenames - : is a directory separator + // instead of /, but we rely on / working already, so there's no reason to + // support a Mac-only path + // Amiga and Windows: : tries to go to root of drive + if (strstr(path, ":")) + return 1; // non-portable attempt to go to root of drive + // Amiga: // is parent directory + if (strstr(path, "//")) + return 1; // non-portable attempt to go to parent directory + // all: don't allow going to current directory (./) or parent directory (../ or /../) + if (strstr(path, "./")) + return 2; // attempt to go to parent directory + // after all these checks we're pretty sure it's a / separated filename + // and won't do much if any harm + return false; +} + /* ==================== @@ -1055,7 +1178,6 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) { searchpath_t *search; pack_t *pak; - int (*strcmp_funct) (const char* str1, const char* str2); // search through the path, one element at a time for (search = fs_searchpaths;search;search = search->next) @@ -1063,26 +1185,27 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) // is the element a pak file? if (search->pack) { - size_t left, right, middle; + int (*strcmp_funct) (const char* str1, const char* str2); + int left, right, middle; pak = search->pack; strcmp_funct = pak->ignorecase ? strcasecmp : strcmp; // Look for the file (binary search) left = 0; - right = pak->numfiles; - while (left != right) + right = pak->numfiles - 1; + while (left <= right) { int diff; - middle = (left + right - 1) / 2; + middle = (left + right) / 2; diff = strcmp_funct (pak->files[middle].name, name); // Found it if (!diff) { if (!quiet) - Sys_Printf("FS_FindFile: %s in %s\n", + Con_DPrintf("FS_FindFile: %s in %s\n", pak->files[middle].name, pak->filename); if (index != NULL) @@ -1092,7 +1215,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) // If we're too far in the list if (diff > 0) - right = middle; + right = middle - 1; else left = middle + 1; } @@ -1104,7 +1227,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) if (FS_SysFileExists (netpath)) { if (!quiet) - Sys_Printf("FS_FindFile: %s\n", netpath); + Con_DPrintf("FS_FindFile: %s\n", netpath); if (index != NULL) *index = -1; @@ -1114,7 +1237,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) } if (!quiet) - Sys_Printf("FS_FindFile: can't find %s\n", name); + Con_DPrintf("FS_FindFile: can't find %s\n", name); if (index != NULL) *index = -1; @@ -1231,6 +1354,12 @@ Open a file. The syntax is the same as fopen */ qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet) { + if (FS_CheckNastyPath(filepath)) + { + Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false"); + return NULL; + } + // If the file is opened in "write" or "append" mode if (strchr (mode, 'w') || strchr (mode, 'a')) { @@ -1259,7 +1388,11 @@ Close a file */ int FS_Close (qfile_t* file) { +#ifdef FS_USESYSCALLS + if (close (file->stream)) +#else if (fclose (file->stream)) +#endif return EOF; if (file->z) @@ -1282,7 +1415,11 @@ Write "datasize" bytes into a file */ size_t FS_Write (qfile_t* file, const void* data, size_t datasize) { +#ifdef FS_USESYSCALLS + return write (file->stream, data, datasize); +#else return fwrite (data, 1, datasize, file->stream); +#endif } @@ -1300,7 +1437,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) // Quick path for unpacked files if (! (file->flags & FS_FLAG_PACKED)) +#ifdef FS_USESYSCALLS + return read (file->stream, buffer, buffersize); +#else return fread (buffer, 1, buffersize, file->stream); +#endif // If the file isn't compressed if (! (file->flags & FS_FLAG_DEFLATED)) @@ -1310,7 +1451,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) if (buffersize > count) buffersize = count; +#ifdef FS_USESYSCALLS + nb = read (file->stream, buffer, buffersize); +#else nb = fread (buffer, 1, buffersize, file->stream); +#endif file->position += nb; return nb; @@ -1348,7 +1493,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) remain = file->length - ztk->in_position; count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain; +#ifdef FS_USESYSCALLS + read (file->stream, ztk->input, count); +#else fread (ztk->input, 1, count, file->stream); +#endif // Update indexes and counters ztk->in_ind = 0; @@ -1423,7 +1572,11 @@ Flush the file output stream */ int FS_Flush (qfile_t* file) { +#ifdef FS_USESYSCALLS + return 0; +#else return fflush (file->stream); +#endif } @@ -1452,7 +1605,7 @@ int FS_Printf(qfile_t* file, const char* format, ...) va_list args; va_start (args, format); - result = vfprintf (file->stream, format, args); + result = FS_VPrintf(file, format, args); va_end (args); return result; @@ -1468,7 +1621,26 @@ Print a string into a file */ int FS_VPrintf(qfile_t* file, const char* format, va_list ap) { +#ifdef FS_USESYSCALLS +{ + int len; + char tempstring[1024]; + len = vsnprintf (tempstring, sizeof(tempstring), format, ap); + if (len >= sizeof(tempstring)) + { + int result; + char *temp = Mem_Alloc(tempmempool, len + 1); + len = vsnprintf (temp, len + 1, format, ap); + result = write (file->stream, temp, len); + Mem_Free(temp); + return result; + } + else + return write (file->stream, tempstring, len); +} +#else return vfprintf (file->stream, format, ap); +#endif } @@ -1501,7 +1673,15 @@ int FS_Seek (qfile_t* file, long offset, int whence) { // Quick path for unpacked files if (! (file->flags & FS_FLAG_PACKED)) +#ifdef FS_USESYSCALLS + { + if (lseek (file->stream, offset, whence) == -1) + return -1; + return 0; + } +#else return fseek (file->stream, offset, whence); +#endif // Seeking in compressed files is more a hack than anything else, // but we need to support it, so here it is. @@ -1548,7 +1728,11 @@ int FS_Seek (qfile_t* file, long offset, int whence) ztk->out_max = 0; ztk->out_position = 0; file->position = 0; +#ifdef FS_USESYSCALLS + lseek (file->stream, file->offset, SEEK_SET); +#else fseek (file->stream, file->offset, SEEK_SET); +#endif // Reset the Zlib stream ztk->zstream.next_in = ztk->input; @@ -1592,8 +1776,13 @@ int FS_Seek (qfile_t* file, long offset, int whence) if (offset < 0 || offset > (long) file->length) return -1; +#ifdef FS_USESYSCALLS + if (lseek (file->stream, file->offset + offset, SEEK_SET) == -1) + return -1; +#else if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1) return -1; +#endif file->position = offset; return 0; } @@ -1611,7 +1800,11 @@ long FS_Tell (qfile_t* file) if (file->flags & FS_FLAG_PACKED) return file->position; +#ifdef FS_USESYSCALLS + return lseek (file->stream, 0, SEEK_CUR); +#else return ftell (file->stream); +#endif } @@ -1627,8 +1820,10 @@ char* FS_Gets (qfile_t* file, char* buffer, int buffersize) size_t ind; // Quick path for unpacked files +#ifndef FS_USESYSCALLS if (! (file->flags & FS_FLAG_PACKED)) return fgets (buffer, buffersize, file->stream); +#endif for (ind = 0; ind < (size_t) buffersize - 1; ind++) { @@ -1706,6 +1901,7 @@ FS_Eof Extract a line from a file ==================== */ +// FIXME: remove this function? int FS_Eof (qfile_t* file) { if (file->flags & FS_FLAG_PACKED) @@ -1716,7 +1912,12 @@ int FS_Eof (qfile_t* file) return (file->position == file->length); } +#ifdef FS_USESYSCALLS + Sys_Error("FS_Eof: not implemented using syscalls\n"); + return false; +#else return feof (file->stream); +#endif } @@ -1956,7 +2157,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) if (liststart == NULL) liststart = listcurrent; if (!quiet) - Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp); + Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp); } } // strip off one path element at a time until empty @@ -1995,7 +2196,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) if (liststart == NULL) liststart = listcurrent; if (!quiet) - Sys_Printf("SearchDirFile: %s\n", temp); + Con_DPrintf("SearchDirFile: %s\n", temp); } } }