On Saturday 27 September 2008 17:06, Tito wrote:
> On Saturday 27 September 2008 16:45:47 you wrote:
> > On Saturday 27 September 2008 16:00, Tito wrote:
> > > Hi Denys,
> > > maybe this patches slipped through as my cc to you get
> > > bounced by a spam filter that blacklisted my ip?
> > 
> > I received them. sorry about this delay.
> > 
> > > # [PATCH 1/3] add bb_getgrouplist_malloc to libbb   Tito
> > > http://www.busybox.net/lists/busybox/2008-September/033078.html
> > > # [PATCH 2/3] port id to bb_getgrouplist_malloc + fixes   Tito
> > > http://www.busybox.net/lists/busybox/2008-September/033079.html
> > 
> > Let's look at these two patches together.
> > 
> > Here:
> > 
> >         if (username) {
> > -#if HAVE_getgrouplist
> > -               int m;
> > -#endif
> >                 p = getpwnam(username);
> >                 /* xuname2uid is needed because it exits on failure */
> >                 uid = xuname2uid(username);
> >                 gid = p->pw_gid; /* in this case PRINT_REAL is the same */
> > -
> > -#if HAVE_getgrouplist
> > -               n = 16;
> > -               groups = NULL;
> > -               do {
> > -                       m = n;
> > -                       groups = xrealloc(groups, sizeof(groups[0]) * m);
> > -                       getgrouplist(username, gid, groups, &n); /* GNUism? 
> > */
> > -               } while (n > m);
> > -#endif
> > -       } else {
> > -#if HAVE_getgrouplist
> > -               n = getgroups(0, NULL);
> > -               groups = xmalloc(sizeof(groups[0]) * n);
> > -               getgroups(n, groups);
> > -#endif
> >         }
> > +       group_list = bb_getgrouplist_malloc(uid, gid, NULL);
> > 
> > you replace getgrouplist() call with bb_getgrouplist_malloc().
> > bb_getgrouplist_malloc() iterates through the whole set of group records:
> > 
> > +               while((grp = getgrent())) {
> > ...
> > +                       while (*(grp->gr_mem)) {
> > ...
> > +                       }
> > +               }
> > 
> > This may be very expensive. I know for the fact than in big organizations
> > some groups are HUGE - I saw 500kbytes large "guests" group.
> > (nscd died horribly trying to digest such a large group.)
> > 
> > In these cases, user db is not in /etc/XXX files. It is in LDAP etc.
> > 
> > getgrouplist() may have a more clever way of retrieving this data -
> > instead of pulling megabytes from user datrabase by iterating through
> > all groups it may just directly query uer db "give me group list
> > of this user". Likely to be less than kilobyte of data.
> > 
> > There is no way around it. If you want to make sure you have at least
> > a fleeting chance of not performing horribly, you must use libc interfaces
> > fro retrieving group lists, of which there is two known to me:
> > initgroups (standard, but useless in many ceses) and getgrouplist (GNUism).
> > Then, *if* your libc is well-written, it will hopefully do it efficiently.
> 
> Hi,
> getgrent in fact is used only if getgrouplist is not available as suggested 
> by Bernard,
> see #ifdef HAVE_getgrouplist

I missed that.

> > Secondly, make bloatcheck says:
> > 
> > function                                             old     new   delta
> > bb_getgrouplist_malloc                                 -     192    +192
> > print_single                                           -     106    +106
> > print_group_list                                       -      94     +94
> > decode_format_string                                 824     839     +15
> > ...(deleted gcc-induced random jitter +/-9 bytes)...
> > printf_full                                           44       -     -44
> > id_main                                              539     331    -208
> > ------------------------------------------------------------------------------
> > (add/remove: 3/1 grow/shrink: 4/4 up/down: 418/-259)          Total: 159 
> > bytes
> > 
> > This does not seem to be a progress.

> I suppose  +159 is with all 3 patches applied?

This was with only two first patches. I retested it on glibc, where
getgrouplist() is available:

function                                             old     new   delta
bb_getgrouplist_malloc                                 -     194    +194
print_single                                           -     106    +106
print_group_list                                       -      94     +94
bbunpack                                             386     394      +8
parse_command                                       1443    1440      -3
.rodata                                           125866  125852     -14
dpkg_main                                           2853    2836     -17
printf_full                                           44       -     -44
id_main                                              541     331    -210
------------------------------------------------------------------------------
(add/remove: 3/1 grow/shrink: 1/4 up/down: 402/-288)          Total: 114 bytes

