enum nss_status
_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
			    long int *size, gid_t **groupsp, long int limit,
			    int *errnop)
{
  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
  char *tmpbuf;
  enum nss_status status;
  ent_t intern = { TRUE, NULL, {NULL, 0, 0} };

  status = internal_setgrent (&intern);
  if (status != NSS_STATUS_SUCCESS)
    return status;

  tmpbuf = __alloca (buflen);

  do
    {
      while ((status = internal_getgrent_r (&intern, tmpbuf, buflen,
					    user, group, start, size,
					    groupsp, limit, errnop))
	     == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
	tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
    }
  while (status == NSS_STATUS_SUCCESS);

  internal_endgrent (&intern);

  return NSS_STATUS_SUCCESS;
}
Example #2
0
static int
get_uid (const char *user, uid_t *uidp)
{
  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
  char *buf = (char *) alloca (buflen);

  while (1)
    {
      struct passwd result;
      struct passwd *resp;

      int r = getpwnam_r (user, &result, buf, buflen, &resp);
      if (r == 0 && resp != NULL)
	{
	  *uidp = resp->pw_uid;
	  return 0;
	}

      if (r != ERANGE)
	break;

      buf = extend_alloca (buf, buflen, 2 * buflen);
    }

  return 1;
}
Example #3
0
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset)
{
	size_t cnt;
	if (unlikely (__kernel_cpumask_size == 0)) {
		INTERNAL_SYSCALL_DECL (err);
		int res;
		size_t psize = 128;
		void *p = alloca (psize);

		while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, getpid (),
					       psize, p),
		       INTERNAL_SYSCALL_ERROR_P (res, err)
		       && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL)
			p = extend_alloca (p, psize, 2 * psize);

		if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err)) {
			__set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
			return -1;
		}

		__kernel_cpumask_size = res;
	}

	/* We now know the size of the kernel cpumask_t.  Make sure the user
	   does not request to set a bit beyond that.  */
	for (cnt = __kernel_cpumask_size; cnt < cpusetsize; ++cnt)
		if (((char *) cpuset)[cnt] != '\0') {
			/* Found a nonzero byte.  This means the user request cannot be
			   fulfilled.  */
			__set_errno (EINVAL);
			return -1;
		}

	return INLINE_SYSCALL (sched_setaffinity, 3, pid, cpusetsize, cpuset);
}
Example #4
0
enum nss_status
_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
			    long int *size, gid_t **groupsp, long int limit,
			    int *errnop)
{
  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
  char *tmpbuf;
  enum nss_status status;
  ent_t intern = { true, false, false, NULL, {NULL, 0, 0} };
  bool use_malloc = false;

  status = internal_setgrent (&intern);
  if (status != NSS_STATUS_SUCCESS)
    return status;

  tmpbuf = __alloca (buflen);

  do
    {
      while ((status = internal_getgrent_r (&intern, tmpbuf, buflen,
					    user, group, start, size,
					    groupsp, limit, errnop))
	     == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
        if (__libc_use_alloca (buflen * 2))
          tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
        else
          {
            buflen *= 2;
            char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen);
            if (newbuf == NULL)
              {
                status = NSS_STATUS_TRYAGAIN;
                goto done;
              }
            use_malloc = true;
            tmpbuf = newbuf;
          }
    }
  while (status == NSS_STATUS_SUCCESS);

  status = NSS_STATUS_SUCCESS;

 done:
  if (use_malloc)
    free (tmpbuf);

  internal_endgrent (&intern);

  return status;
}
int
__sched_setaffinity_new (pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset)
{
  if (__builtin_expect (__kernel_cpumask_size == 0, 0))
    {
      INTERNAL_SYSCALL_DECL (err);
      int res;

      size_t psize = 128;
      void *p = alloca (psize);

      while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, getpid (),
				     psize, p),
	     INTERNAL_SYSCALL_ERROR_P (res, err)
	     && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL)
	p = extend_alloca (p, psize, 2 * psize);

      if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err))
	{
	  __set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
	  return -1;
	}

      __kernel_cpumask_size = res;
    }

  /* We now know the size of the kernel cpumask_t.  Make sure the user
     does not request to set a bit beyond that.  */
  for (size_t cnt = __kernel_cpumask_size; cnt < cpusetsize; ++cnt)
    if (((char *) cpuset)[cnt] != '\0')
      {
        /* Found a nonzero byte.  This means the user request cannot be
	   fulfilled.  */
	__set_errno (EINVAL);
	return -1;
      }

  int result = INLINE_SYSCALL (sched_setaffinity, 3, pid, cpusetsize, cpuset);

