From 28096715d49fe420ed194e8520ec5e81bc2cdc85 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 14 Jan 2018 22:17:13 -0600 Subject: grp: Implement fgetgrent_r/getgrent_r Like fgetpwent_r/getpwent_r, these require a deep copy of the structure into the caller-provided buffer. This is nontrivial for the array of strings member. getgrent_r is required by LSB. [NOTE: I'm not too happy with the macro, but it works. Any suggestions?] Signed-off-by: Samuel Holland --- Makefile | 1 + libgcompat/grp.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 libgcompat/grp.c diff --git a/Makefile b/Makefile index 4873403..14c68f4 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ LIBGCOMPAT_SRC = \ libgcompat/dlfcn.c \ libgcompat/execinfo.c \ libgcompat/gnulib.c \ + libgcompat/grp.c \ libgcompat/malloc.c \ libgcompat/math.c \ libgcompat/pthread.c \ diff --git a/libgcompat/grp.c b/libgcompat/grp.c new file mode 100644 index 0000000..5674fda --- /dev/null +++ b/libgcompat/grp.c @@ -0,0 +1,98 @@ +/* some musl versions incorrectly mark fgetgrent() as a GNU extension */ +#define _GNU_SOURCE +#include /* assert */ +#include /* ENOENT, ERANGE */ +#include /* fgetgrent, getgrent, struct group */ +#include /* pthread_mutex_* */ +#include /* NULL, size_t */ +#include /* ptrdiff_t, uintptr_t */ +#include /* FILE */ +#include /* memcpy, stpcpy, strlcpy, strlen */ + +#define ALIGN_PTR_TO_SIZE_OF(ptr, type) \ + ((type *) ((((uintptr_t)(ptr)) + sizeof(type) - 1) \ + & ~(sizeof(type) - 1))) + +static pthread_mutex_t grent_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int __fgetgrent_r(FILE *stream, struct group *grp, char *buf, size_t len, + struct group **result) +{ + struct group *grtmp; + char *cursor = buf, *end = buf + len; + + *result = NULL; + pthread_mutex_lock(&grent_mutex); + grtmp = stream != NULL ? fgetgrent(stream) : getgrent(); + if (grtmp == NULL) { + pthread_mutex_unlock(&grent_mutex); + return ENOENT; + } + memcpy(grp, grtmp, sizeof(*grp)); + if (grtmp->gr_name != NULL) { + grp->gr_name = cursor; + cursor += strlcpy(cursor, grtmp->gr_name, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } + } + if (grtmp->gr_passwd != NULL) { + grp->gr_passwd = cursor; + cursor += strlcpy(cursor, grtmp->gr_passwd, end - cursor) + 1; + if (cursor > end) { + goto err_unlock; + } + } + if (grtmp->gr_mem != NULL) { + char **members = ALIGN_PTR_TO_SIZE_OF(cursor, char *); + ptrdiff_t nameslen = 0; + size_t nmem = 0; + + /* Calculate total size of strings plus their pointers. */ + while (grtmp->gr_mem[nmem++] != NULL) { + nameslen += strlen(grtmp->gr_mem[nmem - 1]) + 1; + } + nameslen += nmem * sizeof(*members); + if (nameslen > end - ((char *) members)) { + goto err_unlock; + } + /* Copy the pointers, including the NULL sentinel. */ + for (size_t i = 0; i < nmem; ++i) { + members[i] = grtmp->gr_mem[i]; + } + /* Copy the strings (the NULL sentinel doesn't point to one). */ + cursor = (char *) &members[nmem]; + for (size_t i = 0; i < nmem - 1; ++i) { + cursor = stpcpy(cursor, members[i]) + 1; + } + } + pthread_mutex_unlock(&grent_mutex); + *result = grp; + + return 0; + +err_unlock: + pthread_mutex_unlock(&grent_mutex); + return ERANGE; +} + +/** + * Get group file entry. + */ +int fgetgrent_r(FILE *stream, struct group *grp, char *buf, size_t len, + struct group **result) +{ + assert(stream != NULL); + + return __fgetgrent_r(stream, grp, buf, len, result); +} + +/** + * Get group database entry. + * + * LSB 5.0: LSB-Core-generic/baselib-getgrent-r-1.html + */ +int getgrent_r(struct group *grp, char *buf, size_t len, struct group **result) +{ + return __fgetgrent_r(NULL, grp, buf, len, result); +} -- cgit v1.2.3-60-g2f50