From b76372685dff9f3fbe896194c4a469c88b562ba5 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 13:51:35 +0000 Subject: [PATCH] gutils: Split out building of g_get_home_dir() path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we can have problems calling g_get_home_dir() from within a g_build_*_dir() function elsewhere in gutils.c: • There will be a deadlock due to trying to recursively acquire the g_utils_global lock. • A stale g_home_dir value may be used if a test harness has called g_set_user_dirs() in the interim. Fix that by splitting the code to find/construct the home path out of g_get_home_dir() into g_build_home_dir(), the same way it’s split for the other g_get_*() functions. Call g_build_home_dir() from any call site where the g_utils_global lock is held. Signed-off-by: Philip Withnall --- glib/gutils.c | 178 ++++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 1e267602a..2fcccc33d 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -795,6 +795,76 @@ g_get_real_name (void) /* Protected by @g_utils_global_lock. */ static gchar *g_home_dir = NULL; /* (owned) (nullable before initialised) */ +static gchar * +g_build_home_dir (void) +{ + gchar *home_dir; + + /* We first check HOME and use it if it is set */ + home_dir = g_strdup (g_getenv ("HOME")); + +#ifdef G_OS_WIN32 + /* Only believe HOME if it is an absolute path and exists. + * + * We only do this check on Windows for a couple of reasons. + * Historically, we only did it there because we used to ignore $HOME + * on UNIX. There are concerns about enabling it now on UNIX because + * of things like autofs. In short, if the user has a bogus value in + * $HOME then they get what they pay for... + */ + if (home_dir != NULL) + { + if (!(g_path_is_absolute (home_dir) && + g_file_test (home_dir, G_FILE_TEST_IS_DIR))) + g_clear_pointer (&home_dir, g_free); + } + + /* In case HOME is Unix-style (it happens), convert it to + * Windows style. + */ + if (home_dir != NULL) + { + gchar *p; + while ((p = strchr (home_dir, '/')) != NULL) + *p = '\\'; + } + + if (home_dir == NULL) + { + /* USERPROFILE is probably the closest equivalent to $HOME? */ + if (g_getenv ("USERPROFILE") != NULL) + home_dir = g_strdup (g_getenv ("USERPROFILE")); + } + + if (home_dir == NULL) + home_dir = get_special_folder (CSIDL_PROFILE); + + if (home_dir == NULL) + home_dir = get_windows_directory_root (); +#endif /* G_OS_WIN32 */ + + if (home_dir == NULL) + { + /* If we didn't get it from any of those methods, we will have + * to read the user database entry. + */ + UserDatabaseEntry *entry = g_get_user_database_entry (); + home_dir = g_strdup (entry->home_dir); + } + + /* If we have been denied access to /etc/passwd (for example, by an + * overly-zealous LSM), make up a junk value. The return value at this + * point is explicitly documented as ‘undefined’. */ + if (home_dir == NULL) + { + g_warning ("Could not find home directory: $HOME is not set, and " + "user database could not be read."); + home_dir = g_strdup ("/"); + } + + return g_steal_pointer (&home_dir); +} + /** * g_get_home_dir: * @@ -829,85 +899,7 @@ g_get_home_dir (void) G_LOCK (g_utils_global); if (g_home_dir == NULL) - { - gchar *tmp; - - /* We first check HOME and use it if it is set */ - tmp = g_strdup (g_getenv ("HOME")); - -#ifdef G_OS_WIN32 - /* Only believe HOME if it is an absolute path and exists. - * - * We only do this check on Windows for a couple of reasons. - * Historically, we only did it there because we used to ignore $HOME - * on UNIX. There are concerns about enabling it now on UNIX because - * of things like autofs. In short, if the user has a bogus value in - * $HOME then they get what they pay for... - */ - if (tmp) - { - if (!(g_path_is_absolute (tmp) && - g_file_test (tmp, G_FILE_TEST_IS_DIR))) - { - g_free (tmp); - tmp = NULL; - } - } - - /* In case HOME is Unix-style (it happens), convert it to - * Windows style. - */ - if (tmp) - { - gchar *p; - while ((p = strchr (tmp, '/')) != NULL) - *p = '\\'; - } - - if (!tmp) - { - /* USERPROFILE is probably the closest equivalent to $HOME? */ - if (g_getenv ("USERPROFILE") != NULL) - tmp = g_strdup (g_getenv ("USERPROFILE")); - } - - if (!tmp) - tmp = get_special_folder (CSIDL_PROFILE); - - if (!tmp) - tmp = get_windows_directory_root (); -#endif /* G_OS_WIN32 */ - - if (!tmp) - { - /* If we didn't get it from any of those methods, we will have - * to read the user database entry. - */ - UserDatabaseEntry *entry; - - entry = g_get_user_database_entry (); - - /* Strictly speaking, we should copy this, but we know that - * neither will ever be freed, so don't bother... - */ - tmp = entry->home_dir; - } - - /* If we have been denied access to /etc/passwd (for example, by an - * overly-zealous LSM), make up a junk value. The return value at this - * point is explicitly documented as ‘undefined’. Memory management is as - * immediately above: strictly this should be copied, but we know not - * copying it is OK. */ - if (tmp == NULL) - { - g_warning ("Could not find home directory: $HOME is not set, and " - "user database could not be read."); - tmp = "/"; - } - - g_home_dir = g_steal_pointer (&tmp); - } - + g_home_dir = g_build_home_dir (); home_dir = g_home_dir; G_UNLOCK (g_utils_global); @@ -1299,12 +1291,14 @@ g_build_user_data_dir (void) #endif if (!data_dir || !data_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) data_dir = g_build_filename (home_dir, ".local", "share", NULL); else data_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".local", "share", NULL); + + g_free (home_dir); } return g_steal_pointer (&data_dir); @@ -1362,12 +1356,14 @@ g_build_user_config_dir (void) #endif if (!config_dir || !config_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) config_dir = g_build_filename (home_dir, ".config", NULL); else config_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".config", NULL); + + g_free (home_dir); } return g_steal_pointer (&config_dir); @@ -1425,12 +1421,14 @@ g_build_user_cache_dir (void) #endif if (!cache_dir || !cache_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) cache_dir = g_build_filename (home_dir, ".cache", NULL); else cache_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".cache", NULL); + + g_free (home_dir); } return g_steal_pointer (&cache_dir); @@ -1805,7 +1803,9 @@ load_user_special_dirs (void) if (is_relative) { - g_user_special_dirs[directory] = g_build_filename (g_get_home_dir (), d, NULL); + gchar *home_dir = g_build_home_dir (); + g_user_special_dirs[directory] = g_build_filename (home_dir, d, NULL); + g_free (home_dir); } else g_user_special_dirs[directory] = g_strdup (d); @@ -1913,7 +1913,11 @@ g_get_user_special_dir (GUserDirectory directory) /* Special-case desktop for historical compatibility */ if (g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] == NULL) - g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = g_build_filename (g_get_home_dir (), "Desktop", NULL); + { + gchar *home_dir = g_build_home_dir (); + g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = g_build_filename (home_dir, "Desktop", NULL); + g_free (home_dir); + } } user_special_dir = g_user_special_dirs[directory];