gutils: stop g_get_home_dir() from reading passwd

In the case that the "HOME" environment variable is set (as it is under
normal circumstances), we don't really need to be opening /etc/passwd.

For historical reasons (ie: how we used to ignore $HOME) and due to the
grouping of many unrelated things together (reading username, hostname,
home directory, tmpdir, etc.) into one function we were still opening
/etc/passwd in g_get_home_dir(), even if $HOME was set.

Since earlier commits removed code from it, all that remains in
g_get_any_init_do() is the logic for dealing with $HOME and reading the
password database.

We now split the logic to deal with $HOME into g_get_home_dir().  With
only the password database functionality remaining, g_get_any_init_do()
is renamed to g_get_user_database_entry() and modified not to set global
variables but rather return a struct.  If g_get_home_dir() cannot find
$HOME, it falls back to calling g_get_user_database_entry() and using
the home directory from there.

Use of the 'g_utils_global' lock is further reduced by using
g_once_init_enter() to protect the critical sections in each of
g_get_user_database_entry() and g_get_home_dir().

Finally, the g_get_user_name() and g_get_real_name() functions are
modified to use the new regime.

https://bugzilla.gnome.org/show_bug.cgi?id=693204
This commit is contained in:
Ryan Lortie 2013-02-04 14:41:25 +01:00
parent 167c73faf4
commit cfafad5aef

View File

@ -577,9 +577,12 @@ g_find_program_in_path (const gchar *program)
G_LOCK_DEFINE_STATIC (g_utils_global); G_LOCK_DEFINE_STATIC (g_utils_global);
static gchar *g_user_name = NULL; typedef struct
static gchar *g_real_name = NULL; {
static gchar *g_home_dir = NULL; gchar *user_name;
gchar *real_name;
gchar *home_dir;
} UserDatabaseEntry;
static gchar *g_user_data_dir = NULL; static gchar *g_user_data_dir = NULL;
static gchar **g_system_data_dirs = NULL; static gchar **g_system_data_dirs = NULL;
@ -643,54 +646,14 @@ get_windows_directory_root (void)
#endif #endif
/* HOLDS: g_utils_global_lock */ /* HOLDS: g_utils_global_lock */
static void static UserDatabaseEntry *
g_get_any_init_do (void) g_get_user_database_entry (void)
{ {
/* We first check HOME and use it if it is set */ static UserDatabaseEntry *entry;
g_home_dir = g_strdup (g_getenv ("HOME"));
#ifdef G_OS_WIN32 if (g_once_init_enter (&entry))
/* 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 (g_home_dir)
{ {
if (!(g_path_is_absolute (g_home_dir) && static UserDatabaseEntry e;
g_file_test (g_home_dir, G_FILE_TEST_IS_DIR)))
{
g_free (g_home_dir);
g_home_dir = NULL;
}
}
/* In case HOME is Unix-style (it happens), convert it to
* Windows style.
*/
if (g_home_dir)
{
gchar *p;
while ((p = strchr (g_home_dir, '/')) != NULL)
*p = '\\';
}
if (!g_home_dir)
{
/* USERPROFILE is probably the closest equivalent to $HOME? */
if (g_getenv ("USERPROFILE") != NULL)
g_home_dir = g_strdup (g_getenv ("USERPROFILE"));
}
if (!g_home_dir)
g_home_dir = get_special_folder (CSIDL_PROFILE);
if (!g_home_dir)
g_home_dir = get_windows_directory_root ();
#endif /* G_OS_WIN32 */
#ifdef HAVE_PWD_H #ifdef HAVE_PWD_H
{ {
@ -786,7 +749,7 @@ g_get_any_init_do (void)
} }
if (pw) if (pw)
{ {
g_user_name = g_strdup (pw->pw_name); e.user_name = g_strdup (pw->pw_name);
if (pw->pw_gecos && *pw->pw_gecos != '\0') if (pw->pw_gecos && *pw->pw_gecos != '\0')
{ {
@ -797,13 +760,13 @@ g_get_any_init_do (void)
gecos_fields = g_strsplit (pw->pw_gecos, ",", 0); gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
name_parts = g_strsplit (gecos_fields[0], "&", 0); name_parts = g_strsplit (gecos_fields[0], "&", 0);
pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]); pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
g_real_name = g_strjoinv (pw->pw_name, name_parts); e.real_name = g_strjoinv (pw->pw_name, name_parts);
g_strfreev (gecos_fields); g_strfreev (gecos_fields);
g_strfreev (name_parts); g_strfreev (name_parts);
} }
if (!g_home_dir) if (!e.home_dir)
g_home_dir = g_strdup (pw->pw_dir); e.home_dir = g_strdup (pw->pw_dir);
} }
g_free (buffer); g_free (buffer);
} }
@ -817,8 +780,8 @@ g_get_any_init_do (void)
if (GetUserNameW (buffer, (LPDWORD) &len)) if (GetUserNameW (buffer, (LPDWORD) &len))
{ {
g_user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL); e.user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
g_real_name = g_strdup (g_user_name); e.real_name = g_strdup (e.user_name);
} }
} }
#endif /* G_OS_WIN32 */ #endif /* G_OS_WIN32 */
@ -827,30 +790,19 @@ g_get_any_init_do (void)
#ifdef __EMX__ #ifdef __EMX__
/* change '\\' in %HOME% to '/' */ /* change '\\' in %HOME% to '/' */
g_strdelimit (g_home_dir, "\\",'/'); g_strdelimit (e.home_dir, "\\",'/');
#endif #endif
if (!g_user_name) if (!e.user_name)
g_user_name = g_strdup ("somebody"); e.user_name = g_strdup ("somebody");
if (!g_real_name) if (!e.real_name)
g_real_name = g_strdup ("Unknown"); e.real_name = g_strdup ("Unknown");
}
static inline void g_once_init_leave (&entry, &e);
g_get_any_init (void) }
{
if (!g_user_name)
g_get_any_init_do ();
}
static inline void return entry;
g_get_any_init_locked (void)
{
G_LOCK (g_utils_global);
g_get_any_init ();
G_UNLOCK (g_utils_global);
} }
/** /**
* g_get_user_name: * g_get_user_name:
* *
@ -864,8 +816,11 @@ g_get_any_init_locked (void)
const gchar * const gchar *
g_get_user_name (void) g_get_user_name (void)
{ {
g_get_any_init_locked (); UserDatabaseEntry *entry;
return g_user_name;
entry = g_get_user_database_entry ();
return entry->user_name;
} }
/** /**
@ -882,8 +837,11 @@ g_get_user_name (void)
const gchar * const gchar *
g_get_real_name (void) g_get_real_name (void)
{ {
g_get_any_init_locked (); UserDatabaseEntry *entry;
return g_real_name;
entry = g_get_user_database_entry ();
return entry->real_name;
} }
/** /**
@ -920,8 +878,77 @@ g_get_real_name (void)
const gchar * const gchar *
g_get_home_dir (void) g_get_home_dir (void)
{ {
g_get_any_init_locked (); static gchar *home_dir;
return g_home_dir;
if (g_once_init_enter (&home_dir))
{
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;
}
g_once_init_leave (&home_dir, tmp);
}
return home_dir;
} }
/** /**