#ifdef RESET_VGETCPU_CACHE
  if (result != -1)
    RESET_VGETCPU_CACHE ();
#endif

  return result;
}
int __determine_cpumask_size (pid_t tid)
{
  INTERNAL_SYSCALL_DECL (err);
  int res;

  size_t psize = 128;
  void *p = alloca (psize);

  while (res = INTERNAL_SYSCALL (sched_getaffinity, err, 3, tid, psize, p),
	 INTERNAL_SYSCALL_ERROR_P (res, err)
	 && INTERNAL_SYSCALL_ERRNO (res, err) == EINVAL)
    p = extend_alloca (p, psize, 2 * psize);

  if (res == 0 || INTERNAL_SYSCALL_ERROR_P (res, err))
    return INTERNAL_SYSCALL_ERRNO (res, err);

  __kernel_cpumask_size = res;

  return 0;
}
Example #7
0
enum nss_status
_nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
			 long int *size, gid_t **groupsp, long int limit,
			 int *errnop)
{
  /* We always need the domain name.  */
  char *domainname;
  if (yp_get_default_domain (&domainname))
    return NSS_STATUS_UNAVAIL;

  /* Check whether we are supposed to use the netid.byname map.  */
  if (_nsl_default_nss () & NSS_FLAG_NETID_AUTHORITATIVE)
    {
      /* We need the user ID.  */
      uid_t uid;

      if (get_uid (user, &uid) == 0
	  && initgroups_netid (uid, group, start, size, groupsp, limit,
			       errnop, domainname) == NSS_STATUS_SUCCESS)
	return NSS_STATUS_SUCCESS;
    }

  struct group grpbuf, *g;
  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
  char *tmpbuf;
  enum nss_status status;
  intern_t intern = { NULL, NULL, 0 };
  gid_t *groups = *groupsp;

  status = internal_setgrent (domainname, &intern);
  if (status != NSS_STATUS_SUCCESS)
    return status;

  tmpbuf = __alloca (buflen);

  while (1)
    {
      while ((status =
	      internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
				   &intern)) == NSS_STATUS_TRYAGAIN
             && *errnop == ERANGE)
	tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);

      if (status != NSS_STATUS_SUCCESS)
	{
	  if (status == NSS_STATUS_NOTFOUND)
	    status = NSS_STATUS_SUCCESS;
	  goto done;
	}

      g = &grpbuf;
      if (g->gr_gid != group)
        {
          char **m;

          for (m = g->gr_mem; *m != NULL; ++m)
            if (strcmp (*m, user) == 0)
              {
                /* Matches user.  Insert this group.  */
                if (*start == *size)
                  {
                    /* Need a bigger buffer.  */
		    gid_t *newgroups;
		    long int newsize;

		    if (limit > 0 && *size == limit)
		      /* We reached the maximum.  */
		      goto done;

		    if (limit <= 0)
		      newsize = 2 * *size;
		    else
		      newsize = MIN (limit, 2 * *size);

		    newgroups = realloc (groups, newsize * sizeof (*groups));
		    if (newgroups == NULL)
		      {
			status = NSS_STATUS_TRYAGAIN;
			*errnop = errno;
			goto done;
		      }
		    *groupsp = groups = newgroups;
                    *size = newsize;
                  }

                groups[*start] = g->gr_gid;
		*start += 1;

                break;
              }
        }
    }

