]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
slight optimization to CL_RelinkNetworkEntities to scan entities faster
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 4e5ff55888a447322385a602a71dfbfdd13c841b..233800ef19157efda9f411569d52a38eae91174b 100644 (file)
--- a/fs.c
+++ b/fs.c
 // 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
+
 
 /*
 
@@ -339,20 +346,25 @@ Try to load the Zlib DLL
 */
 qboolean PK3_OpenLibrary (void)
 {
-       const char* dllname;
+       const char* dllnames [] =
+       {
+#ifdef WIN32
+               "zlib.dll",
+#elif defined(MACOSX)
+               "libz.dylib",
+#else
+               "libz.so.1",
+               "libz.so",
+#endif
+               NULL
+       };
 
        // Already loaded?
        if (zlib_dll)
                return true;
 
-#ifdef WIN32
-       dllname = "zlib.dll";
-#else
-       dllname = "libz.so";
-#endif
-
        // Load the DLL
-       if (! Sys_LoadLibrary (dllname, &zlib_dll, zlibfuncs))
+       if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
        {
                Con_Printf ("Compressed files support disabled\n");
                return false;
@@ -398,10 +410,10 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_end
        buffer = Mem_Alloc (tempmempool, maxsize);
 #ifdef FS_USESYSCALLS
        lseek (packhandle, filesize - maxsize, SEEK_SET);
-       if (read (packhandle, buffer, maxsize) != (unsigned long) maxsize)
+       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);
@@ -536,7 +548,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;
 }
 
@@ -560,7 +574,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
        int real_nb_files;
 
 #ifdef FS_USESYSCALLS
-       packhandle = open (packfile, O_RDONLY);
+       packhandle = open (packfile, O_RDONLY | O_BINARY);
        if (packhandle < 0)
                return NULL;
 #else
@@ -595,7 +609,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
        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);
@@ -658,28 +672,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;
        }
@@ -770,7 +785,7 @@ pack_t *FS_LoadPackPAK (const char *packfile)
        dpackfile_t *info;      // temporary alloc, allowing huge pack directories
 
 #ifdef FS_USESYSCALLS
-       packhandle = open (packfile, O_RDONLY);
+       packhandle = open (packfile, O_RDONLY | O_BINARY);
        if (packhandle < 0)
                return NULL;
        read (packhandle, (void *)&header, sizeof(header));
@@ -836,7 +851,7 @@ Sets fs_gamedir, adds the directory to the head of the path,
 then loads and adds pak1.pak pak2.pak ...
 ================
 */
-void FS_AddGameDirectory (char *dir)
+void FS_AddGameDirectory (const char *dir)
 {
        stringlist_t *list, *current;
        searchpath_t *search;
@@ -895,6 +910,27 @@ void FS_AddGameDirectory (char *dir)
 }
 
 
+/*
+================
+FS_AddGameHierarchy
+================
+*/
+void FS_AddGameHierarchy (const char *dir)
+{
+       const char *homedir;
+
+       strlcpy (com_modname, dir, sizeof (com_modname));
+
+       // Add the common game directory
+       FS_AddGameDirectory (va("%s/%s", fs_basedir, dir));
+
+       // Add the personal game directory
+       homedir = getenv ("HOME");
+       if (homedir != NULL && homedir[0] != '\0')
+               FS_AddGameDirectory (va("%s/.darkplaces/%s", homedir, dir));
+}
+
+
 /*
 ============
 FS_FileExtension
@@ -995,16 +1031,14 @@ void FS_Init (void)
        }
 
        // start up with GAMENAME by default (id1)
-       strlcpy (com_modname, GAMENAME, sizeof (com_modname));
-       FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
+       FS_AddGameHierarchy (GAMENAME);
        Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
 
        // add the game-specific path, if any
        if (gamedirname[0])
        {
                fs_modified = true;
-               strlcpy (com_modname, gamedirname, sizeof (com_modname));
-               FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
+               FS_AddGameHierarchy (gamedirname);
        }
 
        // -game <gamedir>
@@ -1018,13 +1052,26 @@ void FS_Init (void)
                {
                        i++;
                        fs_modified = true;
-                       strlcpy (com_modname, com_argv[i], sizeof (com_modname));
-                       FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i]));
+                       FS_AddGameHierarchy (com_argv[i]);
                        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));
 }
 
+/*
+================
+FS_Shutdown
+================
+*/
+void FS_Shutdown (void)
+{
+       Mem_FreePool (&pak_mempool);
+       Mem_FreePool (&fs_mempool);
+}
 
 /*
 ====================
@@ -1042,11 +1089,11 @@ static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
 
 #ifdef FS_USESYSCALLS
        if (strchr(mode, 'r'))
-               file->stream = open (filepath, O_RDONLY);
+               file->stream = open (filepath, O_RDONLY | O_BINARY);
        else if (strchr(mode, 'w'))
-               file->stream = open (filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+               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_CREAT | O_APPEND, 0666);
+               file->stream = open (filepath, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0666);
        else
                file->stream = -1;
        if (file->stream < 0)
@@ -1114,6 +1161,42 @@ 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 outside the game directory
+       // Windows and UNIXes: don't allow absolute paths
+       if (path[0] == '/')
+               return 2; // attempt to go outside the game directory
+       // after all these checks we're pretty sure it's a / separated filename
+       // and won't do much if any harm
+       return false;
+}
+
 
 /*
 ====================
@@ -1129,7 +1212,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)
@@ -1137,26 +1219,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)
@@ -1166,7 +1249,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;
                        }
@@ -1178,7 +1261,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;
@@ -1188,7 +1271,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;
@@ -1305,6 +1388,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'))
        {
@@ -1619,7 +1708,11 @@ int FS_Seek (qfile_t* file, long offset, int whence)
        // Quick path for unpacked files
        if (! (file->flags & FS_FLAG_PACKED))
 #ifdef FS_USESYSCALLS
-               return lseek (file->stream, offset, whence);
+       {
+               if (lseek (file->stream, offset, whence) == -1)
+                       return -1;
+               return 0;
+       }
 #else
                return fseek (file->stream, offset, whence);
 #endif
@@ -2098,7 +2191,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
@@ -2137,7 +2230,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);
                                                }
                                        }
                                }