From 3d60770b84e3e6ae995f6b2e4dc7f3fed998bc72 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 19 Dec 2024 04:18:19 +0300 Subject: [PATCH] filesystem: add flag to open files in a RAM On Linux, it uses memfd_create syscall that can be found on Linux 3.17 and higher. By default memfds are executable, so we set MFD_NOEXEC_SEAL flag to prevent execution at creation time. --- filesystem/filesystem.c | 58 ++++++++++++++++++++++++++++++----------- filesystem/wscript | 7 +++++ 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 3719baf6..9caad56e 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -16,6 +16,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#define _GNU_SOURCE 1 + #include "build.h" #include #include @@ -32,6 +34,9 @@ GNU General Public License for more details. #endif #include #include +#if HAVE_MEMFD_CREATE +#include +#endif #include "port.h" #include "defaults.h" #include "const.h" @@ -1666,9 +1671,10 @@ Internal function used to create a file_t and open the relevant non-packed file */ file_t *FS_SysOpen( const char *filepath, const char *mode ) { - file_t *file; - int mod, opt; - uint ind; + file_t *file; + int mod, opt, fd = -1; + qboolean memfile = false; + uint ind; // Parse the mode string switch( mode[0] ) @@ -1708,30 +1714,52 @@ file_t *FS_SysOpen( const char *filepath, const char *mode ) } } - file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); - file->filetime = FS_SysFileTime( filepath ); - file->ungetc = EOF; + // the 'm' flag let's user to create temporary file in memory + // through so-called "anonymous files" + if( Q_strchr( mode, 'm' )) + { +#if HAVE_MEMFD_CREATE +#ifndef MFD_NOEXEC_SEAL +#define MFD_NOEXEC_SEAL 8U +#endif + fd = memfd_create( filepath, MFD_CLOEXEC | MFD_NOEXEC_SEAL ); + // through fcntl() and MFD_ALLOW_SEALING we could enforce + // read-write flags but we don't really care about them yet + if( fd < 0 ) + Con_Printf( S_ERROR "%s: can't create anonymous file %s: %s", __func__, filepath, strerror( errno )); + else memfile = true; +#endif + // if it's unsupported, we can open it on disk + } + + if( fd < 0 ) + { #if XASH_WIN32 - file->handle = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 ); + fd = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 ); #else - file->handle = open( filepath, mod|opt, 0666 ); + fd = open( filepath, mod|opt, 0666 ); #endif + } -#if !XASH_WIN32 - if( file->handle < 0 ) - FS_BackupFileName( file, filepath, mod|opt ); -#endif - - if( file->handle < 0 ) + if( fd < 0 ) { if( errno != ENOENT ) Con_Printf( S_ERROR "%s: can't open file %s: %s\n", __func__, filepath, strerror( errno )); - Mem_Free( file ); return NULL; } + file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); + file->filetime = memfile ? 0 : FS_SysFileTime( filepath ); + file->ungetc = EOF; + file->handle = fd; + +#if !XASH_WIN32 + if( !memfile ) + FS_BackupFileName( file, filepath, mod|opt ); +#endif + file->searchpath = NULL; file->real_length = lseek( file->handle, 0, SEEK_END ); diff --git a/filesystem/wscript b/filesystem/wscript index 1403e922..3013a538 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -1,5 +1,9 @@ #!/usr/bin/env python +MEMFD_CREATE_TEST = '''#define _GNU_SOURCE +#include +int main(int argc, char **argv) { return memfd_create(argv[0], 0); }''' + def options(opt): pass @@ -11,6 +15,9 @@ def configure(conf): elif conf.env.cxxshlib_PATTERN.startswith('lib'): # remove lib prefix for other systems than Android conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] + if conf.check_cc(fragment=MEMFD_CREATE_TEST, msg='Checking for memfd_create', mandatory=False): + conf.define('HAVE_MEMFD_CREATE', 1) + def build(bld): bld(name = 'filesystem_includes', export_includes = '.')