diff --git a/source/lib/res/file/path.cpp b/source/lib/res/file/path.cpp index ed72903da3..b381a9c003 100644 --- a/source/lib/res/file/path.cpp +++ b/source/lib/res/file/path.cpp @@ -138,6 +138,16 @@ LibError file_make_full_portable_path(const char* n_full_path, char* path) } +// security check: only allow attempting to chdir once, so that malicious +// code cannot circumvent the VFS checks that disallow access to anything +// above the current directory (set here). +// this routine is called early at startup, so any subsequent attempts +// are likely bogus. +// we provide for resetting this from the self-test to allow clean +// re-init of the individual tests. +static bool root_dir_established; + + // establish the root directory from , which is treated as // relative to the executable's directory (determined via argv[0]). // all relative file paths passed to this module will be based from @@ -153,15 +163,9 @@ LibError file_make_full_portable_path(const char* n_full_path, char* path) // can only be called once, by design (see below). rel_path is trusted. LibError file_set_root_dir(const char* argv0, const char* rel_path) { - // security check: only allow attempting to chdir once, so that malicious - // code cannot circumvent the VFS checks that disallow access to anything - // above the current directory (set here). - // this routine is called early at startup, so any subsequent attempts - // are likely bogus. - static bool already_attempted; - if(already_attempted) + if(root_dir_established) WARN_RETURN(ERR::ROOT_DIR_ALREADY_SET); - already_attempted = true; + root_dir_established = true; // get full path to executable char n_path[PATH_MAX]; @@ -193,6 +197,16 @@ LibError file_set_root_dir(const char* argv0, const char* rel_path) } +void path_reset_root_dir() +{ + // see comment at root_dir_established. + debug_assert(root_dir_established); + n_root_dir[0] = '\0'; + n_root_dir_len = 0; + root_dir_established = false; +} + + //----------------------------------------------------------------------------- // storage for path strings //----------------------------------------------------------------------------- diff --git a/source/lib/res/file/path.h b/source/lib/res/file/path.h index beed66b3ec..dc469d315f 100644 --- a/source/lib/res/file/path.h +++ b/source/lib/res/file/path.h @@ -58,6 +58,16 @@ extern bool path_is_atom_fn(const char* fn); extern const char* file_get_random_name(); + +/** + * reset root directory that was previously established via file_set_root_dir. + * + * this function avoids the security complaint that would be raised if + * file_set_root_dir is called twice; it is provided for the + * legitimate application of a self-test setUp()/tearDown(). + **/ +extern void path_reset_root_dir(); + // note: other functions are declared directly in the public file.h header. diff --git a/source/lib/res/file/tests/test_archive_builder.h b/source/lib/res/file/tests/test_archive_builder.h index 09a688c4ff..1b5ea99447 100644 --- a/source/lib/res/file/tests/test_archive_builder.h +++ b/source/lib/res/file/tests/test_archive_builder.h @@ -97,6 +97,7 @@ public: vfs_shutdown(); dir_delete("archivetest"); file_delete(archive_fn); + path_reset_root_dir(); } void test_create_archive_with_random_files() @@ -126,4 +127,12 @@ public: } TS_ASSERT_OK(archive_close(ha)); } + + void test_multiple_init_shutdown() + { + vfs_init(); + vfs_shutdown(); + vfs_init(); + vfs_shutdown(); + } }; diff --git a/source/lib/res/file/vfs.cpp b/source/lib/res/file/vfs.cpp index b14659732f..f3afe7d4bd 100644 --- a/source/lib/res/file/vfs.cpp +++ b/source/lib/res/file/vfs.cpp @@ -738,12 +738,13 @@ void vfs_display() } -// allow init more complicated than merely calling mount_init by -// splitting this into a separate function. -static void vfs_init_once(void) +static enum VfsInitState { - mount_init(); + VFS_BEFORE_INIT, + VFS_INITIALIZED, + VFS_SHUTDOWN } +vfs_init_state; // make the VFS tree ready for use. must be called before all other // functions below, barring explicit mentions to the contrary. @@ -754,14 +755,21 @@ static void vfs_init_once(void) // is necessary anyway and this way is simpler/easier to maintain. void vfs_init() { + debug_assert(vfs_init_state == VFS_BEFORE_INIT || vfs_init_state == VFS_SHUTDOWN); + stats_vfs_init_start(); - static pthread_once_t once = PTHREAD_ONCE_INIT; - WARN_ERR(pthread_once(&once, vfs_init_once)); + mount_init(); stats_vfs_init_finish(); + + vfs_init_state = VFS_INITIALIZED; } void vfs_shutdown() { + debug_assert(vfs_init_state == VFS_INITIALIZED); + trace_shutdown(); mount_shutdown(); + + vfs_init_state = VFS_SHUTDOWN; } diff --git a/source/lib/res/file/vfs_tree.cpp b/source/lib/res/file/vfs_tree.cpp index da5c5a0dbe..eee499d0cf 100644 --- a/source/lib/res/file/vfs_tree.cpp +++ b/source/lib/res/file/vfs_tree.cpp @@ -540,6 +540,8 @@ void tree_shutdown() // freeing memory they hold. tree_root->clearR(); + tree_root_shutdown(); + // free memory underlying the nodes themselves. node_shutdown(); }