https://bugzilla.redhat.com/show_bug.cgi?id=964358 A future patch needs to look up pw_gid; but it is wasteful to crawl through getpwuid_r twice for two separate pieces of information, and annoying to copy that much boilerplate code for doing the crawl. The current internal-only virGetUserEnt is also a rather awkward interface; it's easier to just design it to let callers request multiple pieces of data as needed from one traversal. And while at it, I noticed that virGetXDGDirectory could deref NULL if the getpwuid_r lookup fails. * src/util/virutil.c (virGetUserEnt): Alter signature. (virGetUserDirectory, virGetXDGDirectory, virGetUserName): Adjust callers. Signed-off-by: Eric Blake <eblake@xxxxxxxxxx> (cherry picked from commit c1983ba4e3902308054e961fcae75cece73ef4ba) Conflicts: src/util/virutil.c - oom reporting/strdup changes not backported --- src/util/util.c | 60 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/util/util.c b/src/util/util.c index 8315515..dd4e4ea 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -2290,21 +2290,23 @@ check_and_return: } #ifdef HAVE_GETPWUID_R -enum { - VIR_USER_ENT_DIRECTORY, - VIR_USER_ENT_NAME, -}; - -static char *virGetUserEnt(uid_t uid, - int field) +/* Look up fields from the user database for the given user. On + * error, set errno, report the error, and return -1. */ +static int +virGetUserEnt(uid_t uid, char **name, gid_t *group, char **dir) { char *strbuf; - char *ret; struct passwd pwbuf; struct passwd *pw = NULL; long val = sysconf(_SC_GETPW_R_SIZE_MAX); size_t strbuflen = val; int rc; + int ret = -1; + + if (name) + *name = NULL; + if (dir) + *dir = NULL; /* sysconf is a hint; if it fails, fall back to a reasonable size */ if (val < 0) @@ -2312,7 +2314,7 @@ static char *virGetUserEnt(uid_t uid, if (VIR_ALLOC_N(strbuf, strbuflen) < 0) { virReportOOMError(); - return NULL; + return -1; } /* @@ -2325,28 +2327,33 @@ static char *virGetUserEnt(uid_t uid, while ((rc = getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw)) == ERANGE) { if (VIR_RESIZE_N(strbuf, strbuflen, strbuflen, strbuflen) < 0) { virReportOOMError(); - VIR_FREE(strbuf); - return NULL; + goto cleanup; } } if (rc != 0 || pw == NULL) { virReportSystemError(rc, _("Failed to find user record for uid '%u'"), (unsigned int) uid); - VIR_FREE(strbuf); - return NULL; + goto cleanup; } - if (field == VIR_USER_ENT_DIRECTORY) - ret = strdup(pw->pw_dir); - else - ret = strdup(pw->pw_name); + if (name && !(*name = strdup(pw->pw_name))) + goto no_memory; + if (group) + *group = pw->pw_gid; + if (dir && !(*dir = strdup(pw->pw_dir))) { + if (name) + VIR_FREE(*name); + goto no_memory; + } + ret = 0; +cleanup: VIR_FREE(strbuf); - if (!ret) - virReportOOMError(); - return ret; +no_memory: + virReportOOMError(); + goto cleanup; } static char *virGetGroupEnt(gid_t gid) @@ -2401,20 +2408,23 @@ static char *virGetGroupEnt(gid_t gid) char *virGetUserDirectory(void) { - return virGetUserEnt(geteuid(), VIR_USER_ENT_DIRECTORY); + char *ret; + virGetUserEnt(geteuid(), NULL, NULL, &ret); + return ret; } static char *virGetXDGDirectory(const char *xdgenvname, const char *xdgdefdir) { const char *path = getenv(xdgenvname); char *ret = NULL; - char *home = virGetUserEnt(geteuid(), VIR_USER_ENT_DIRECTORY); + char *home = virGetUserDirectory(); if (path && path[0]) { if (virAsprintf(&ret, "%s/libvirt", path) < 0) goto no_memory; } else { - if (virAsprintf(&ret, "%s/%s/libvirt", home, xdgdefdir) < 0) + if (home && + virAsprintf(&ret, "%s/%s/libvirt", home, xdgdefdir) < 0) goto no_memory; } @@ -2456,7 +2466,9 @@ char *virGetUserRuntimeDirectory(void) char *virGetUserName(uid_t uid) { - return virGetUserEnt(uid, VIR_USER_ENT_NAME); + char *ret; + virGetUserEnt(uid, &ret, NULL, NULL); + return ret; } char *virGetGroupName(gid_t gid) -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list