> In this case bloat check is not so important as we are adding a function
> to libbb that could be reused in other applets and the new function
> has more features as the previous code: built in egid handling.

It's difficult to see how much of the growth is because of fixes
and how much because of bb_getgrouplist_malloc. Can you
submit them separately, so that it is possible
to measure them separately?

Meanwhile, I propose adding getgrouplist() to uclibc. See attached.
--
vda
diff -d -urpN -U5 uClibc.0/include/grp.h uClibc.1/include/grp.h
--- uClibc.0/include/grp.h	2008-09-27 16:49:39.000000000 +0200
+++ uClibc.1/include/grp.h	2008-09-27 17:15:56.000000000 +0200
@@ -167,31 +167,34 @@ extern int fgetgrent_r (FILE *__restrict
 # endif
 
 #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
    or due to the implementation it is a cancellation point and
    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.
 
    This function is not part of POSIX and therefore no official
diff -d -urpN -U5 uClibc.0/libc/pwd_grp/getgrouplist.c uClibc.1/libc/pwd_grp/getgrouplist.c
--- uClibc.0/libc/pwd_grp/getgrouplist.c	1970-01-01 01:00:00.000000000 +0100
+++ uClibc.1/libc/pwd_grp/getgrouplist.c	2008-09-28 00:34:50.000000000 +0200
@@ -0,0 +1,8 @@
+/*
+ * Copyright (C) 2000-2006 Erik Andersen <[EMAIL PROTECTED]>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+#define L_getgrouplist
+#include "pwd_grp.c"
diff -d -urpN -U5 uClibc.0/libc/pwd_grp/__getgrouplist_internal.c uClibc.1/libc/pwd_grp/__getgrouplist_internal.c
--- uClibc.0/libc/pwd_grp/__getgrouplist_internal.c	1970-01-01 01:00:00.000000000 +0100
+++ uClibc.1/libc/pwd_grp/__getgrouplist_internal.c	2008-09-28 00:34:50.000000000 +0200
@@ -0,0 +1,8 @@
+/*
+ * Copyright (C) 2000-2006 Erik Andersen <[EMAIL PROTECTED]>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+#define L___getgrouplist_internal
+#include "pwd_grp.c"
diff -d -urpN -U5 uClibc.0/libc/pwd_grp/pwd_grp.c uClibc.1/libc/pwd_grp/pwd_grp.c
--- uClibc.0/libc/pwd_grp/pwd_grp.c	2008-09-27 16:49:57.000000000 +0200
+++ uClibc.1/libc/pwd_grp/pwd_grp.c	2008-09-28 00:34:50.000000000 +0200
@@ -62,10 +62,12 @@ extern int __parsegrent(void *gr, char *
 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
  *
  *  0: success
  *  ENOENT: end-of-file encountered
@@ -682,66 +684,109 @@ struct spwd *sgetspent(const char *strin
 	return result;
 }
 
 #endif
 /**********************************************************************/
-#ifdef L_initgroups
-
-#ifdef __USE_BSD
-
-libc_hidden_proto(setgroups)
+#ifdef L___getgrouplist_internal
 
-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)
-		) {
+	group_list = malloc(8 * sizeof(group_list[0]));
+	if (!group_list)
+		return NULL;
 
-		__STDIO_SET_USER_LOCKING(grfile);
+	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;
 
-		*group_list = gid;
-		num_groups = 1;
+	__STDIO_SET_USER_LOCKING(grfile);
 
-		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;
-					}
-				}
+	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;
 		}
+	}
 
-		rv = setgroups(num_groups, group_list);
-	DO_CLOSE:
-		fclose(grfile);
+ DO_CLOSE:
+	fclose(grfile);
+	*ngroups = num_groups;
+	return group_list;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_getgrouplist
+
+#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 */
 
-	/* group_list will be NULL if initial malloc failed, which may trigger
-	 * warnings from various malloc debuggers. */
+	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
+
+#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;
 }
 #endif
 
_______________________________________________
busybox mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/busybox

Reply via email to