done:
  while (intern.start != NULL)
    {
      intern.next = intern.start;
      intern.start = intern.start->next;
      free (intern.next);
    }

  return status;
}
/* Get the next group from NSS  (+ entry). If the NSS module supports
   initgroups_dyn, get all entries at once.  */
static enum nss_status
getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
		   gid_t group, long int *start, long int *size,
		   gid_t **groupsp, long int limit, int *errnop)
{
  enum nss_status status;
  struct group grpbuf;

  /* if this module does not support getgrent_r and initgroups_dyn,
     abort. We cannot find the needed group entries.  */
  if (nss_getgrent_r == NULL && nss_initgroups_dyn == NULL)
    return NSS_STATUS_UNAVAIL;

  /* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
     If this function is not supported, step through the whole group
     database with getgrent_r.  */
  if (nss_initgroups_dyn && nss_getgrgid_r)
    {
      long int mystart = 0;
      long int mysize = limit <= 0 ? *size : limit;
      gid_t *mygroups = malloc (mysize * sizeof (gid_t));

      if (mygroups == NULL)
	return NSS_STATUS_TRYAGAIN;

      /* For every gid in the list we get from the NSS module,
         get the whole group entry. We need to do this, since we
         need the group name to check if it is in the blacklist.
         In worst case, this is as twice as slow as stepping with
         getgrent_r through the whole group database. But for large
         group databases this is faster, since the user can only be
         in a limited number of groups.  */
      if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
			      limit, errnop) == NSS_STATUS_SUCCESS)
	{
	  /* A temporary buffer. We use the normal buffer, until we find
	     an entry, for which this buffer is to small.  In this case, we
	     overwrite the pointer with one to a bigger buffer.  */
	  char *tmpbuf = buffer;
	  size_t tmplen = buflen;
	  int i;

	  for (i = 0; i < mystart; i++)
	    {
	      while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf,
					       tmplen,
					       errnop)) == NSS_STATUS_TRYAGAIN
		     && *errnop == ERANGE)
		if (tmpbuf == buffer)
		  {
		    tmplen *= 2;
		    tmpbuf = __alloca (tmplen);
		  }
		else
		  tmpbuf = extend_alloca (tmpbuf, tmplen, 2 * tmplen);

	      if (!in_blacklist (grpbuf.gr_name,
				 strlen (grpbuf.gr_name), ent))
		check_and_add_group (user, group, start, size, groupsp,
				     limit, &grpbuf);
	    }

	  free (mygroups);

	  return NSS_STATUS_NOTFOUND;
	}

      free (mygroups);
    }

  /* If we come here, the NSS module does not support initgroups_dyn
     and we have to step through the whole list ourself.  */
  do
    {
      if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
	  NSS_STATUS_SUCCESS)
	return status;
    }
  while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));

  check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
  return NSS_STATUS_SUCCESS;
}
Example #9
0
int
__netlink_request (struct netlink_handle *h, int type)
{
  struct netlink_res *nlm_next;
  struct netlink_res **new_nlm_list;
  static volatile size_t buf_size = 4096;
  char *buf;
  struct sockaddr_nl nladdr;
  struct nlmsghdr *nlmh;
  ssize_t read_len;
  bool done = false;
  bool use_malloc = false;

  if (__netlink_sendreq (h, type) < 0)
    return -1;

  size_t this_buf_size = buf_size;
  if (__libc_use_alloca (this_buf_size))
    buf = alloca (this_buf_size);
  else
    {
      buf = malloc (this_buf_size);
      if (buf != NULL)
	use_malloc = true;
      else
	goto out_fail;
    }

  struct iovec iov = { buf, this_buf_size };

  if (h->nlm_list != NULL)
    new_nlm_list = &h->end_ptr->next;
  else
    new_nlm_list = &h->nlm_list;

  while (! done)
    {
      struct msghdr msg =
	{
	  (void *) &nladdr, sizeof (nladdr),
	  &iov, 1,
	  NULL, 0,
	  0
	};

      read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0));
      if (read_len < 0)
	goto out_fail;

      if (nladdr.nl_pid != 0)
	continue;

      if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0))
	{
	  if (this_buf_size >= SIZE_MAX / 2)
	    goto out_fail;

	  nlm_next = *new_nlm_list;
	  while (nlm_next != NULL)
	    {
	      struct netlink_res *tmpptr;

	      tmpptr = nlm_next->next;
	      free (nlm_next);
	      nlm_next = tmpptr;
	    }
	  *new_nlm_list = NULL;

	  if (__libc_use_alloca (2 * this_buf_size))
	    buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size);
	  else
	    {
	      this_buf_size *= 2;

	      char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size);
	      if (new_buf == NULL)
		goto out_fail;
	      new_buf = buf;

	      use_malloc = true;
	    }
	  buf_size = this_buf_size;

	  iov.iov_base = buf;
	  iov.iov_len = this_buf_size;

	  /* Increase sequence number, so that we can distinguish
	     between old and new request messages.  */
	  h->seq++;

	  if (__netlink_sendreq (h, type) < 0)
	    goto out_fail;

	  continue;
	}

      size_t count = 0;
      size_t remaining_len = read_len;
      for (nlmh = (struct nlmsghdr *) buf;
	   NLMSG_OK (nlmh, remaining_len);
	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len))
	{
	  if ((pid_t) nlmh->nlmsg_pid != h->pid
	      || nlmh->nlmsg_seq != h->seq)
	    continue;

	  ++count;
	  if (nlmh->nlmsg_type == NLMSG_DONE)
	    {
	      /* We found the end, leave the loop.  */
	      done = true;
	      break;
	    }
	  if (nlmh->nlmsg_type == NLMSG_ERROR)
	    {
	      struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh);
	      if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
		errno = EIO;
	      else
		errno = -nlerr->error;
	      goto out_fail;
	    }
	}

      /* If there was nothing with the expected nlmsg_pid and nlmsg_seq,
	 there is no point to record it.  */
      if (count == 0)
	continue;

      nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res)
						+ read_len);
      if (nlm_next == NULL)
	goto out_fail;
      nlm_next->next = NULL;
      nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len);
      nlm_next->size = read_len;
      nlm_next->seq = h->seq;
      if (h->nlm_list == NULL)
	h->nlm_list = nlm_next;
      else
	h->end_ptr->next = nlm_next;
      h->end_ptr = nlm_next;
    }

  if (use_malloc)
    free (buf);
  return 0;

