diff options
-rw-r--r-- | libgcompat/pwd.c | 119 |
1 files changed, 74 insertions, 45 deletions
diff --git a/libgcompat/pwd.c b/libgcompat/pwd.c index 50d8da0..34c5d7b 100644 --- a/libgcompat/pwd.c +++ b/libgcompat/pwd.c @@ -1,63 +1,92 @@ /* some musl versions incorrectly mark fgetpwent() as a GNU extension */ #define _GNU_SOURCE +#include <assert.h> /* assert */ +#include <errno.h> /* ENOENT, ERANGE */ +#include <pthread.h> /* pthread_mutex_* */ +#include <pwd.h> /* fgetpwent, getpwent, struct passwd */ +#include <stddef.h> /* NULL, size_t */ +#include <stdio.h> /* FILE */ +#include <string.h> /* memcpy, stpcpy, strlcpy, strlen */ -#include <errno.h> -#include <pwd.h> -#include <stdio.h> -#include <string.h> +static pthread_mutex_t pwent_mutex = PTHREAD_MUTEX_INITIALIZER; -int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, - struct passwd **pwbufp) +static int __fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf, + size_t len, struct passwd **result) { - struct passwd *pwd; + struct passwd *pwtmp; + char *cursor = buf, *end = buf + len; - if (pwbufp == NULL || pwbuf == NULL) { - return ERANGE; + *result = NULL; + pthread_mutex_lock(&pwent_mutex); + pwtmp = stream != NULL ? fgetpwent(stream) : getpwent(); + if (pwtmp == NULL) { + pthread_mutex_unlock(&pwent_mutex); + return ENOENT; } - - if (buflen < 1) { - return ERANGE; + memcpy(pwd, pwtmp, sizeof(*pwd)); + if (pwtmp->pw_name != NULL) { + pwd->pw_name = cursor; + cursor += strlcpy(cursor, pwtmp->pw_name, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } } - - if (buf != NULL) { - *buf = '\0'; + if (pwtmp->pw_passwd != NULL) { + pwd->pw_passwd = cursor; + cursor += strlcpy(cursor, pwtmp->pw_passwd, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } } - - if ((pwd = getpwent()) == NULL) { - *pwbufp = NULL; - return ENOENT; + if (pwtmp->pw_gecos != NULL) { + pwd->pw_gecos = cursor; + cursor += strlcpy(cursor, pwtmp->pw_gecos, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } } - - memcpy(pwbuf, pwd, sizeof(*pwd)); - *pwbufp = pwbuf; + if (pwtmp->pw_dir != NULL) { + pwd->pw_dir = cursor; + cursor += strlcpy(cursor, pwtmp->pw_dir, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } + } + if (pwtmp->pw_shell != NULL) { + pwd->pw_shell = cursor; + cursor += strlcpy(cursor, pwtmp->pw_shell, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } + } + pthread_mutex_unlock(&pwent_mutex); + *result = pwd; return 0; + +err_unlock: + pthread_mutex_unlock(&pwent_mutex); + return ERANGE; } -int fgetpwent_r(FILE *filp, struct passwd *pwbuf, char *buf, size_t buflen, - struct passwd **pwbufp) +/** + * Get passwd file entry. + */ +int fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf, size_t len, + struct passwd **result) { - struct passwd *pwd; - - if (pwbufp == NULL || pwbuf == NULL) { - return ERANGE; - } + assert(stream != NULL); - if (buflen < 1) { - return ERANGE; - } - - if (buf != NULL) { - *buf = '\0'; - } - - if ((pwd = fgetpwent(filp)) == NULL) { - *pwbufp = NULL; - return ENOENT; - } - - memcpy(pwbuf, pwd, sizeof(*pwd)); - *pwbufp = pwbuf; + return fgetpwent_r(stream, pwd, buf, len, result); +} - return 0; +/** + * Get user database entry. + * + * LSB 5.0: LSB-Core-generic/baselib-getpwent-r-1.html + */ +int getpwent_r(struct passwd *pwd, char *buf, size_t len, + struct passwd **result) +{ + return __fgetpwent_r(NULL, pwd, buf, len, result); } |