Blame nscd/netgroupcache.c

Packit 6c4009
/* Cache handling for netgroup lookup.
Packit 6c4009
   Copyright (C) 2011-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <alloca.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
Packit 6c4009
#include "../inet/netgroup.h"
Packit 6c4009
#include "nscd.h"
Packit 6c4009
#include "dbg_log.h"
Packit 6c4009
Packit 6c4009
#include <kernel-features.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is the standard reply in case the service is disabled.  */
Packit 6c4009
static const netgroup_response_header disabled =
Packit 6c4009
{
Packit 6c4009
  .version = NSCD_VERSION,
Packit 6c4009
  .found = -1,
Packit 6c4009
  .nresults = 0,
Packit 6c4009
  .result_len = 0
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* This is the struct describing how to write this record.  */
Packit 6c4009
const struct iovec netgroup_iov_disabled =
Packit 6c4009
{
Packit 6c4009
  .iov_base = (void *) &disabled,
Packit 6c4009
  .iov_len = sizeof (disabled)
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is the standard reply in case we haven't found the dataset.  */
Packit 6c4009
static const netgroup_response_header notfound =
Packit 6c4009
{
Packit 6c4009
  .version = NSCD_VERSION,
Packit 6c4009
  .found = 0,
Packit 6c4009
  .nresults = 0,
Packit 6c4009
  .result_len = 0
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct dataset
Packit 6c4009
{
Packit 6c4009
  struct datahead head;
Packit 6c4009
  netgroup_response_header resp;
Packit 6c4009
  char strdata[0];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Sends a notfound message and prepares a notfound dataset to write to the
Packit 6c4009
   cache.  Returns true if there was enough memory to allocate the dataset and
Packit 6c4009
   returns the dataset in DATASETP, total bytes to write in TOTALP and the
Packit 6c4009
   timeout in TIMEOUTP.  KEY_COPY is set to point to the copy of the key in the
Packit 6c4009
   dataset. */
Packit 6c4009
static bool
Packit 6c4009
do_notfound (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
	       const char *key, struct dataset **datasetp, ssize_t *totalp,
Packit 6c4009
	       time_t *timeoutp, char **key_copy)
Packit 6c4009
{
Packit 6c4009
  struct dataset *dataset;
Packit 6c4009
  ssize_t total;
Packit 6c4009
  time_t timeout;
Packit 6c4009
  bool cacheable = false;
Packit 6c4009
Packit 6c4009
  total = sizeof (notfound);
Packit 6c4009
  timeout = time (NULL) + db->negtimeout;
Packit 6c4009
Packit 6c4009
  if (fd != -1)
Packit 6c4009
    TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
Packit 6c4009
Packit 6c4009
  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
Packit 6c4009
  /* If we cannot permanently store the result, so be it.  */
Packit 6c4009
  if (dataset != NULL)
Packit 6c4009
    {
Packit 6c4009
      timeout = datahead_init_neg (&dataset->head,
Packit 6c4009
				   sizeof (struct dataset) + req->key_len,
Packit 6c4009
				   total, db->negtimeout);
Packit 6c4009
Packit 6c4009
      /* This is the reply.  */
Packit 6c4009
      memcpy (&dataset->resp, &notfound, total);
Packit 6c4009
Packit 6c4009
      /* Copy the key data.  */
Packit 6c4009
      memcpy (dataset->strdata, key, req->key_len);
Packit 6c4009
      *key_copy = dataset->strdata;
Packit 6c4009
Packit 6c4009
      cacheable = true;
Packit 6c4009
    }
Packit 6c4009
  *timeoutp = timeout;
Packit 6c4009
  *totalp = total;
Packit 6c4009
  *datasetp = dataset;
Packit 6c4009
  return cacheable;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static time_t
Packit 6c4009
addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
		 const char *key, uid_t uid, struct hashentry *he,
Packit Service f86772
		 struct datahead *dh, struct dataset **resultp,
Packit Service f86772
		 void **tofreep)
Packit 6c4009
{
Packit 6c4009
  if (__glibc_unlikely (debug_level > 0))
Packit 6c4009
    {
Packit 6c4009
      if (he == NULL)
Packit 6c4009
	dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key);
Packit 6c4009
      else
Packit 6c4009
	dbg_log (_("Reloading \"%s\" in netgroup cache!"), key);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  static service_user *netgroup_database;
Packit 6c4009
  time_t timeout;
Packit 6c4009
  struct dataset *dataset;
Packit 6c4009
  bool cacheable = false;
Packit 6c4009
  ssize_t total;
Packit 6c4009
  bool found = false;
Packit 6c4009
Packit 6c4009
  char *key_copy = NULL;
Packit 6c4009
  struct __netgrent data;
Packit 6c4009
  size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
Packit 6c4009
  size_t buffilled = sizeof (*dataset);
Packit 6c4009
  char *buffer = NULL;
Packit 6c4009
  size_t nentries = 0;
Packit 6c4009
  size_t group_len = strlen (key) + 1;
Packit 6c4009
  struct name_list *first_needed
Packit 6c4009
    = alloca (sizeof (struct name_list) + group_len);
Packit Service f86772
  *tofreep = NULL;
Packit 6c4009
Packit 6c4009
  if (netgroup_database == NULL
Packit Service 8fc214
      && __nss_database_lookup2 ("netgroup", NULL, NULL, &netgroup_database))
Packit 6c4009
    {
Packit 6c4009
      /* No such service.  */
Packit 6c4009
      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
Packit 6c4009
			       &key_copy);
Packit 6c4009
      goto writeout;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  memset (&data, '\0', sizeof (data));
Packit 6c4009
  buffer = xmalloc (buflen);
Packit Service f86772
  *tofreep = buffer;
Packit 6c4009
  first_needed->next = first_needed;
Packit 6c4009
  memcpy (first_needed->name, key, group_len);
Packit 6c4009
  data.needed_groups = first_needed;
Packit 6c4009
Packit 6c4009
  while (data.needed_groups != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Add the next group to the list of those which are known.  */
Packit 6c4009
      struct name_list *this_group = data.needed_groups->next;
Packit 6c4009
      if (this_group == data.needed_groups)
Packit 6c4009
	data.needed_groups = NULL;
Packit 6c4009
      else
Packit 6c4009
	data.needed_groups->next = this_group->next;
Packit 6c4009
      this_group->next = data.known_groups;
Packit 6c4009
      data.known_groups = this_group;
Packit 6c4009
Packit 6c4009
      union
Packit 6c4009
      {
Packit 6c4009
	enum nss_status (*f) (const char *, struct __netgrent *);
Packit 6c4009
	void *ptr;
Packit 6c4009
      } setfct;
Packit 6c4009
Packit 6c4009
      service_user *nip = netgroup_database;
Packit 6c4009
      int no_more = __nss_lookup (&nip, "setnetgrent", NULL, &setfct.ptr);
Packit 6c4009
      while (!no_more)
Packit 6c4009
	{
Packit 6c4009
	  enum nss_status status
Packit 6c4009
	    = DL_CALL_FCT (*setfct.f, (data.known_groups->name, &data));
Packit 6c4009
Packit 6c4009
	  if (status == NSS_STATUS_SUCCESS)
Packit 6c4009
	    {
Packit 6c4009
	      found = true;
Packit 6c4009
	      union
Packit 6c4009
	      {
Packit 6c4009
		enum nss_status (*f) (struct __netgrent *, char *, size_t,
Packit 6c4009
				      int *);
Packit 6c4009
		void *ptr;
Packit 6c4009
	      } getfct;
Packit 6c4009
	      getfct.ptr = __nss_lookup_function (nip, "getnetgrent_r");
Packit 6c4009
	      if (getfct.f != NULL)
Packit 6c4009
		while (1)
Packit 6c4009
		  {
Packit 6c4009
		    int e;
Packit 6c4009
		    status = getfct.f (&data, buffer + buffilled,
Packit 6c4009
				       buflen - buffilled - req->key_len, &e);
Packit 6c4009
		    if (status == NSS_STATUS_SUCCESS)
Packit 6c4009
		      {
Packit 6c4009
			if (data.type == triple_val)
Packit 6c4009
			  {
Packit 6c4009
			    const char *nhost = data.val.triple.host;
Packit 6c4009
			    const char *nuser = data.val.triple.user;
Packit 6c4009
			    const char *ndomain = data.val.triple.domain;
Packit 6c4009
Packit 6c4009
			    size_t hostlen = strlen (nhost ?: "") + 1;
Packit 6c4009
			    size_t userlen = strlen (nuser ?: "") + 1;
Packit 6c4009
			    size_t domainlen = strlen (ndomain ?: "") + 1;
Packit 6c4009
Packit 6c4009
			    if (nhost == NULL || nuser == NULL || ndomain == NULL
Packit 6c4009
				|| nhost > nuser || nuser > ndomain)
Packit 6c4009
			      {
Packit 6c4009
				const char *last = nhost;
Packit 6c4009
				if (last == NULL
Packit 6c4009
				    || (nuser != NULL && nuser > last))
Packit 6c4009
				  last = nuser;
Packit 6c4009
				if (last == NULL
Packit 6c4009
				    || (ndomain != NULL && ndomain > last))
Packit 6c4009
				  last = ndomain;
Packit 6c4009
Packit 6c4009
				size_t bufused
Packit 6c4009
				  = (last == NULL
Packit 6c4009
				     ? buffilled
Packit 6c4009
				     : last + strlen (last) + 1 - buffer);
Packit 6c4009
Packit 6c4009
				/* We have to make temporary copies.  */
Packit 6c4009
				size_t needed = hostlen + userlen + domainlen;
Packit 6c4009
Packit 6c4009
				if (buflen - req->key_len - bufused < needed)
Packit 6c4009
				  {
Packit 6c4009
				    buflen += MAX (buflen, 2 * needed);
Packit 6c4009
				    /* Save offset in the old buffer.  We don't
Packit 6c4009
				       bother with the NULL check here since
Packit 6c4009
				       we'll do that later anyway.  */
Packit 6c4009
				    size_t nhostdiff = nhost - buffer;
Packit 6c4009
				    size_t nuserdiff = nuser - buffer;
Packit 6c4009
				    size_t ndomaindiff = ndomain - buffer;
Packit 6c4009
Packit 6c4009
				    char *newbuf = xrealloc (buffer, buflen);
Packit 6c4009
				    /* Fix up the triplet pointers into the new
Packit 6c4009
				       buffer.  */
Packit 6c4009
				    nhost = (nhost ? newbuf + nhostdiff
Packit 6c4009
					     : NULL);
Packit 6c4009
				    nuser = (nuser ? newbuf + nuserdiff
Packit 6c4009
					     : NULL);
Packit 6c4009
				    ndomain = (ndomain ? newbuf + ndomaindiff
Packit 6c4009
					       : NULL);
Packit 6c4009
				    buffer = newbuf;
Packit 6c4009
				  }
Packit 6c4009
Packit 6c4009
				nhost = memcpy (buffer + bufused,
Packit 6c4009
						nhost ?: "", hostlen);
Packit 6c4009
				nuser = memcpy ((char *) nhost + hostlen,
Packit 6c4009
						nuser ?: "", userlen);
Packit 6c4009
				ndomain = memcpy ((char *) nuser + userlen,
Packit 6c4009
						  ndomain ?: "", domainlen);
Packit 6c4009
			      }
Packit 6c4009
Packit 6c4009
			    char *wp = buffer + buffilled;
Packit 6c4009
			    wp = memmove (wp, nhost ?: "", hostlen);
Packit 6c4009
			    wp += hostlen;
Packit 6c4009
			    wp = memmove (wp, nuser ?: "", userlen);
Packit 6c4009
			    wp += userlen;
Packit 6c4009
			    wp = memmove (wp, ndomain ?: "", domainlen);
Packit 6c4009
			    wp += domainlen;
Packit 6c4009
			    buffilled = wp - buffer;
Packit 6c4009
			    ++nentries;
Packit 6c4009
			  }
Packit 6c4009
			else
Packit 6c4009
			  {
Packit 6c4009
			    /* Check that the group has not been
Packit 6c4009
			       requested before.  */
Packit 6c4009
			    struct name_list *runp = data.needed_groups;
Packit 6c4009
			    if (runp != NULL)
Packit 6c4009
			      while (1)
Packit 6c4009
				{
Packit 6c4009
				  if (strcmp (runp->name, data.val.group) == 0)
Packit 6c4009
				    break;
Packit 6c4009
Packit 6c4009
				  runp = runp->next;
Packit 6c4009
				  if (runp == data.needed_groups)
Packit 6c4009
				    {
Packit 6c4009
				      runp = NULL;
Packit 6c4009
				      break;
Packit 6c4009
				    }
Packit 6c4009
				}
Packit 6c4009
Packit 6c4009
			    if (runp == NULL)
Packit 6c4009
			      {
Packit 6c4009
				runp = data.known_groups;
Packit 6c4009
				while (runp != NULL)
Packit 6c4009
				  if (strcmp (runp->name, data.val.group) == 0)
Packit 6c4009
				    break;
Packit 6c4009
				  else
Packit 6c4009
				    runp = runp->next;
Packit 6c4009
				}
Packit 6c4009
Packit 6c4009
			    if (runp == NULL)
Packit 6c4009
			      {
Packit 6c4009
				/* A new group is requested.  */
Packit 6c4009
				size_t namelen = strlen (data.val.group) + 1;
Packit 6c4009
				struct name_list *newg = alloca (sizeof (*newg)
Packit 6c4009
								 + namelen);
Packit 6c4009
				memcpy (newg->name, data.val.group, namelen);
Packit 6c4009
				if (data.needed_groups == NULL)
Packit 6c4009
				  data.needed_groups = newg->next = newg;
Packit 6c4009
				else
Packit 6c4009
				  {
Packit 6c4009
				    newg->next = data.needed_groups->next;
Packit 6c4009
				    data.needed_groups->next = newg;
Packit 6c4009
				    data.needed_groups = newg;
Packit 6c4009
				  }
Packit 6c4009
			      }
Packit 6c4009
			  }
Packit 6c4009
		      }
Packit 6c4009
		    else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
Packit 6c4009
		      {
Packit 6c4009
			buflen *= 2;
Packit 6c4009
			buffer = xrealloc (buffer, buflen);
Packit 6c4009
		      }
Packit 6c4009
		    else if (status == NSS_STATUS_RETURN
Packit 6c4009
			     || status == NSS_STATUS_NOTFOUND
Packit 6c4009
			     || status == NSS_STATUS_UNAVAIL)
Packit 6c4009
		      /* This was either the last one for this group or the
Packit 6c4009
			 group was empty or the NSS module had an internal
Packit 6c4009
			 failure.  Look at next group if available.  */
Packit 6c4009
		      break;
Packit 6c4009
		  }
Packit 6c4009
Packit 6c4009
	      enum nss_status (*endfct) (struct __netgrent *);
Packit 6c4009
	      endfct = __nss_lookup_function (nip, "endnetgrent");
Packit 6c4009
	      if (endfct != NULL)
Packit 6c4009
		(void) DL_CALL_FCT (*endfct, (&data));
Packit 6c4009
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  no_more = __nss_next2 (&nip, "setnetgrent", NULL, &setfct.ptr,
Packit 6c4009
				 status, 0);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* No results.  Return a failure and write out a notfound record in the
Packit 6c4009
     cache.  */
Packit 6c4009
  if (!found)
Packit 6c4009
    {
Packit 6c4009
      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
Packit 6c4009
			       &key_copy);
Packit 6c4009
      goto writeout;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  total = buffilled;
Packit 6c4009
Packit 6c4009
  /* Fill in the dataset.  */
Packit 6c4009
  dataset = (struct dataset *) buffer;
Packit 6c4009
  timeout = datahead_init_pos (&dataset->head, total + req->key_len,
Packit 6c4009
			       total - offsetof (struct dataset, resp),
Packit 6c4009
			       he == NULL ? 0 : dh->nreloads + 1,
Packit 6c4009
			       db->postimeout);
Packit 6c4009
Packit 6c4009
  dataset->resp.version = NSCD_VERSION;
Packit 6c4009
  dataset->resp.found = 1;
Packit 6c4009
  dataset->resp.nresults = nentries;
Packit 6c4009
  dataset->resp.result_len = buffilled - sizeof (*dataset);
Packit 6c4009
Packit 6c4009
  assert (buflen - buffilled >= req->key_len);
Packit 6c4009
  key_copy = memcpy (buffer + buffilled, key, req->key_len);
Packit 6c4009
  buffilled += req->key_len;
Packit 6c4009
Packit 6c4009
  /* Now we can determine whether on refill we have to create a new
Packit 6c4009
     record or not.  */
Packit 6c4009
  if (he != NULL)
Packit 6c4009
    {
Packit 6c4009
      assert (fd == -1);
Packit 6c4009
Packit 6c4009
      if (dataset->head.allocsize == dh->allocsize
Packit 6c4009
	  && dataset->head.recsize == dh->recsize
Packit 6c4009
	  && memcmp (&dataset->resp, dh->data,
Packit 6c4009
		     dh->allocsize - offsetof (struct dataset, resp)) == 0)
Packit 6c4009
	{
Packit 6c4009
	  /* The data has not changed.  We will just bump the timeout
Packit 6c4009
	     value.  Note that the new record has been allocated on
Packit 6c4009
	     the stack and need not be freed.  */
Packit 6c4009
	  dh->timeout = dataset->head.timeout;
Packit 6c4009
	  dh->ttl = dataset->head.ttl;
Packit 6c4009
	  ++dh->nreloads;
Packit 6c4009
	  dataset = (struct dataset *) dh;
Packit 6c4009
Packit 6c4009
	  goto out;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  {
Packit 6c4009
    struct dataset *newp
Packit 6c4009
      = (struct dataset *) mempool_alloc (db, total + req->key_len, 1);
Packit 6c4009
    if (__glibc_likely (newp != NULL))
Packit 6c4009
      {
Packit 6c4009
	/* Adjust pointer into the memory block.  */
Packit 6c4009
	key_copy = (char *) newp + (key_copy - buffer);
Packit 6c4009
Packit 6c4009
	dataset = memcpy (newp, dataset, total + req->key_len);
Packit 6c4009
	cacheable = true;
Packit 6c4009
Packit 6c4009
	if (he != NULL)
Packit 6c4009
	  /* Mark the old record as obsolete.  */
Packit 6c4009
	  dh->usable = false;
Packit 6c4009
      }
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  if (he == NULL && fd != -1)
Packit 6c4009
    {
Packit 6c4009
      /* We write the dataset before inserting it to the database
Packit 6c4009
	 since while inserting this thread might block and so would
Packit 6c4009
	 unnecessarily let the receiver wait.  */
Packit 6c4009
    writeout:
Packit 6c4009
      writeall (fd, &dataset->resp, dataset->head.recsize);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (cacheable)
Packit 6c4009
    {
Packit 6c4009
      /* If necessary, we also propagate the data to disk.  */
Packit 6c4009
      if (db->persistent)
Packit 6c4009
	{
Packit 6c4009
	  // XXX async OK?
Packit 6c4009
	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
Packit 6c4009
	  msync ((void *) pval,
Packit 6c4009
		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
Packit 6c4009
		 MS_ASYNC);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
Packit 6c4009
			true, db, uid, he == NULL);
Packit 6c4009
Packit 6c4009
      pthread_rwlock_unlock (&db->lock);
Packit 6c4009
Packit 6c4009
      /* Mark the old entry as obsolete.  */
Packit 6c4009
      if (dh != NULL)
Packit 6c4009
	dh->usable = false;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  *resultp = dataset;
Packit 6c4009
Packit 6c4009
  return timeout;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static time_t
Packit 6c4009
addinnetgrX (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
	     char *key, uid_t uid, struct hashentry *he,
Packit 6c4009
	     struct datahead *dh)
Packit 6c4009
{
Packit 6c4009
  const char *group = key;
Packit 6c4009
  key = (char *) rawmemchr (key, '\0') + 1;
Packit 6c4009
  size_t group_len = key - group;
Packit 6c4009
  const char *host = *key++ ? key : NULL;
Packit 6c4009
  if (host != NULL)
Packit 6c4009
    key = (char *) rawmemchr (key, '\0') + 1;
Packit 6c4009
  const char *user = *key++ ? key : NULL;
Packit 6c4009
  if (user != NULL)
Packit 6c4009
    key = (char *) rawmemchr (key, '\0') + 1;
Packit 6c4009
  const char *domain = *key++ ? key : NULL;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (debug_level > 0))
Packit 6c4009
    {
Packit 6c4009
      if (he == NULL)
Packit 6c4009
	dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
Packit 6c4009
		 group, host ?: "", user ?: "", domain ?: "");
Packit 6c4009
      else
Packit 6c4009
	dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
Packit 6c4009
		 group, host ?: "", user ?: "", domain ?: "");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct dataset *result = (struct dataset *) cache_search (GETNETGRENT,
Packit 6c4009
							    group, group_len,
Packit 6c4009
							    db, uid);
Packit 6c4009
  time_t timeout;
Packit Service f86772
  void *tofree;
Packit 6c4009
  if (result != NULL)
Packit Service f86772
    {
Packit Service f86772
      timeout = result->head.timeout;
Packit Service f86772
      tofree = NULL;
Packit Service f86772
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      request_header req_get =
Packit 6c4009
	{
Packit 6c4009
	  .type = GETNETGRENT,
Packit 6c4009
	  .key_len = group_len
Packit 6c4009
	};
Packit 6c4009
      timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
Packit Service f86772
				 &result, &tofree);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct indataset
Packit 6c4009
  {
Packit 6c4009
    struct datahead head;
Packit 6c4009
    innetgroup_response_header resp;
Packit 6c4009
  } *dataset
Packit 6c4009
      = (struct indataset *) mempool_alloc (db,
Packit 6c4009
					    sizeof (*dataset) + req->key_len,
Packit 6c4009
					    1);
Packit 6c4009
  struct indataset dataset_mem;
Packit 6c4009
  bool cacheable = true;
Packit 6c4009
  if (__glibc_unlikely (dataset == NULL))
Packit 6c4009
    {
Packit 6c4009
      cacheable = false;
Packit 6c4009
      dataset = &dataset_mem;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
Packit 6c4009
		     sizeof (innetgroup_response_header),
Packit 6c4009
		     he == NULL ? 0 : dh->nreloads + 1, result->head.ttl);
Packit 6c4009
  /* Set the notfound status and timeout based on the result from
Packit 6c4009
     getnetgrent.  */
Packit 6c4009
  dataset->head.notfound = result->head.notfound;
Packit 6c4009
  dataset->head.timeout = timeout;
Packit 6c4009
Packit 6c4009
  dataset->resp.version = NSCD_VERSION;
Packit 6c4009
  dataset->resp.found = result->resp.found;
Packit 6c4009
  /* Until we find a matching entry the result is 0.  */
Packit 6c4009
  dataset->resp.result = 0;
Packit 6c4009
Packit 6c4009
  char *key_copy = memcpy ((char *) (dataset + 1), group, req->key_len);
Packit 6c4009
Packit 6c4009
  if (dataset->resp.found)
Packit 6c4009
    {
Packit 6c4009
      const char *triplets = (const char *) (&result->resp + 1);
Packit 6c4009
Packit 6c4009
      for (nscd_ssize_t i = result->resp.nresults; i > 0; --i)
Packit 6c4009
	{
Packit 6c4009
	  bool success = true;
Packit 6c4009
Packit 6c4009
	  /* For the host, user and domain in each triplet, we assume success
Packit 6c4009
	     if the value is blank because that is how the wildcard entry to
Packit 6c4009
	     match anything is stored in the netgroup cache.  */
Packit 6c4009
	  if (host != NULL && *triplets != '\0')
Packit 6c4009
	    success = strcmp (host, triplets) == 0;
Packit 6c4009
	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
Packit 6c4009
Packit 6c4009
	  if (success && user != NULL && *triplets != '\0')
Packit 6c4009
	    success = strcmp (user, triplets) == 0;
Packit 6c4009
	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
Packit 6c4009
Packit 6c4009
	  if (success && (domain == NULL || *triplets == '\0'
Packit 6c4009
			  || strcmp (domain, triplets) == 0))
Packit 6c4009
	    {
Packit 6c4009
	      dataset->resp.result = 1;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (he != NULL && dh->data[0].innetgroupdata.result == dataset->resp.result)
Packit 6c4009
    {
Packit 6c4009
      /* The data has not changed.  We will just bump the timeout
Packit 6c4009
	 value.  Note that the new record has been allocated on
Packit 6c4009
	 the stack and need not be freed.  */
Packit 6c4009
      dh->timeout = timeout;
Packit 6c4009
      dh->ttl = dataset->head.ttl;
Packit 6c4009
      ++dh->nreloads;
Packit 6c4009
      if (cacheable)
Packit 6c4009
        pthread_rwlock_unlock (&db->lock);
Packit Service f86772
      goto out;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (he == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* We write the dataset before inserting it to the database
Packit 6c4009
	 since while inserting this thread might block and so would
Packit 6c4009
	 unnecessarily let the receiver wait.  */
Packit 6c4009
      assert (fd != -1);
Packit 6c4009
Packit 6c4009
      writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (cacheable)
Packit 6c4009
    {
Packit 6c4009
      /* If necessary, we also propagate the data to disk.  */
Packit 6c4009
      if (db->persistent)
Packit 6c4009
	{
Packit 6c4009
	  // XXX async OK?
Packit 6c4009
	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
Packit 6c4009
	  msync ((void *) pval,
Packit 6c4009
		 ((uintptr_t) dataset & pagesize_m1) + sizeof (*dataset)
Packit 6c4009
		 + req->key_len,
Packit 6c4009
		 MS_ASYNC);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
Packit 6c4009
			true, db, uid, he == NULL);
Packit 6c4009
Packit 6c4009
      pthread_rwlock_unlock (&db->lock);
Packit 6c4009
Packit 6c4009
      /* Mark the old entry as obsolete.  */
Packit 6c4009
      if (dh != NULL)
Packit 6c4009
	dh->usable = false;
Packit 6c4009
    }
Packit 6c4009
Packit Service f86772
 out:
Packit Service f86772
  free (tofree);
Packit 6c4009
  return timeout;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service f86772
static time_t
Packit Service f86772
addgetnetgrentX_ignore (struct database_dyn *db, int fd, request_header *req,
Packit Service f86772
			const char *key, uid_t uid, struct hashentry *he,
Packit Service f86772
			struct datahead *dh)
Packit Service f86772
{
Packit Service f86772
  struct dataset *ignore;
Packit Service f86772
  void *tofree;
Packit Service f86772
  time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh,
Packit Service f86772
				    &ignore, &tofree);
Packit Service f86772
  free (tofree);
Packit Service f86772
  return timeout;
Packit Service f86772
}
Packit Service f86772
Packit 6c4009
void
Packit 6c4009
addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
		void *key, uid_t uid)
Packit 6c4009
{
Packit Service f86772
  addgetnetgrentX_ignore (db, fd, req, key, uid, NULL, NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
time_t
Packit 6c4009
readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
Packit 6c4009
		  struct datahead *dh)
Packit 6c4009
{
Packit 6c4009
  request_header req =
Packit 6c4009
    {
Packit 6c4009
      .type = GETNETGRENT,
Packit 6c4009
      .key_len = he->len
Packit 6c4009
    };
Packit Service f86772
  return addgetnetgrentX_ignore
Packit Service f86772
    (db, -1, &req, db->data + he->key, he->owner, he, dh);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
addinnetgr (struct database_dyn *db, int fd, request_header *req,
Packit 6c4009
	    void *key, uid_t uid)
Packit 6c4009
{
Packit 6c4009
  addinnetgrX (db, fd, req, key, uid, NULL, NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
time_t
Packit 6c4009
readdinnetgr (struct database_dyn *db, struct hashentry *he,
Packit 6c4009
	      struct datahead *dh)
Packit 6c4009
{
Packit 6c4009
  request_header req =
Packit 6c4009
    {
Packit 6c4009
      .type = INNETGR,
Packit 6c4009
      .key_len = he->len
Packit 6c4009
    };
Packit 6c4009
Packit 6c4009
  return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
Packit 6c4009
}