Browse Source

implement getgrouplist()

Denis Vlasenko 15 years ago
parent
commit
9385a9419d
2 changed files with 95 additions and 47 deletions
  1. 8 5
      include/grp.h
  2. 87 42
      libc/pwd_grp/pwd_grp.c

+ 8 - 5
include/grp.h

@@ -169,18 +169,15 @@ extern int fgetgrent_r (FILE *__restrict __stream,
 #endif	/* POSIX or reentrant */
 #endif	/* POSIX or reentrant */
 
 
 
 
-#ifdef	__USE_BSD
+#if defined __USE_BSD || defined __USE_GNU
 
 
 # define __need_size_t
 # define __need_size_t
 # include <stddef.h>
 # include <stddef.h>
 
 
-/* Set the group set for the current user to GROUPS (N of them).  */
-extern int setgroups (size_t __n, __const __gid_t *__groups) __THROW;
-
-#if 0
 /* Store at most *NGROUPS members of the group set for USER into
 /* Store at most *NGROUPS members of the group set for USER into
    *GROUPS.  Also include GROUP.  The actual number of groups found is
    *GROUPS.  Also include GROUP.  The actual number of groups found is
    returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.
    returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.
+   In all cases the actual number of groups is stored in *NGROUPS.
 
 
    This function is not part of POSIX and therefore no official
    This function is not part of POSIX and therefore no official
    cancellation point.  But due to similarity with an POSIX interface
    cancellation point.  But due to similarity with an POSIX interface
@@ -188,8 +185,14 @@ extern int setgroups (size_t __n, __const __gid_t *__groups) __THROW;
    therefore not marked with __THROW.  */
    therefore not marked with __THROW.  */
 extern int getgrouplist (__const char *__user, __gid_t __group,
 extern int getgrouplist (__const char *__user, __gid_t __group,
 			 __gid_t *__groups, int *__ngroups);
 			 __gid_t *__groups, int *__ngroups);
+
 #endif
 #endif
 
 
+#if defined __USE_BSD
+
+/* Set the group set for the current user to GROUPS (N of them).  */
+extern int setgroups (size_t __n, __const __gid_t *__groups) __THROW;
+
 /* Initialize the group set for the current user
 /* Initialize the group set for the current user
    by reading the group database and using all groups
    by reading the group database and using all groups
    of which USER is a member.  Also include GROUP.
    of which USER is a member.  Also include GROUP.

+ 87 - 42
libc/pwd_grp/pwd_grp.c

@@ -64,6 +64,8 @@ extern int __parsespent(void *sp, char *line) attribute_hidden;
 extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
 extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
 			   char *__restrict line_buff, size_t buflen, FILE *f) attribute_hidden;
 			   char *__restrict line_buff, size_t buflen, FILE *f) attribute_hidden;
 
 
+extern gid_t* __getgrouplist_internal(const char *user, gid_t gid, int *ngroups) attribute_hidden;
+
 /**********************************************************************/
 /**********************************************************************/
 /* For the various fget??ent_r funcs, return
 /* For the various fget??ent_r funcs, return
  *
  *
@@ -684,62 +686,105 @@ struct spwd *sgetspent(const char *string)
 
 
 #endif
 #endif
 /**********************************************************************/
 /**********************************************************************/
-#ifdef L_initgroups
+#ifdef L___getgrouplist_internal
 
 
-#ifdef __USE_BSD
-
-libc_hidden_proto(setgroups)
-
-int initgroups(const char *user, gid_t gid)
+gid_t attribute_hidden *__getgrouplist_internal(const char *user, gid_t gid, int *ngroups)
 {
 {
 	FILE *grfile;
 	FILE *grfile;
 	gid_t *group_list;
 	gid_t *group_list;
-	int num_groups, rv;
-	char **m;
+	int num_groups;
 	struct group group;
 	struct group group;
 	char buff[__UCLIBC_PWD_BUFFER_SIZE__];
 	char buff[__UCLIBC_PWD_BUFFER_SIZE__];
 
 
-	rv = -1;
+	*ngroups = num_groups = 1;
 
 
 	/* We alloc space for 8 gids at a time. */
 	/* We alloc space for 8 gids at a time. */
-	if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL)
-		&& ((grfile = fopen(_PATH_GROUP, "r")) != NULL)
-		) {
-
-		__STDIO_SET_USER_LOCKING(grfile);
-
-		*group_list = gid;
-		num_groups = 1;
-
-		while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grfile)) {
-			assert(group.gr_mem); /* Must have at least a NULL terminator. */
-			if (group.gr_gid != gid) {
-				for (m=group.gr_mem ; *m ; m++) {
-					if (!strcmp(*m, user)) {
-						if (!(num_groups & 7)) {
-							gid_t *tmp = (gid_t *)
-								realloc(group_list,
-										(num_groups+8) * sizeof(gid_t *));
-							if (!tmp) {
-								rv = -1;
-								goto DO_CLOSE;
-							}
-							group_list = tmp;
-						}
-						group_list[num_groups++] = group.gr_gid;
-						break;
-					}
-				}
+	group_list = malloc(8 * sizeof(group_list[0]));
+	if (!group_list)
+		return NULL;
+
+	group_list[0] = gid;
+	grfile = fopen(_PATH_GROUP, "r");
+	/* If /etc/group doesn't exist, we still return 1-element vector */
+	if (!grfile)
+		return group_list;
+
+	__STDIO_SET_USER_LOCKING(grfile);
+
+	while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grfile)) {
+		char **m;
+
+		assert(group.gr_mem); /* Must have at least a NULL terminator. */
+		if (group.gr_gid == gid)
+			continue;
+		for (m = group.gr_mem; *m; m++) {
+			if (strcmp(*m, user) != 0)
+				continue;
+			if (!(num_groups & 7)) {
+				gid_t *tmp = realloc(group_list, (num_groups+8) * sizeof(group_list[0]));
+				if (!tmp)
+					goto DO_CLOSE;
+				group_list = tmp;
 			}
 			}
+			group_list[num_groups++] = group.gr_gid;
+			break;
 		}
 		}
