diff options
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | csu/tst-atomic.c | 3 | ||||
-rw-r--r-- | nis/Makefile | 2 | ||||
-rw-r--r-- | nis/nis_hash.c | 76 | ||||
-rw-r--r-- | nis/nis_util.c | 58 | ||||
-rw-r--r-- | nscd/Makefile | 1 | ||||
-rw-r--r-- | nscd/connections.c | 78 | ||||
-rw-r--r-- | nscd/hstcache.c | 163 | ||||
-rw-r--r-- | nscd/mem.c | 11 | ||||
-rw-r--r-- | nscd/nscd-client.h | 146 | ||||
-rw-r--r-- | nscd/nscd.conf | 4 | ||||
-rw-r--r-- | nscd/nscd.h | 82 | ||||
-rw-r--r-- | nscd/nscd_conf.c | 14 | ||||
-rw-r--r-- | nscd/nscd_getgr_r.c | 154 | ||||
-rw-r--r-- | nscd/nscd_gethst_r.c | 295 | ||||
-rw-r--r-- | nscd/nscd_getpw_r.c | 127 | ||||
-rw-r--r-- | nscd/nscd_helper.c | 335 |
17 files changed, 1093 insertions, 480 deletions
@@ -1,5 +1,29 @@ 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. diff --git a/csu/tst-atomic.c b/csu/tst-atomic.c index cb6b6ba3d4..7a2e3d0865 100644 --- a/csu/tst-atomic.c +++ b/csu/tst-atomic.c @@ -130,7 +130,8 @@ do_test (void) ret = 1; } - if (atomic_increment_val (&mem) != 1) + mem = 2; + if (atomic_increment_val (&mem) != 3) { puts ("atomic_increment_val test failed"); ret = 1; diff --git a/nis/Makefile b/nis/Makefile index 9f429ffc65..fa5c4dfd30 100644 --- a/nis/Makefile +++ b/nis/Makefile @@ -21,6 +21,8 @@ # 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 diff --git a/nis/nis_hash.c b/nis/nis_hash.c new file mode 100644 index 0000000000..ddd91515b0 --- /dev/null +++ b/nis/nis_hash.c @@ -0,0 +1,76 @@ +/* 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; +} diff --git a/nis/nis_util.c b/nis/nis_util.c index 99313bebfd..1220987d9c 100644 --- a/nis/nis_util.c +++ b/nis/nis_util.c @@ -1,4 +1,4 @@ -/* 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. @@ -47,58 +47,6 @@ __nis_finddirectory (directory_obj *dir, const_nis_name name) 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" diff --git a/nscd/Makefile b/nscd/Makefile index cb82a2e06f..5b3529267a 100644 --- a/nscd/Makefile +++ b/nscd/Makefile @@ -22,6 +22,7 @@ subdir := nscd routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r +aux := nscd_helper include ../Makeconfig diff --git a/nscd/connections.c b/nscd/connections.c index 53e258b498..552d554295 100644 --- a/nscd/connections.c +++ b/nscd/connections.c @@ -80,7 +80,10 @@ const char *serv2str[LASTREQ] = [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6", [SHUTDOWN] = "SHUTDOWN", [GETSTAT] = "GETSTAT", - [INVALIDATE] = "INVALIDATE" + [INVALIDATE] = "INVALIDATE", + [GETFDPW] = "GETFDPW", + [GETFDGR] = "GETFDGR", + [GETFDHST] = "GETFDHST" }; /* The control data structures for the services. */ @@ -91,6 +94,7 @@ struct database_dyn dbs[lastdb] = .enabled = 0, .check_file = 1, .persistent = 0, + .shared = 0, .filename = "/etc/passwd", .db_filename = _PATH_NSCD_PASSWD_DB, .disabled_iov = &pwd_iov_disabled, @@ -105,6 +109,7 @@ struct database_dyn dbs[lastdb] = .enabled = 0, .check_file = 1, .persistent = 0, + .shared = 0, .filename = "/etc/group", .db_filename = _PATH_NSCD_GROUP_DB, .disabled_iov = &grp_iov_disabled, @@ -119,6 +124,7 @@ struct database_dyn dbs[lastdb] = .enabled = 0, .check_file = 1, .persistent = 0, + .shared = 0, .filename = "/etc/hosts", .db_filename = _PATH_NSCD_HOSTS_DB, .disabled_iov = &hst_iov_disabled, @@ -132,7 +138,7 @@ struct database_dyn dbs[lastdb] = /* 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], @@ -141,7 +147,10 @@ static struct database_dyn *const serv2db[LASTDBREQ + 1] = [GETHOSTBYNAME] = &dbs[hstdb], [GETHOSTBYNAMEv6] = &dbs[hstdb], [GETHOSTBYADDR] = &dbs[hstdb], - [GETHOSTBYADDRv6] = &dbs[hstdb] + [GETHOSTBYADDRv6] = &dbs[hstdb], + [GETFDPW] = &dbs[pwddb], + [GETFDGR] = &dbs[grpdb], + [GETFDHST] = &dbs[hstdb], }; @@ -158,9 +167,6 @@ static int sock; /* 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 @@ -224,11 +230,10 @@ nscd_init (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, @@ -253,6 +258,7 @@ nscd_init (void) 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); @@ -439,6 +445,9 @@ cannot create read-only descriptor for \"%s\"; no mmap"), * 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) @@ -529,6 +538,43 @@ invalidate_cache (char *key) } +#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) @@ -614,7 +660,7 @@ cannot handle old request version %d; current version is %d"), 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]); } @@ -697,6 +743,14 @@ cannot handle old request version %d; current version is %d"), } 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; @@ -733,7 +787,9 @@ nscd_run (void *p) 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; } diff --git a/nscd/hstcache.c b/nscd/hstcache.c index 5a536b3346..94568d0d2b 100644 --- a/nscd/hstcache.c +++ b/nscd/hstcache.c @@ -77,7 +77,7 @@ static const hst_response_header notfound = 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; @@ -208,7 +208,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req, 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); @@ -269,10 +269,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req, 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. */ @@ -349,141 +346,21 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req, 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); } } @@ -534,10 +411,18 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req, 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) @@ -583,7 +468,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req, 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) diff --git a/nscd/mem.c b/nscd/mem.c index a4e30535c8..5207efc2de 100644 --- a/nscd/mem.c +++ b/nscd/mem.c @@ -230,6 +230,12 @@ gc (struct database_dyn *db) ++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 @@ -445,6 +451,11 @@ gc (struct database_dyn *db) 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); diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h index 470bc6986e..708f62d6aa 100644 --- a/nscd/nscd-client.h +++ b/nscd/nscd-client.h @@ -23,6 +23,7 @@ #ifndef _NSCD_CLIENT_H #define _NSCD_CLIENT_H 1 +#include <atomic.h> #include <nscd-types.h> /* Version number of the daemon interface */ @@ -53,6 +54,9 @@ typedef enum SHUTDOWN, /* Shut the server down. */ GETSTAT, /* Get the server statistic. */ INVALIDATE, /* Invalidate one special cache. */ + GETFDPW, + GETFDGR, + GETFDHST, LASTREQ } request_type; @@ -110,9 +114,151 @@ typedef struct } 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 */ diff --git a/nscd/nscd.conf b/nscd/nscd.conf index 11d26b9efe..f972851116 100644 --- a/nscd/nscd.conf +++ b/nscd/nscd.conf @@ -19,6 +19,7 @@ # 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 # @@ -37,6 +38,7 @@ suggested-size passwd 211 check-files passwd yes persistent passwd yes + shared passwd yes enable-cache group yes positive-time-to-live group 3600 @@ -44,6 +46,7 @@ suggested-size group 211 check-files group yes persistent group yes + shared group yes enable-cache hosts yes positive-time-to-live hosts 3600 @@ -51,3 +54,4 @@ suggested-size hosts 211 check-files hosts yes persistent hosts yes + shared hosts yes diff --git a/nscd/nscd.h b/nscd/nscd.h index 45a93fdf29..889588cd9a 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -42,30 +42,6 @@ typedef enum } 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 @@ -74,63 +50,6 @@ struct datahead #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 { @@ -139,6 +58,7 @@ struct database_dyn int enabled; int check_file; int persistent; + int shared; const char *filename; const char *db_filename; time_t file_mtime; diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c index be7527eafb..2e6f8127a7 100644 --- a/nscd/nscd_conf.c +++ b/nscd/nscd_conf.c @@ -216,6 +216,20 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb]) 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) diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c index b9cde73044..3a66bb849b 100644 --- a/nscd/nscd_getgr_r.c +++ b/nscd/nscd_getgr_r.c @@ -18,6 +18,7 @@ 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> @@ -25,6 +26,7 @@ #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> @@ -64,36 +66,82 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer, } +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; @@ -103,71 +151,90 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type, 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 { @@ -177,8 +244,15 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type, 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; } diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c index 7449aade80..42327dd317 100644 --- a/nscd/nscd_gethst_r.c +++ b/nscd/nscd_gethst_r.c @@ -17,6 +17,7 @@ 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> @@ -26,9 +27,7 @@ #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" @@ -89,56 +88,15 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type, } -/* 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); } @@ -148,30 +106,89 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, 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; @@ -185,96 +202,110 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, 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]; @@ -282,14 +313,29 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, } 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; } @@ -297,7 +343,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, else { /* Store the error number. */ - *h_errnop = hst_resp.error; + *h_errnop = hst_resp->error; /* The `errno' to some value != ERANGE. */ __set_errno (ENOENT); @@ -305,8 +351,15 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type, 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; } diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c index 21f214d9a0..0d15a0cc3f 100644 --- a/nscd/nscd_getpw_r.c +++ b/nscd/nscd_getpw_r.c @@ -17,6 +17,7 @@ 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> @@ -24,6 +25,7 @@ #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> @@ -64,70 +66,124 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer, } +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; } } @@ -139,8 +195,15 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type, 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; } diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c new file mode 100644 index 0000000000..95661b6ff3 --- /dev/null +++ b/nscd/nscd_helper.c @@ -0,0 +1,335 @@ +/* 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; +} |