out_fail:
  if (use_malloc)
    free (buf);
  return -1;
}
Example #10
0
/* Get the next group from NSS  (+ entry). If the NSS module supports
   initgroups_dyn, get all entries at once.  */
static enum nss_status
getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
		   gid_t group, long int *start, long int *size,
		   gid_t **groupsp, long int limit, int *errnop)
{
  enum nss_status status;
  struct group grpbuf;

  /* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
     If this function is not supported, step through the whole group
     database with getgrent_r.  */
  if (! ent->skip_initgroups_dyn)
    {
      long int mystart = 0;
      long int mysize = limit <= 0 ? *size : limit;
      gid_t *mygroups = malloc (mysize * sizeof (gid_t));

      if (mygroups == NULL)
	return NSS_STATUS_TRYAGAIN;

      /* For every gid in the list we get from the NSS module,
	 get the whole group entry. We need to do this, since we
	 need the group name to check if it is in the blacklist.
	 In worst case, this is as twice as slow as stepping with
	 getgrent_r through the whole group database. But for large
	 group databases this is faster, since the user can only be
	 in a limited number of groups.  */
      if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
			      limit, errnop) == NSS_STATUS_SUCCESS)
	{
	  status = NSS_STATUS_NOTFOUND;

	  /* If there is no blacklist we can trust the underlying
	     initgroups implementation.  */
	  if (ent->blacklist.current <= 1)
	    for (int i = 0; i < mystart; i++)
	      add_group (start, size, groupsp, limit, mygroups[i]);
	  else
	    {
	      /* A temporary buffer. We use the normal buffer, until we find
		 an entry, for which this buffer is to small.  In this case, we
		 overwrite the pointer with one to a bigger buffer.  */
	      char *tmpbuf = buffer;
	      size_t tmplen = buflen;
	      bool use_malloc = false;

	      for (int i = 0; i < mystart; i++)
		{
		  while ((status = nss_getgrgid_r (mygroups[i], &grpbuf,
						   tmpbuf, tmplen, errnop))
			 == NSS_STATUS_TRYAGAIN
			 && *errnop == ERANGE)
                    {
                      if (__libc_use_alloca (tmplen * 2))
                        {
                          if (tmpbuf == buffer)
                            {
                              tmplen *= 2;
                              tmpbuf = __alloca (tmplen);
                            }
                          else
                            tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2);
                        }
                      else
                        {
                          tmplen *= 2;
                          char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen);

                          if (newbuf == NULL)
                            {
                              status = NSS_STATUS_TRYAGAIN;
			      goto done;
                            }
                          use_malloc = true;
                          tmpbuf = newbuf;
                        }
                    }

		  if (__builtin_expect  (status != NSS_STATUS_NOTFOUND, 1))
		    {
		      if (__builtin_expect  (status != NSS_STATUS_SUCCESS, 0))
		        goto done;

		      if (!in_blacklist (grpbuf.gr_name,
					 strlen (grpbuf.gr_name), ent)
			  && check_and_add_group (user, group, start, size,
						  groupsp, limit, &grpbuf))
			{
			  if (nss_setgrent != NULL)
			    {
			      nss_setgrent (1);
			      ent->need_endgrent = true;
			    }
			  ent->skip_initgroups_dyn = true;

			  goto iter;
			}
		    }
		}

	      status = NSS_STATUS_NOTFOUND;

 done:
	      if (use_malloc)
	        free (tmpbuf);
	    }

	  free (mygroups);

	  return status;
	}

      free (mygroups);
    }

  /* If we come here, the NSS module does not support initgroups_dyn
     or we were confronted with a split group.  In these cases we have
     to step through the whole list ourself.  */
 iter:
  do
    {
      if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
	  NSS_STATUS_SUCCESS)
	break;
    }
  while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));

  if (status == NSS_STATUS_SUCCESS)
    check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);

  return status;
}
static struct if_nameindex *
if_nameindex_ioctl (void)
{
  int fd = __opensock ();
  struct ifconf ifc;
  unsigned int nifs, i;
  int rq_len;
  struct if_nameindex *idx = NULL;
# define RQ_IFS	4

  if (fd < 0)
    return NULL;

  ifc.ifc_buf = NULL;

  /* We may be able to get the needed buffer size directly, rather than
     guessing.  */
  if (! old_siocgifconf)
    {
      ifc.ifc_buf = NULL;
      ifc.ifc_len = 0;
      if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
	{
# if __ASSUME_SIOCGIFNAME == 0
	  old_siocgifconf = 1;
# endif
	  rq_len = RQ_IFS * sizeof (struct ifreq);
	}
      else
	rq_len = ifc.ifc_len;
    }
  else
    rq_len = RQ_IFS * sizeof (struct ifreq);

  /* Read all the interfaces out of the kernel.  */
  ifc.ifc_buf = alloca (rq_len);
  ifc.ifc_len = rq_len;
  while (1)
    {
        if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0)
	{
	  close_not_cancel_no_status (fd);
	  return NULL;
	}
      if (ifc.ifc_len < rq_len || ! old_siocgifconf)
	break;

      ifc.ifc_buf = extend_alloca (ifc.ifc_buf, rq_len, 2 * rq_len);
      ifc.ifc_len = rq_len;
    }

  nifs = ifc.ifc_len / sizeof (struct ifreq);

  idx = malloc ((nifs + 1) * sizeof (struct if_nameindex));
  if (idx == NULL)
    {
      close_not_cancel_no_status (fd);
      __set_errno (ENOBUFS);
      return NULL;
    }

  for (i = 0; i < nifs; ++i)
    {
      struct ifreq *ifr = &ifc.ifc_req[i];
      idx[i].if_name = __strdup (ifr->ifr_name);
      if (idx[i].if_name == NULL
	  || __ioctl (fd, SIOCGIFINDEX, ifr) < 0)
	{
	  int saved_errno = errno;
	  unsigned int j;

	  for (j =  0; j < i; ++j)
	    free (idx[j].if_name);
	  free (idx);
	  close_not_cancel_no_status (fd);
	  if (saved_errno == EINVAL)
	    saved_errno = ENOSYS;
	  else if (saved_errno == ENOMEM)
	    saved_errno = ENOBUFS;
	  __set_errno (saved_errno);
	  return NULL;
	}
      idx[i].if_index = ifr->ifr_ifindex;
    }

  idx[i].if_index = 0;
  idx[i].if_name = NULL;

  close_not_cancel_no_status (fd);
  return idx;
}
Example #12
0
enum nss_status
_nss_files_initgroups_dyn (const char *user, gid_t group, long int *start,
			   long int *size, gid_t **groupsp, long int limit,
			   int *errnop)
{
  FILE *stream = fopen ("/etc/group", "rce");
  if (stream == NULL)
    {
      *errnop = errno;
      return *errnop == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
    }

  /* No other thread using this stream.  */
  __fsetlocking (stream, FSETLOCKING_BYCALLER);

  char *line = NULL;
  size_t linelen = 0;
  enum nss_status status = NSS_STATUS_SUCCESS;
  bool any = false;

  size_t buflen = 1024;
  void *buffer = alloca (buflen);
  bool buffer_use_malloc = false;

  gid_t *groups = *groupsp;

  /* We have to iterate over the entire file.  */
  while (1)
    {
      fpos_t pos;
      fgetpos (stream, &pos);
      ssize_t n = getline (&line, &linelen, stream);
      if (n < 0)
	{
	  if (! feof_unlocked (stream))
	    status = ((*errnop = errno) == ENOMEM
		      ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL);
	  break;
	}

      struct group grp;
      int res = _nss_files_parse_grent (line, &grp, buffer, buflen, errnop);
      if (res == -1)
	{
	  size_t newbuflen = 2 * buflen;
	  if (buffer_use_malloc || ! __libc_use_alloca (buflen + newbuflen))
	    {
	      void *newbuf = realloc (buffer_use_malloc ? buffer : NULL,
				      newbuflen);
	      if (newbuf == NULL)
		{
		  *errnop = ENOMEM;
		  status = NSS_STATUS_TRYAGAIN;
		  goto out;
		}
	      buffer = newbuf;
	      buflen = newbuflen;
	      buffer_use_malloc = true;
	    }
	  else
	    buffer = extend_alloca (buffer, buflen, newbuflen);
	  /* Reread current line, the parser has clobbered it.  */
	  fsetpos (stream, &pos);
	  continue;
	}

      if (res > 0 && grp.gr_gid != group)
	for (char **m = grp.gr_mem; *m != NULL; ++m)
	  if (strcmp (*m, user) == 0)
	    {
	      /* Matches user.  Insert this group.  */
	      if (*start == *size)
		{
		  /* Need a bigger buffer.  */
		  if (limit > 0 && *size == limit)
		    /* We reached the maximum.  */
		    goto out;

		  long int newsize;
		  if (limit <= 0)
		    newsize = 2 * *size;
		  else
		    newsize = MIN (limit, 2 * *size);

		  gid_t *newgroups = realloc (groups,
					      newsize * sizeof (*groups));
		  if (newgroups == NULL)
		    {
		      *errnop = ENOMEM;
		      status = NSS_STATUS_TRYAGAIN;
		      goto out;
		    }
		  *groupsp = groups = newgroups;
		  *size = newsize;
		}

	      groups[*start] = grp.gr_gid;
	      *start += 1;
	      any = true;

	      break;
	    }
    }

 out:
  /* Free memory.  */
  if (buffer_use_malloc)
    free (buffer);
  free (line);

  fclose (stream);

  return status == NSS_STATUS_SUCCESS && !any ? NSS_STATUS_NOTFOUND : status;
}