+	}
+
+ DO_CLOSE:
+	fclose(grfile);
+	*ngroups = num_groups;
+	return group_list;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrouplist
 
 
-		rv = setgroups(num_groups, group_list);
-	DO_CLOSE:
-		fclose(grfile);
+#if defined __USE_BSD || defined __USE_GNU
+int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
+{
+	int sz = *ngroups;
+	gid_t *group_list = __getgrouplist_internal(user, gid, ngroups);
+
+	if (!group_list) {
+		/* malloc failure - what shall we do?
+		 * fail with ENOMEM? I bet users never check for that */
+		/* *ngroups = 1; - already done by __getgrouplist_internal */
+		if (sz) {
+			groups[0] = gid;
+			return 1;
+		}
+		return -1;
 	}
 	}
+	/* *ngroups is non-zero here */
+
+	if (sz > *ngroups)
+		sz = *ngroups;
+	if (sz)
+		memcpy(groups, group_list, sz * sizeof(group_list[0]));
+	free(group_list);
+	if (sz < *ngroups)
+		return -1;
+	return sz;
+}
+#endif
+
+#endif
+/**********************************************************************/
+#ifdef L_initgroups
 
 
-	/* group_list will be NULL if initial malloc failed, which may trigger
-	 * warnings from various malloc debuggers. */
+#ifdef __USE_BSD
+libc_hidden_proto(setgroups)
+
+int initgroups(const char *user, gid_t gid)
+{
+	int rv;
+	int num_groups = ((unsigned)~0) >> 1; /* INT_MAX */
+	gid_t *group_list = __getgrouplist_internal(user, gid, &num_groups);
+	if (!group_list)
+		return -1;
+	rv = setgroups(num_groups, group_list);
 	free(group_list);
 	free(group_list);
 	return rv;
 	return rv;
 }
 }