summaryrefslogtreecommitdiff
path: root/libgcompat
diff options
context:
space:
mode:
authorSamuel Holland <samuel@sholland.org>2018-01-14 22:17:13 -0600
committerSamuel Holland <samuel@sholland.org>2018-01-15 00:02:54 -0600
commit28096715d49fe420ed194e8520ec5e81bc2cdc85 (patch)
tree854bfe79c924dc6c66bc8065a45b543822935277 /libgcompat
parent810f46ab1cc801e54fc3836c22b5f1364cd632e6 (diff)
downloadgcompat-28096715d49fe420ed194e8520ec5e81bc2cdc85.tar.gz
gcompat-28096715d49fe420ed194e8520ec5e81bc2cdc85.tar.bz2
gcompat-28096715d49fe420ed194e8520ec5e81bc2cdc85.tar.xz
gcompat-28096715d49fe420ed194e8520ec5e81bc2cdc85.zip
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 <samuel@sholland.org>
Diffstat (limited to 'libgcompat')
-rw-r--r--libgcompat/grp.c98
1 files changed, 98 insertions, 0 deletions
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.h> /* assert */
+#include <errno.h> /* ENOENT, ERANGE */
+#include <grp.h> /* fgetgrent, getgrent, struct group */
+#include <pthread.h> /* pthread_mutex_* */
+#include <stddef.h> /* NULL, size_t */
+#include <stdint.h> /* ptrdiff_t, uintptr_t */
+#include <stdio.h> /* FILE */
+#include <string.h> /* 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);
+}