2004-09-08 Ulrich Drepper <drepper@redhat.com>
+ * nscd/connections.c: Implement r/o sharing of nscd's cache with client
+ processes via shared memory.
+ * nscd/nscd-client.h: Likewise.
+ * nscd/nscd.h: Likewise.
+ * nscd/nscd_conf.c: Likewise.
+ * nscd/nscd_getgr_r.c: Likewise.
+ * nscd/nscd_getpw_r.c: Likewise.
+ * nscd/nscd_gethst_r.c: Likewise.
+ * nscd/nscd.conf: Add new config parameters.
+ * nscd/Makefile (aux): Add nscd_helper.
+ * nscd/nscd_helper.c: New file.
+ * nscd/mem.c (gc): Indicate beginning and end of the gc cycle.
+
+ * nscd/hstcache.c: Simplify a lot. We cache only the request itself,
+ no derived information.
+ * connections.c (nscd_init): Fix bug in testing size of the persistent.
+
+ * nis/Makefile (aux): Add nis_hash.
+ * nis/nis_hash.c: New file. Split out from nis_util.c.
+ * nis/nis_util.c: Move __nis_hash code in separate file.
+
+ * csu/tst-atomic.c: Improve atomic_increment_val test which would
+ not have found a ppc bug.
+
* sysdeps/s390/fpu/bits/mathinline.h: Remove unnecessary includes.
* malloc/arena.c: Remove __MALLOC_P uses.
ret = 1;
}
- if (atomic_increment_val (&mem) != 1)
+ mem = 2;
+ if (atomic_increment_val (&mem) != 3)
{
puts ("atomic_increment_val test failed");
ret = 1;
#
subdir := nis
+aux := nis_hash
+
headers := $(wildcard rpcsvc/*.[hx])
distribute := nss-nis.h nss-nisplus.h nis_intern.h Banner \
nisplus-parser.h nis_xdr.h nss
--- /dev/null
+/* Copyright (c) 1997, 1998, 1999, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <rpcsvc/nis.h>
+
+/* This is from libc/db/hash/hash_func.c, hash3 is static there */
+/*
+ * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
+ * units. On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash
+ */
+uint32_t
+__nis_hash (const void *keyarg, register size_t len)
+{
+ register const u_char *key;
+ register size_t loop;
+ register uint32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0)
+ {
+ loop = (len + 8 - 1) >> 3;
+ switch (len & (8 - 1))
+ {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return h;
+}
-/* Copyright (c) 1997, 1998, 1999 Free Software Foundation, Inc.
+/* Copyright (c) 1997, 1998, 1999, 2004 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
return fd_res;
}
-/* This is from libc/db/hash/hash_func.c, hash3 is static there */
-/*
- * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
- * units. On the first time through the loop we get the "leftover bytes"
- * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
- * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
- * this routine is heavily used enough, it's worth the ugly coding.
- *
- * OZ's original sdbm hash
- */
-uint32_t
-__nis_hash (const void *keyarg, register size_t len)
-{
- register const u_char *key;
- register size_t loop;
- register uint32_t h;
-
-#define HASHC h = *key++ + 65599 * h
- h = 0;
- key = keyarg;
- if (len > 0)
- {
- loop = (len + 8 - 1) >> 3;
- switch (len & (8 - 1))
- {
- case 0:
- do {
- HASHC;
- /* FALLTHROUGH */
- case 7:
- HASHC;
- /* FALLTHROUGH */
- case 6:
- HASHC;
- /* FALLTHROUGH */
- case 5:
- HASHC;
- /* FALLTHROUGH */
- case 4:
- HASHC;
- /* FALLTHROUGH */
- case 3:
- HASHC;
- /* FALLTHROUGH */
- case 2:
- HASHC;
- /* FALLTHROUGH */
- case 1:
- HASHC;
- } while (--loop);
- }
- }
- return (h);
-}
+/* The hash implementation is in a separate file. */
+#include "nis_hash.c"
subdir := nscd
routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
+aux := nscd_helper
include ../Makeconfig
[GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
[SHUTDOWN] = "SHUTDOWN",
[GETSTAT] = "GETSTAT",
- [INVALIDATE] = "INVALIDATE"
+ [INVALIDATE] = "INVALIDATE",
+ [GETFDPW] = "GETFDPW",
+ [GETFDGR] = "GETFDGR",
+ [GETFDHST] = "GETFDHST"
};
/* The control data structures for the services. */
.enabled = 0,
.check_file = 1,
.persistent = 0,
+ .shared = 0,
.filename = "/etc/passwd",
.db_filename = _PATH_NSCD_PASSWD_DB,
.disabled_iov = &pwd_iov_disabled,
.enabled = 0,
.check_file = 1,
.persistent = 0,
+ .shared = 0,
.filename = "/etc/group",
.db_filename = _PATH_NSCD_GROUP_DB,
.disabled_iov = &grp_iov_disabled,
.enabled = 0,
.check_file = 1,
.persistent = 0,
+ .shared = 0,
.filename = "/etc/hosts",
.db_filename = _PATH_NSCD_HOSTS_DB,
.disabled_iov = &hst_iov_disabled,
/* Mapping of request type to database. */
-static struct database_dyn *const serv2db[LASTDBREQ + 1] =
+static struct database_dyn *const serv2db[LASTREQ] =
{
[GETPWBYNAME] = &dbs[pwddb],
[GETPWBYUID] = &dbs[pwddb],
[GETHOSTBYNAME] = &dbs[hstdb],
[GETHOSTBYNAMEv6] = &dbs[hstdb],
[GETHOSTBYADDR] = &dbs[hstdb],
- [GETHOSTBYADDRv6] = &dbs[hstdb]
+ [GETHOSTBYADDRv6] = &dbs[hstdb],
+ [GETFDPW] = &dbs[pwddb],
+ [GETFDGR] = &dbs[grpdb],
+ [GETFDHST] = &dbs[hstdb],
};
/* Number of times clients had to wait. */
unsigned long int client_queued;
-/* Alignment requirement of the beginning of the data region. */
-#define ALIGN 16
-
/* Initialize database information structures. */
void
dbs[cnt].persistent = 0;
}
else if ((total = (sizeof (head)
- + roundup (head.module
- * sizeof (struct hashentry),
+ + roundup (head.module * sizeof (ref_t),
ALIGN)
+ head.data_size))
- < st.st_size)
+ > st.st_size)
{
dbg_log (_("invalid persistent database file \"%s\": %s"),
dbs[cnt].db_filename,
dbnames[cnt]);
dbs[cnt].wr_fd = fd;
+ dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
fd = -1;
/* We also need a read-only descriptor. */
dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
* dbs[cnt].head->module);
dbs[cnt].data = xmalloc (dbs[cnt].head->data_size);
dbs[cnt].head->first_free = 0;
+
+ dbs[cnt].shared = 0;
+ assert (dbs[cnt].ro_fd == -1);
}
if (dbs[cnt].check_file)
}
+#ifdef SCM_RIGHTS
+static void
+send_ro_fd (struct database_dyn *db, char *key, int fd)
+{
+ /* If we do not have an read-only file descriptor do nothing. */
+ if (db->ro_fd == -1)
+ return;
+
+ /* We need to send some data along with the descriptor. */
+ struct iovec iov[1];
+ iov[0].iov_base = key;
+ iov[0].iov_len = strlen (key) + 1;
+
+ /* Prepare the control message to transfer the descriptor. */
+ char buf[CMSG_SPACE (sizeof (int))];
+ struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+ .msg_control = buf, .msg_controllen = sizeof (buf) };
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+
+ *(int *) CMSG_DATA (cmsg) = db->ro_fd;
+
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ /* Send the control message. We repeat when we are interrupted but
+ everything else is ignored. */
+ (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, 0));
+
+ if (__builtin_expect (debug_level > 0, 0))
+ dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
+}
+#endif /* SCM_RIGHTS */
+
+
/* Handle new request. */
static void
handle_request (int fd, request_header *req, void *key, uid_t uid)
else if (__builtin_expect (debug_level, 0) > 0)
{
if (req->type == INVALIDATE)
- dbg_log ("\t%s (%s)", serv2str[req->type], (char *)key);
+ dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
else
dbg_log ("\t%s", serv2str[req->type]);
}
}
break;
+ case GETFDPW:
+ case GETFDGR:
+ case GETFDHST:
+#ifdef SCM_RIGHTS
+ send_ro_fd (serv2db[req->type], key, fd);
+#endif
+ break;
+
default:
/* Ignore the command, it's nothing we know. */
break;
int timeout = -1;
if (run_prune)
{
- now = time (NULL);
+ /* NB: we do not flush the timestamp update using msync since
+ this value doesnot matter on disk. */
+ dbs[my_number].head->timestamp = now = time (NULL);
timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
}
static void
cache_addhst (struct database_dyn *db, int fd, request_header *req,
- const void *key, struct hostent *hst, uid_t owner, int add_addr,
+ const void *key, struct hostent *hst, uid_t owner,
struct hashentry *he, struct datahead *dh, int errval)
{
ssize_t total;
the current cache handling cannot handle and it is more than
questionable whether it is worthwhile complicating the cache
handling just for handling such a special case. */
- if (he == NULL && (add_addr || hst->h_addr_list[1] == NULL))
+ if (he == NULL && hst->h_addr_list[1] == NULL)
{
dataset = (struct dataset *) mempool_alloc (db,
total + req->key_len);
itself. This is the case if the resolver is used and the name
is extended by the domainnames from /etc/resolv.conf. Therefore
we explicitly add the name here. */
- if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
- key_copy = memcpy (cp, key, req->key_len);
- else
- memset (cp, '\0', req->key_len);
+ key_copy = memcpy (cp, key, req->key_len);
/* Now we can determine whether on refill we have to create a new
record or not. */
problem is that we always must add the hash table entry
with the FIRST flag set first. Otherwise we get dangling
pointers in case memory allocation fails. */
- assert (add_addr || hst->h_addr_list[1] == NULL);
-
- /* Add the normal addresses. */
- if (add_addr)
- {
- for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
- {
- if (cache_add (addr_list_type, addresses, hst->h_length,
- &dataset->head, cnt == 0, db, owner) < 0)
- {
- /* Ensure the data can be recovered. */
- if (cnt == 0)
- dataset->head.usable = false;
- goto out;
- }
- addresses += hst->h_length;
- }
-
- /* If necessary the IPv6 addresses. */
- if (addr_list_type == GETHOSTBYADDR)
- for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
- {
- if (cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ,
- &dataset->head, false, db, owner) < 0)
- goto out;
+ assert (hst->h_addr_list[1] == NULL);
- addresses += IN6ADDRSZ;
- }
- }
/* Avoid adding names if more than one address is available. See
above for more info. */
- else
- {
- assert (req->type == GETHOSTBYNAME
- || req->type == GETHOSTBYNAMEv6
- || req->type == GETHOSTBYADDR
- || req->type == GETHOSTBYADDRv6);
-
- /* If necessary add the key for this request. */
- if (req->type == GETHOSTBYNAME)
- {
- bool first = true;
- if (addr_list_type == GETHOSTBYADDR)
- {
- if (cache_add (GETHOSTBYNAME, key_copy, req->key_len,
- &dataset->head, true, db, owner) < 0)
- {
- /* Could not allocate memory. Make sure the
- data gets discarded. */
- dataset->head.usable = false;
- goto out;
- }
-
- first = false;
- }
- if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
- &dataset->head, first, db, owner) < 0)
- {
- /* Could not allocate memory. Make sure the
- data gets discarded. */
- if (first)
- dataset->head.usable = false;
- goto out;
- }
- }
- else if (req->type == GETHOSTBYNAMEv6)
- {
- if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
- &dataset->head, true, db, owner) < 0)
- {
- /* Could not allocate memory. Make sure the
- data gets discarded. */
- dataset->head.usable = false;
- goto out;
- }
-
- if (addr_list_type == GETHOSTBYADDR
- && cache_add (GETHOSTBYNAME, key_copy, req->key_len,
- &dataset->head, false, db, owner) < 0)
- goto out;
- }
-
- /* And finally the name. We mark this as the last entry. */
- if (addr_list_type == GETHOSTBYADDR
- && req->type == GETHOSTBYADDR
- && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
- &dataset->head, true, db, owner) < 0)
- {
- /* Could not allocate memory. Make sure the
- data gets discarded. */
- dataset->head.usable = false;
- goto out;
- }
-
- if (cache_add (GETHOSTBYNAMEv6, dataset->strdata,
- h_name_len, &dataset->head,
- ((req->type == GETHOSTBYADDR
- && addr_list_type != GETHOSTBYADDR)
- || req->type == GETHOSTBYADDRv6), db,
- owner) < 0)
- {
- /* Could not allocate memory. Make sure the
- data gets discarded. */
- if ((req->type == GETHOSTBYADDR
- && addr_list_type != GETHOSTBYADDR)
- || req->type == GETHOSTBYADDRv6)
- dataset->head.usable = false;
- goto out;
- }
+ assert (req->type == GETHOSTBYNAME
+ || req->type == GETHOSTBYNAMEv6
+ || req->type == GETHOSTBYADDR
+ || req->type == GETHOSTBYADDRv6);
- if (addr_list_type == GETHOSTBYADDR
- && req->type != GETHOSTBYADDR
- && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
- &dataset->head, false, db, owner) < 0)
- goto out;
+ if (cache_add (req->type, key_copy, req->key_len,
+ &dataset->head, true, db, owner) < 0)
+ /* Could not allocate memory. Make sure the
+ data gets discarded. */
+ dataset->head.usable = false;
- /* First add all the aliases. */
- for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
- {
- if (addr_list_type == GETHOSTBYADDR)
- if (cache_add (GETHOSTBYNAME, aliases,
- h_aliases_len[cnt], &dataset->head,
- false, db, owner) < 0)
- break;
-
- if (cache_add (GETHOSTBYNAMEv6, aliases,
- h_aliases_len[cnt], &dataset->head,
- false, db, owner) < 0)
- break;
-
- aliases += h_aliases_len[cnt];
- }
- }
-
- out:
pthread_rwlock_unlock (&db->lock);
}
}
if (__builtin_expect (debug_level > 0, 0))
{
+ const char *str;
+ char buf[INET6_ADDRSTRLEN + 1];
+ if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+ str = key;
+ else
+ str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+ key, buf, sizeof (buf));
+
if (he == NULL)
- dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+ dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
else
- dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
+ dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
}
if (db->secure)
if (db->secure)
seteuid (oldeuid);
- cache_addhst (db, fd, req, key, hst, uid, 0, he, dh,
+ cache_addhst (db, fd, req, key, hst, uid, he, dh,
h_errno == TRY_AGAIN ? errval : 0);
if (use_malloc)
++next_data;
+ /* Now we start modifying the data. Make sure all readers of the
+ data are aware of this and temporarily don't use the data. */
+ ++db->head->gc_cycle;
+ assert ((db->head->gc_cycle & 1) == 1);
+
+
/* We do not perform the move operations right away since the
he_data array is not sorted by the address of the data. */
struct moveinfo
msync (db->head, db->data + db->head->first_free - (char *) db->head,
MS_ASYNC);
+
+ /* Now we are done modifying the data. */
+ ++db->head->gc_cycle;
+ assert ((db->head->gc_cycle & 1) == 0);
+
/* We are done. */
out:
pthread_mutex_unlock (&db->memlock);
#ifndef _NSCD_CLIENT_H
#define _NSCD_CLIENT_H 1
+#include <atomic.h>
#include <nscd-types.h>
/* Version number of the daemon interface */
SHUTDOWN, /* Shut the server down. */
GETSTAT, /* Get the server statistic. */
INVALIDATE, /* Invalidate one special cache. */
+ GETFDPW,
+ GETFDGR,
+ GETFDHST,
LASTREQ
} request_type;
} hst_response_header;
+/* Type for offsets in data part of database. */
+typedef uint32_t ref_t;
+/* Value for invalid/no reference. */
+#define ENDREF UINT32_MAX
+
+/* Alignment requirement of the beginning of the data region. */
+#define ALIGN 16
+
+
+/* Head of record in data part of database. */
+struct datahead
+{
+ size_t allocsize; /* Allocated Bytes. */
+ size_t recsize; /* Size of the record. */
+ time_t timeout; /* Time when this entry becomes invalid. */
+ bool notfound; /* Nonzero if data for key has not been found. */
+ uint8_t nreloads; /* Reloads without use. */
+ bool usable; /* False if the entry must be ignored. */
+
+ /* We need to have the following element aligned for the response
+ header data types and their use in the 'struct dataset' types
+ defined in the XXXcache.c files. */
+ union
+ {
+ pw_response_header pwdata;
+ gr_response_header grdata;
+ hst_response_header hstdata;
+ ssize_t align1;
+ time_t align2;
+ } data[0];
+};
+
+
+/* Structure for one hash table entry. */
+struct hashentry
+{
+ request_type type:8; /* Which type of dataset. */
+ bool first; /* True if this was the original key. */
+ size_t len; /* Length of key. */
+ ref_t key; /* Pointer to key. */
+ uid_t owner; /* If secure table, this is the owner. */
+ ref_t next; /* Next entry in this hash bucket list. */
+ ref_t packet; /* Records for the result. */
+ union
+ {
+ struct hashentry *dellist; /* Next record to be deleted. This can be a
+ pointer since only nscd uses this field. */
+ ref_t *prevp; /* Pointer to field containing forward
+ reference. */
+ };
+};
+
+
+/* Current persistent database version. */
+#define DB_VERSION 1
+
+/* Maximum time allowed between updates of the timestamp. */
+#define MAPPING_TIMEOUT (5 * 60)
+
+
+/* Header of persistent database file. */
+struct database_pers_head
+{
+ int version;
+ int header_size;
+ int gc_cycle;
+ volatile time_t timestamp;
+
+ size_t module;
+ size_t data_size;
+
+ size_t first_free; /* Offset of first free byte in data area. */
+
+ size_t nentries;
+ size_t maxnentries;
+ size_t maxnsearched;
+
+ uintmax_t poshit;
+ uintmax_t neghit;
+ uintmax_t posmiss;
+ uintmax_t negmiss;
+
+ uintmax_t rdlockdelayed;
+ uintmax_t wrlockdelayed;
+
+ uintmax_t addfailed;
+
+ ref_t array[0];
+};
+
+
+/* Mapped database record. */
+struct mapped_database
+{
+ const struct database_pers_head *head;
+ const char *data;
+ size_t mapsize;
+ int counter; /* > 0 indicates it isusable. */
+};
+#define NO_MAPPING ((struct mapped_database *) -1l)
+
+struct locked_map_ptr
+{
+ int lock;
+ struct mapped_database *mapped;
+};
+#define libc_locked_map_ptr(name) static struct locked_map_ptr name
+
+
/* Open socket connection to nscd server. */
extern int __nscd_open_socket (const char *key, size_t keylen,
request_type type, void *response,
size_t responselen) attribute_hidden;
+/* Get reference of mapping. */
+extern struct mapped_database *__nscd_get_map_ref (request_type type,
+ const char *name,
+ struct locked_map_ptr *mapptr,
+ int *gc_cyclep);
+
+/* Unmap database. */
+extern void __nscd_unmap (struct mapped_database *mapped);
+
+/* Drop reference of mapping. */
+static inline int __nscd_drop_map_ref (struct mapped_database *map,
+ int gc_cycle)
+{
+ if (map != NO_MAPPING)
+ {
+ if (__builtin_expect (map->head->gc_cycle != gc_cycle, 0))
+ /* We might have read inconsistent data. */
+ return -1;
+
+ if (atomic_decrement_val (&map->counter) == 0)
+ __nscd_unmap (map);
+ }
+
+ return 0;
+}
+
+
+/* Search the mapped database. */
+extern const struct datahead * __nscd_cache_search (request_type type,
+ const char *key,
+ size_t keylen,
+ const struct mapped_database *mapped);
+
#endif /* nscd.h */
# suggested-size <service> <prime number>
# check-files <service> <yes|no>
# persistent <service> <yes|no>
+# shared <service> <yes|no>
#
# Currently supported cache names (services): passwd, group, hosts
#
suggested-size passwd 211
check-files passwd yes
persistent passwd yes
+ shared passwd yes
enable-cache group yes
positive-time-to-live group 3600
suggested-size group 211
check-files group yes
persistent group yes
+ shared group yes
enable-cache hosts yes
positive-time-to-live hosts 3600
suggested-size hosts 211
check-files hosts yes
persistent hosts yes
+ shared hosts yes
} dbtype;
-/* Head of record in data part of database. */
-struct datahead
-{
- size_t allocsize; /* Allocated Bytes. */
- size_t recsize; /* Size of the record. */
- time_t timeout; /* Time when this entry becomes invalid. */
- bool notfound; /* Nonzero if data for key has not been found. */
- uint8_t nreloads; /* Reloads without use. */
- bool usable; /* False if the entry must be ignored. */
-
- /* We need to have the following element aligned for the response
- header data types and their use in the 'struct dataset' types
- defined in the XXXcache.c files. */
- union
- {
- pw_response_header pwdata;
- gr_response_header grdata;
- hst_response_header hstdata;
- ssize_t align1;
- time_t align2;
- } data[0];
-};
-
-
/* Default limit on the number of times a value gets reloaded without
being used in the meantime. NSCD does not throw a value out as
soon as it times out. It tries to reload the value from the
#define DEFAULT_RELOAD_LIMIT 5
-/* Type for offsets in data part of database. */
-typedef uint32_t ref_t;
-/* Value for invalid/no reference. */
-#define ENDREF UINT32_MAX
-
-
-/* Structure for one hash table entry. */
-struct hashentry
-{
- request_type type:8; /* Which type of dataset. */
- bool first; /* True if this was the original key. */
- size_t len; /* Length of key. */
- ref_t key; /* Pointer to key. */
- uid_t owner; /* If secure table, this is the owner. */
- ref_t next; /* Next entry in this hash bucket list. */
- ref_t packet; /* Records for the result. */
- union
- {
- struct hashentry *dellist; /* Next record to be deleted. This can be a
- pointer since only nscd uses this field. */
- ref_t *prevp; /* Pointer to field containing forward
- reference. */
- };
-};
-
-
-/* Current persistent database version. */
-#define DB_VERSION 1
-
-/* Header of persistent database file. */
-struct database_pers_head
-{
- int version;
- int header_size;
-
- size_t module;
- size_t data_size;
-
- size_t first_free; /* Offset of first free byte in data area. */
-
- size_t nentries;
- size_t maxnentries;
- size_t maxnsearched;
-
- uintmax_t poshit;
- uintmax_t neghit;
- uintmax_t posmiss;
- uintmax_t negmiss;
-
- uintmax_t rdlockdelayed;
- uintmax_t wrlockdelayed;
-
- uintmax_t addfailed;
-
- ref_t array[0];
-};
-
/* Structure describing dynamic part of one database. */
struct database_dyn
{
int enabled;
int check_file;
int persistent;
+ int shared;
const char *filename;
const char *db_filename;
time_t file_mtime;
if (cnt == lastdb)
dbg_log ("database %s is not supported\n", arg1);
}
+ else if (strcmp (entry, "shared") == 0)
+ {
+ for (cnt = 0; cnt < lastdb; ++cnt)
+ if (strcmp (arg1, dbnames[cnt]) == 0)
+ {
+ if (strcmp (arg2, "no") == 0)
+ dbs[cnt].shared = 0;
+ else if (strcmp (arg2, "yes") == 0)
+ dbs[cnt].shared = 1;
+ break;
+ }
+ if (cnt == lastdb)
+ dbg_log ("database %s is not supported\n", arg1);
+ }
else if (strcmp (entry, "reload-count") == 0)
{
if (strcasecmp (arg1, "unlimited") == 0)
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
}
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary. The memory
+ mapping is not removed since it is not visible to the malloc
+ handling. */
+libc_freeres_fn (gr_map_free)
+{
+
+ if (map_handle.mapped != NO_MAPPING)
+ free (map_handle.mapped);
+}
+
+
static int
internal_function
nscd_getgr_r (const char *key, size_t keylen, request_type type,
struct group *resultbuf, char *buffer, size_t buflen,
struct group **result)
{
- gr_response_header gr_resp;
- int sock = __nscd_open_socket (key, keylen, type, &gr_resp,
- sizeof (gr_resp));
- if (sock == -1)
+ const gr_response_header *gr_resp = NULL;
+ const uint32_t *len = NULL;
+ const char *gr_name = NULL;
+ size_t gr_name_len = 0;
+ int retval = -1;
+ int gc_cycle;
+ const char *recend = (const char *) ~UINTMAX_C (0);
+
+ /* If the mapping is available, try to search there instead of
+ communicating with the nscd. */
+ struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
+ &map_handle, &gc_cycle);
+ retry:
+ if (mapped != NO_MAPPING)
{
- __nss_not_use_nscd_group = 1;
- return -1;
+ const struct datahead *found = __nscd_cache_search (type, key, keylen,
+ mapped);
+ if (found != NULL)
+ {
+ gr_resp = &found->data[0].grdata;
+ len = (const uint32_t *) (gr_resp + 1);
+ /* The alignment is always sufficient. */
+ assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
+ gr_name = ((const char *) len
+ + gr_resp->gr_mem_cnt * sizeof (uint32_t));
+ gr_name_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
+ recend = (const char *) found->data + found->recsize;
+ }
+ }
+
+ gr_response_header gr_resp_mem;
+ int sock = -1;
+ if (gr_resp == NULL)
+ {
+ sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
+ sizeof (gr_resp_mem));
+ if (sock == -1)
+ {
+ __nss_not_use_nscd_group = 1;
+ goto out;
+ }
+
+ gr_resp = &gr_resp_mem;
}
/* No value found so far. */
- int retval = -1;
*result = NULL;
- if (gr_resp.found == -1)
+ if (__builtin_expect (gr_resp->found == -1, 0))
{
/* The daemon does not cache this database. */
__nss_not_use_nscd_group = 1;
- goto out;
+ goto out_close;
}
- if (gr_resp.found == 1)
+ if (gr_resp->found == 1)
{
struct iovec vec[2];
- uint32_t *len;
char *p = buffer;
size_t total_len;
uintptr_t align;
align the pointer. */
align = ((__alignof__ (char *) - (p - ((char *) 0)))
& (__alignof__ (char *) - 1));
- total_len = align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
- + gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+ total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *)
+ + gr_resp->gr_name_len + gr_resp->gr_passwd_len);
if (__builtin_expect (buflen < total_len, 0))
{
no_room:
__set_errno (ERANGE);
retval = ERANGE;
- goto out;
+ goto out_close;
}
buflen -= total_len;
p += align;
resultbuf->gr_mem = (char **) p;
- p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
+ p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *);
/* Set pointers for strings. */
resultbuf->gr_name = p;
- p += gr_resp.gr_name_len;
+ p += gr_resp->gr_name_len;
resultbuf->gr_passwd = p;
- p += gr_resp.gr_passwd_len;
+ p += gr_resp->gr_passwd_len;
/* Fill in what we know now. */
- resultbuf->gr_gid = gr_resp.gr_gid;
+ resultbuf->gr_gid = gr_resp->gr_gid;
- /* Allocate array to store lengths. */
- len = (uint32_t *) alloca (gr_resp.gr_mem_cnt * sizeof (uint32_t));
+ /* Read the length information, group name, and password. */
+ if (len == NULL)
+ {
+ /* Allocate array to store lengths. */
+ len = (uint32_t *) alloca (gr_resp->gr_mem_cnt * sizeof (uint32_t));
- total_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
- vec[0].iov_base = len;
- vec[0].iov_len = total_len;
- vec[1].iov_base = resultbuf->gr_name;
- vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
- total_len += gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+ vec[0].iov_base = (void *) len;
+ vec[0].iov_len = gr_resp->gr_mem_cnt * sizeof (uint32_t);
+ vec[1].iov_base = resultbuf->gr_name;
+ vec[1].iov_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
+ total_len = vec[0].iov_len + vec[1].iov_len;
- /* Get this data. */
- size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
- if (__builtin_expect (n != total_len, 0))
- goto out;
+ /* Get this data. */
+ size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
+ if (__builtin_expect (n != total_len, 0))
+ goto out_close;
+ }
+ else
+ /* We already have the data. Just copy the group name and
+ password. */
+ memcpy (resultbuf->gr_name, gr_name, gr_name_len);
/* Clear the terminating entry. */
- resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
+ resultbuf->gr_mem[gr_resp->gr_mem_cnt] = NULL;
/* Prepare reading the group members. */
total_len = 0;
- for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
+ for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
{
resultbuf->gr_mem[cnt] = p;
total_len += len[cnt];
p += len[cnt];
}
+ if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
+ goto out_close;
if (__builtin_expect (total_len > buflen, 0))
goto no_room;
retval = 0;
- n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
- total_len));
- if (__builtin_expect (n != total_len, 0))
+ if (gr_name == NULL)
{
- /* The `errno' to some value != ERANGE. */
- __set_errno (ENOENT);
- retval = ENOENT;
+ size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
+ total_len));
+ if (__builtin_expect (n != total_len, 0))
+ {
+ /* The `errno' to some value != ERANGE. */
+ __set_errno (ENOENT);
+ retval = ENOENT;
+ }
+ else
+ *result = resultbuf;
}
else
- *result = resultbuf;
+ {
+ /* Copy the group member names. */
+ memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
+
+ *result = resultbuf;
+ }
}
else
{
retval = 0;
}
+ out_close:
+ if (sock != -1)
+ close_not_cancel_no_status (sock);
out:
- close_not_cancel_no_status (sock);
+ if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
+ /* When we come here this means there has been a GC cycle while we
+ were looking for the data. This means the data might have been
+ inconsistent. Retry. */
+ goto retry;
return retval;
}
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <string.h>
#include <unistd.h>
#include <arpa/nameser.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/un.h>
+#include <sys/mman.h>
#include <not-cancel.h>
#include "nscd-client.h"
}
-/* Create a socket connected to a name. */
-int
-__nscd_open_socket (const char *key, size_t keylen, request_type type,
- void *response, size_t responselen)
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary. The memory
+ mapping is not removed since it is not visible to the malloc
+ handling. */
+libc_freeres_fn (gr_map_free)
{
- struct sockaddr_un addr;
- int sock;
- int saved_errno = errno;
-
- sock = __socket (PF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- __set_errno (saved_errno);
- return -1;
- }
-
- addr.sun_family = AF_UNIX;
- strcpy (addr.sun_path, _PATH_NSCDSOCKET);
- if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
- {
- __set_errno (saved_errno);
- goto out;
- }
-
- request_header req;
- req.version = NSCD_VERSION;
- req.type = type;
- req.key_len = keylen;
-
- struct iovec vec[2];
- vec[0].iov_base = &req;
- vec[0].iov_len = sizeof (request_header);
- vec[1].iov_base = (void *) key;
- vec[1].iov_len = keylen;
- ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
- if (nbytes != (ssize_t) (sizeof (request_header) + keylen))
- {
- out:
- close_not_cancel_no_status (sock);
- sock = -1;
- }
- else
- {
- nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
- if (nbytes != (ssize_t) responselen)
- goto out;
- }
-
- return sock;
+ if (map_handle.mapped != NO_MAPPING)
+ free (map_handle.mapped);
}
struct hostent *resultbuf, char *buffer, size_t buflen,
struct hostent **result, int *h_errnop)
{
- hst_response_header hst_resp;
- int sock = __nscd_open_socket (key, keylen, type, &hst_resp,
- sizeof (hst_resp));
- if (sock == -1)
+ const hst_response_header *hst_resp = NULL;
+ const char *h_name = NULL;
+ const uint32_t *aliases_len = NULL;
+ const char *addr_list = NULL;
+ size_t addr_list_len = 0;
+ int retval = -1;
+ int gc_cycle;
+ const char *recend = (const char *) ~UINTMAX_C (0);
+ int sock = -1;
+
+ /* If the mapping is available, try to search there instead of
+ communicating with the nscd. */
+ struct mapped_database *mapped = __nscd_get_map_ref (GETFDHST, "hosts",
+ &map_handle, &gc_cycle);
+ retry:
+ if (mapped != MAP_FAILED)
{
- __nss_not_use_nscd_hosts = 1;
- return -1;
+ const struct datahead *found = __nscd_cache_search (type, key, keylen,
+ mapped);
+ if (found != NULL)
+ {
+ hst_resp = &found->data[0].hstdata;
+ h_name = (char *) (hst_resp + 1);
+ aliases_len = (uint32_t *) (h_name + hst_resp->h_name_len);
+ addr_list = ((char *) aliases_len
+ + hst_resp->h_aliases_cnt * sizeof (uint32_t));
+ addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+
+#ifndef _STRING_ARCH_unaligned
+ /* The aliases_len array in the mapped database might very
+ well be unaligned. We will access it word-wise so on
+ platforms which do not tolerate unaligned accesses we
+ need to make an aligned copy. */
+ if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
+ != 0)
+ {
+ uint32_t *tmp = alloca (hst_resp->h_aliases_cnt
+ * sizeof (uint32_t));
+ aliases_len = memcpy (tmp, aliases_len,
+ hst_resp->h_aliases_cnt
+ * sizeof (uint32_t));
+ }
+#endif
+ if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
+ {
+ if (hst_resp->h_length == INADDRSZ)
+ addr_list += addr_list_len;
+ addr_list_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+ }
+ recend = (const char *) found->data + found->recsize;
+ if (__builtin_expect ((const char *) addr_list + addr_list_len
+ > recend, 0))
+ goto out_close;
+ }
+ }
+
+ hst_response_header hst_resp_mem;
+ if (hst_resp == NULL)
+ {
+ sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
+ sizeof (hst_resp_mem));
+ if (sock == -1)
+ {
+ __nss_not_use_nscd_hosts = 1;
+ goto out;;
+ }
+
+ hst_resp = &hst_resp_mem;
}
/* No value found so far. */
- int retval = -1;
*result = NULL;
- if (hst_resp.found == -1)
+ if (__builtin_expect (hst_resp->found == -1, 0))
{
/* The daemon does not cache this database. */
__nss_not_use_nscd_hosts = 1;
- goto out;
+ goto out_close;
}
- if (hst_resp.found == 1)
+ if (hst_resp->found == 1)
{
struct iovec vec[4];
- uint32_t *aliases_len;
char *cp = buffer;
uintptr_t align1;
uintptr_t align2;
align the pointer and the base of the h_addr_list pointers. */
align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
& (__alignof__ (char *) - 1));
- align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
+ align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp->h_name_len)
- ((char *) 0)))
& (__alignof__ (char *) - 1));
- if (buflen < (align1 + hst_resp.h_name_len + align2
- + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt + 2)
+ if (buflen < (align1 + hst_resp->h_name_len + align2
+ + ((hst_resp->h_aliases_cnt + hst_resp->h_addr_list_cnt
+ + 2)
* sizeof (char *))
- + hst_resp.h_addr_list_cnt * (type == AF_INET
- ? INADDRSZ : IN6ADDRSZ)))
+ + hst_resp->h_addr_list_cnt * (type == AF_INET
+ ? INADDRSZ : IN6ADDRSZ)))
{
no_room:
__set_errno (ERANGE);
retval = ERANGE;
- goto out;
+ goto out_close;
}
cp += align1;
/* Prepare the result as far as we can. */
resultbuf->h_aliases = (char **) cp;
- cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
+ cp += (hst_resp->h_aliases_cnt + 1) * sizeof (char *);
resultbuf->h_addr_list = (char **) cp;
- cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
+ cp += (hst_resp->h_addr_list_cnt + 1) * sizeof (char *);
resultbuf->h_name = cp;
- cp += hst_resp.h_name_len + align2;
- vec[0].iov_base = resultbuf->h_name;
- vec[0].iov_len = hst_resp.h_name_len;
-
- aliases_len = alloca (hst_resp.h_aliases_cnt * sizeof (uint32_t));
- vec[1].iov_base = aliases_len;
- vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
+ cp += hst_resp->h_name_len + align2;
- total_len = (hst_resp.h_name_len
- + hst_resp.h_aliases_cnt * sizeof (uint32_t));
-
- n = 2;
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
- vec[2].iov_base = cp;
- vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
-
- for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
- {
- resultbuf->h_addr_list[cnt] = cp;
- cp += INADDRSZ;
- }
-
resultbuf->h_addrtype = AF_INET;
resultbuf->h_length = INADDRSZ;
-
- total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
-
- n = 3;
}
else
{
- if (hst_resp.h_length == INADDRSZ)
- {
- ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
- vec[2].iov_base = ignore;
- vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+ resultbuf->h_addrtype = AF_INET6;
+ resultbuf->h_length = IN6ADDRSZ;
+ }
+ for (cnt = 0; cnt < hst_resp->h_addr_list_cnt; ++cnt)
+ {
+ resultbuf->h_addr_list[cnt] = cp;
+ cp += resultbuf->h_length;
+ }
+ resultbuf->h_addr_list[cnt] = NULL;
- total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+ if (h_name == NULL)
+ {
+ vec[0].iov_base = resultbuf->h_name;
+ vec[0].iov_len = hst_resp->h_name_len;
+ total_len = hst_resp->h_name_len;
+ n = 1;
- n = 3;
- }
+ if (hst_resp->h_aliases_cnt > 0)
+ {
+ aliases_len = alloca (hst_resp->h_aliases_cnt
+ * sizeof (uint32_t));
+ vec[n].iov_base = (void *) aliases_len;
+ vec[n].iov_len = hst_resp->h_aliases_cnt * sizeof (uint32_t);
- vec[n].iov_base = cp;
- vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+ total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
+ ++n;
+ }
- for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
+ if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
- resultbuf->h_addr_list[cnt] = cp;
- cp += IN6ADDRSZ;
+ vec[n].iov_base = resultbuf->h_addr_list[0];
+ vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+
+ total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
+
+ ++n;
}
+ else
+ {
+ if (hst_resp->h_length == INADDRSZ)
+ {
+ ignore = alloca (hst_resp->h_addr_list_cnt * INADDRSZ);
+ vec[n].iov_base = ignore;
+ vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
- resultbuf->h_addrtype = AF_INET6;
- resultbuf->h_length = IN6ADDRSZ;
+ total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
- total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+ ++n;
+ }
- ++n;
- }
- resultbuf->h_addr_list[cnt] = NULL;
+ vec[n].iov_base = resultbuf->h_addr_list[0];
+ vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
- if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len)
- goto out;
+ total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+
+ ++n;
+ }
+
+ if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n))
+ != total_len)
+ goto out_close;
+ }
+ else
+ {
+ memcpy (resultbuf->h_name, h_name, hst_resp->h_name_len);
+ memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
+ }
/* Now we also can read the aliases. */
total_len = 0;
- for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
+ for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
{
resultbuf->h_aliases[cnt] = cp;
cp += aliases_len[cnt];
}
resultbuf->h_aliases[cnt] = NULL;
+ if (__builtin_expect ((const char *) addr_list + addr_list_len
+ + total_len > recend, 0))
+ goto out_close;
/* See whether this would exceed the buffer capacity. */
- if (cp > buffer + buflen)
+ if (__builtin_expect (cp > buffer + buflen, 0))
goto no_room;
/* And finally read the aliases. */
- if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf->h_aliases[0],
- total_len)) == total_len)
+ if (addr_list == NULL)
+ {
+ if ((size_t) TEMP_FAILURE_RETRY (__read (sock,
+ resultbuf->h_aliases[0],
+ total_len)) == total_len)
+ {
+ retval = 0;
+ *result = resultbuf;
+ }
+ }
+ else
{
+ memcpy (resultbuf->h_aliases[0],
+ (const char *) addr_list + addr_list_len, total_len);
+
retval = 0;
*result = resultbuf;
}
else
{
/* Store the error number. */
- *h_errnop = hst_resp.error;
+ *h_errnop = hst_resp->error;
/* The `errno' to some value != ERANGE. */
__set_errno (ENOENT);
retval = 0;
}
+ out_close:
+ if (sock != -1)
+ close_not_cancel_no_status (sock);
out:
- __close (sock);
+ if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
+ /* When we come here this means there has been a GC cycle while we
+ were looking for the data. This means the data might have been
+ inconsistent. Retry. */
+ goto retry;
return retval;
}
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <assert.h>
#include <errno.h>
#include <pwd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
}
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary. The memory
+ mapping is not removed since it is not visible to the malloc
+ handling. */
+libc_freeres_fn (gr_map_free)
+{
+
+ if (map_handle.mapped != NO_MAPPING)
+ free (map_handle.mapped);
+}
+
+
static int
internal_function
nscd_getpw_r (const char *key, size_t keylen, request_type type,
struct passwd *resultbuf, char *buffer, size_t buflen,
struct passwd **result)
{
- pw_response_header pw_resp;
- int sock = __nscd_open_socket (key, keylen, type, &pw_resp,
- sizeof (pw_resp));
- if (sock == -1)
+ const pw_response_header *pw_resp = NULL;
+ const char *pw_name = NULL;
+ int retval = -1;
+ int gc_cycle;
+ const char *recend = (const char *) ~UINTMAX_C (0);
+
+ /* If the mapping is available, try to search there instead of
+ communicating with the nscd. */
+ struct mapped_database *mapped = __nscd_get_map_ref (GETFDPW, "passwd",
+ &map_handle, &gc_cycle);
+ retry:
+ if (mapped != NO_MAPPING)
{
- __nss_not_use_nscd_passwd = 1;
- return -1;
+ const struct datahead *found = __nscd_cache_search (type, key, keylen,
+ mapped);
+ if (found != NULL)
+ {
+ pw_resp = &found->data[0].pwdata;
+ pw_name = (const char *) (pw_resp + 1);
+ recend = (const char *) found->data + found->recsize;
+ }
+ }
+
+ pw_response_header pw_resp_mem;
+ int sock = -1;
+ if (pw_resp == NULL)
+ {
+ sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
+ sizeof (pw_resp_mem));
+ if (sock == -1)
+ {
+ __nss_not_use_nscd_passwd = 1;
+ goto out;
+ }
+
+ pw_resp = &pw_resp_mem;
}
/* No value found so far. */
- int retval = -1;
*result = NULL;
- if (__builtin_expect (pw_resp.found == -1, 0))
+ if (__builtin_expect (pw_resp->found == -1, 0))
{
/* The daemon does not cache this database. */
__nss_not_use_nscd_passwd = 1;
- goto out;
+ goto out_close;
}
- if (pw_resp.found == 1)
+ if (pw_resp->found == 1)
{
- char *p = buffer;
- size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len
- + pw_resp.pw_gecos_len + pw_resp.pw_dir_len
- + pw_resp.pw_shell_len);
-
- if (__builtin_expect (buflen < total, 0))
- {
- __set_errno (ERANGE);
- retval = ERANGE;
- goto out;
- }
-
/* Set the information we already have. */
- resultbuf->pw_uid = pw_resp.pw_uid;
- resultbuf->pw_gid = pw_resp.pw_gid;
+ resultbuf->pw_uid = pw_resp->pw_uid;
+ resultbuf->pw_gid = pw_resp->pw_gid;
+ char *p = buffer;
/* get pw_name */
resultbuf->pw_name = p;
- p += pw_resp.pw_name_len;
+ p += pw_resp->pw_name_len;
/* get pw_passwd */
resultbuf->pw_passwd = p;
- p += pw_resp.pw_passwd_len;
+ p += pw_resp->pw_passwd_len;
/* get pw_gecos */
resultbuf->pw_gecos = p;
- p += pw_resp.pw_gecos_len;
+ p += pw_resp->pw_gecos_len;
/* get pw_dir */
resultbuf->pw_dir = p;
- p += pw_resp.pw_dir_len;
+ p += pw_resp->pw_dir_len;
/* get pw_pshell */
resultbuf->pw_shell = p;
+ p += pw_resp->pw_shell_len;
- ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+ ssize_t total = p - buffer;
+ if (__builtin_expect (pw_name + total > recend, 0))
+ goto out_close;
+ if (__builtin_expect (buflen < total, 0))
+ {
+ __set_errno (ERANGE);
+ retval = ERANGE;
+ goto out_close;
+ }
- if (nbytes == (ssize_t) total)
+ retval = 0;
+ if (pw_name == NULL)
+ {
+ ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+
+ if (__builtin_expect (nbytes != total, 0))
+ {
+ /* The `errno' to some value != ERANGE. */
+ __set_errno (ENOENT);
+ retval = ENOENT;
+ }
+ else
+ *result = resultbuf;
+ }
+ else
{
- retval = 0;
+ /* Copy the various strings. */
+ memcpy (resultbuf->pw_name, pw_name, total);
+
*result = resultbuf;
}
}
retval = 0;
}
+ out_close:
+ if (sock != -1)
+ close_not_cancel_no_status (sock);
out:
- close_not_cancel_no_status (sock);
+ if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
+ /* When we come here this means there has been a GC cycle while we
+ were looking for the data. This means the data might have been
+ inconsistent. Retry. */
+ goto retry;
return retval;
}
--- /dev/null
+/* Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <nis/rpcsvc/nis.h>
+
+#include "nscd-client.h"
+
+
+static int
+open_socket (void)
+{
+ int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -1;
+
+ /* Make socket non-blocking. */
+ int fl = __fcntl (sock, F_GETFL);
+ if (fl != -1)
+ __fcntl (sock, F_SETFL, fl | O_NONBLOCK);
+
+ struct sockaddr_un sun;
+ sun.sun_family = AF_UNIX;
+ strcpy (sun.sun_path, _PATH_NSCDSOCKET);
+ if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
+ && errno != EINPROGRESS)
+ goto out;
+
+ struct pollfd fds[1];
+ fds[0].fd = sock;
+ fds[0].events = POLLOUT | POLLERR | POLLHUP;
+ if (__poll (fds, 1, 5 * 1000) > 0)
+ /* Success. We do not check for success of the connect call here.
+ If it failed, the following operations will fail. */
+ return sock;
+
+ out:
+ close_not_cancel_no_status (sock);
+
+ return -1;
+}
+
+
+void
+__nscd_unmap (struct mapped_database *mapped)
+{
+ assert (mapped->counter == 0);
+ munmap ((void *) mapped->head, mapped->mapsize);
+ free (mapped);
+}
+
+
+/* Try to get a file descriptor for the shared meory segment
+ containing the database. */
+static struct mapped_database *
+get_mapping (request_type type, const char *key,
+ struct mapped_database **mappedp)
+{
+ struct mapped_database *result = NO_MAPPING;
+#ifdef SCM_RIGHTS
+ const size_t keylen = strlen (key) + 1;
+ char resdata[keylen];
+ int saved_errno = errno;
+
+ int mapfd = -1;
+
+ /* Send the request. */
+ struct iovec iov[2];
+ request_header req;
+
+ int sock = open_socket ();
+ if (sock < 0)
+ goto out;
+
+ req.version = NSCD_VERSION;
+ req.type = type;
+ req.key_len = keylen;
+
+ iov[0].iov_base = &req;
+ iov[0].iov_len = sizeof (req);
+ iov[1].iov_base = (void *) key;
+ iov[1].iov_len = keylen;
+
+ if (TEMP_FAILURE_RETRY (__writev (sock, iov, 2))
+ != iov[0].iov_len + iov[1].iov_len)
+ /* We cannot even write the request. */
+ goto out_close2;
+
+ /* Room for the data sent along with the file descriptor. We expect
+ the key name back. */
+ iov[0].iov_base = resdata;
+ iov[0].iov_len = keylen;
+
+ char buf[CMSG_SPACE (sizeof (int))];
+ struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+ .msg_control = buf, .msg_controllen = sizeof (buf) };
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+
+ *(int *) CMSG_DATA (cmsg) = -1;
+
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ struct pollfd fds[1];
+ fds[0].fd = sock;
+ fds[0].events = POLLIN | POLLERR | POLLHUP;
+ if (__poll (fds, 1, 5 * 1000) <= 0)
+ /* Failure or timeout. */
+ goto out_close2;
+
+ if (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0)) != keylen
+ || msg.msg_controllen != CMSG_LEN (sizeof (int)))
+ goto out_close2;
+
+ mapfd = *(int *) CMSG_DATA (cmsg);
+
+ struct stat64 st;
+ if (strcmp (resdata, key) != 0
+ || fstat64 (mapfd, &st) != 0
+ || st.st_size < sizeof (struct database_pers_head))
+ goto out_close;
+
+ struct database_pers_head head;
+ if (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0))
+ != sizeof (head))
+ goto out_close;
+
+ if (head.version != DB_VERSION || head.header_size != sizeof (head)
+ /* This really should not happen but who knows, maybe the update
+ thread got stuck. */
+ || head.timestamp + MAPPING_TIMEOUT < time (NULL))
+ goto out_close;
+
+ size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
+ + head.data_size);
+
+ if (st.st_size < size)
+ goto out_close;
+
+ /* The file is large enough, map it now. */
+ void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
+ if (mapping != MAP_FAILED)
+ {
+ /* Allocate a record for the mapping. */
+ struct mapped_database *newp;
+
+ newp = malloc (sizeof (*newp));
+ if (newp == NULL)
+ {
+ /* Ugh, after all we went through the memory allocation failed. */
+ munmap (result, size);
+ goto out_close;
+ }
+
+ newp->head = mapping;
+ newp->data = ((char *) mapping + head.header_size
+ + roundup (head.module * sizeof (ref_t), ALIGN));
+ newp->mapsize = size;
+ /* Set counter to 1 to show it is usable. */
+ newp->counter = 1;
+
+ result = newp;
+ }
+
+ out_close:
+ __close (mapfd);
+ out_close2:
+ __close (sock);
+ out:
+ __set_errno (saved_errno);
+#endif /* SCM_RIGHTS */
+
+ struct mapped_database *oldval = *mappedp;
+ *mappedp = result;
+
+ if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
+ __nscd_unmap (oldval);
+
+ return result;
+}
+
+
+struct mapped_database *
+__nscd_get_map_ref (request_type type, const char *name,
+ struct locked_map_ptr *mapptr, int *gc_cyclep)
+{
+ struct mapped_database *cur = mapptr->mapped;
+ if (cur == NO_MAPPING)
+ return cur;
+
+ int cnt = 0;
+ while (atomic_compare_and_exchange_val_acq (&mapptr->lock, 1, 0) != 0)
+ {
+ // XXX Best number of rounds?
+ if (++cnt > 5)
+ return NO_MAPPING;
+
+ atomic_delay ();
+ }
+
+ cur = mapptr->mapped;
+
+ if (__builtin_expect (cur != NO_MAPPING, 1))
+ {
+ /* If not mapped or timestamp not updated, request new map. */
+ if (cur == NULL
+ // XXX The following syscalls increases the cost of the entire
+ // XXX lookup by a factor of 5 but unfortunately there is not
+ // XXX much we can do except hoping we get a userlevel
+ // XXX implementation soon.
+ || cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
+ cur = get_mapping (type, name, &mapptr->mapped);
+
+ if (__builtin_expect (cur != NO_MAPPING, 1))
+ {
+ if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
+ 0))
+ cur = NO_MAPPING;
+ else
+ atomic_increment (&cur->counter);
+ }
+ }
+
+ mapptr->lock = 0;
+
+ return cur;
+}
+
+
+const struct datahead *
+__nscd_cache_search (request_type type, const char *key, size_t keylen,
+ const struct mapped_database *mapped)
+{
+ unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
+
+ ref_t work = mapped->head->array[hash];
+ while (work != ENDREF)
+ {
+ struct hashentry *here = (struct hashentry *) (mapped->data + work);
+
+ if (type == here->type && keylen == here->len
+ && memcmp (key, mapped->data + here->key, keylen) == 0)
+ {
+ /* We found the entry. Increment the appropriate counter. */
+ const struct datahead *dh
+ = (struct datahead *) (mapped->data + here->packet);
+
+ /* See whether we must ignore the entry or whether something
+ is wrong because garbage collection is in progress. */
+ if (dh->usable && ((char *) dh + dh->allocsize
+ <= (char *) mapped->head + mapped->mapsize))
+ return dh;
+ }
+
+ work = here->next;
+ }
+
+ return NULL;
+}
+
+
+/* Create a socket connected to a name. */
+int
+__nscd_open_socket (const char *key, size_t keylen, request_type type,
+ void *response, size_t responselen)
+{
+ int saved_errno = errno;
+
+ int sock = open_socket ();
+ if (sock >= 0)
+ {
+ request_header req;
+ req.version = NSCD_VERSION;
+ req.type = type;
+ req.key_len = keylen;
+
+ struct iovec vec[2];
+ vec[0].iov_base = &req;
+ vec[0].iov_len = sizeof (request_header);
+ vec[1].iov_base = (void *) key;
+ vec[1].iov_len = keylen;
+
+ ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
+ if (nbytes == (ssize_t) (sizeof (request_header) + keylen))
+ {
+ /* Wait for data. */
+ struct pollfd fds[1];
+ fds[0].fd = sock;
+ fds[0].events = POLLIN | POLLERR | POLLHUP;
+ if (__poll (fds, 1, 5 * 1000) > 0)
+ {
+ nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
+ responselen));
+ if (nbytes == (ssize_t) responselen)
+ return sock;
+ }
+ }
+
+ close_not_cancel_no_status (sock);
+ }
+
+ __set_errno (saved_errno);
+
+ return -1;
+}