Blame nscd/netgroupcache.c

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