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 */
 
 
-#ifdef	__USE_BSD
+#if defined __USE_BSD || defined __USE_GNU
 
 # define __need_size_t
 # 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
    *GROUPS.  Also include GROUP.  The actual number of groups found is
    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
    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.  */
 extern int getgrouplist (__const char *__user, __gid_t __group,
 			 __gid_t *__groups, int *__ngroups);
+
 #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
    by reading the group database and using all groups
    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,
 			   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
  *
@@ -684,62 +686,105 @@ struct spwd *sgetspent(const char *string)
 
 #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;
 	gid_t *group_list;
-	int num_groups, rv;
-	char **m;
+	int num_groups;
 	struct group group;
 	char buff[__UCLIBC_PWD_BUFFER_SIZE__];
 
-	rv = -1;
+	*ngroups = num_groups = 1;
 
 	/* 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);
 	return rv;
 }