* 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.
This commit is contained in:
Ulrich Drepper 2004-09-08 15:46:42 +00:00
parent 0a3ad40da9
commit c207f23b0b
17 changed files with 1106 additions and 493 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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

76
nis/nis_hash.c Normal file
View File

@ -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;
}

View File

@ -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"

View File

@ -22,6 +22,7 @@
subdir := nscd
routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
aux := nscd_helper
include ../Makeconfig

View File

@ -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;
}

View File

@ -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);
assert (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;
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);
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;
}
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 = 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;
}
if (addr_list_type == GETHOSTBYADDR
&& req->type != GETHOSTBYADDR
&& cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
&dataset->head, false, db, owner) < 0)
goto out;
/* 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))
{
if (he == NULL)
dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
const char *str;
char buf[INET6_ADDRSTRLEN + 1];
if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
str = key;
else
dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
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 *) str);
else
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)

View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
cp += hst_resp->h_name_len + align2;
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);
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;
total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
n = 3;
}
vec[n].iov_base = cp;
vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
{
resultbuf->h_addr_list[cnt] = cp;
cp += IN6ADDRSZ;
}
resultbuf->h_addrtype = AF_INET6;
resultbuf->h_length = IN6ADDRSZ;
total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
++n;
}
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;
if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len)
goto out;
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;
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);
total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
++n;
}
if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
{
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;
total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
++n;
}
vec[n].iov_base = resultbuf->h_addr_list[0];
vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
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;
}

View File

@ -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);
/* Set the information we already have. */
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;
/* get pw_passwd */
resultbuf->pw_passwd = p;
p += pw_resp->pw_passwd_len;
/* get pw_gecos */
resultbuf->pw_gecos = p;
p += pw_resp->pw_gecos_len;
/* get pw_dir */
resultbuf->pw_dir = p;
p += pw_resp->pw_dir_len;
/* get pw_pshell */
resultbuf->pw_shell = p;
p += pw_resp->pw_shell_len;
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;
goto out_close;
}
/* Set the information we already have. */
resultbuf->pw_uid = pw_resp.pw_uid;
resultbuf->pw_gid = pw_resp.pw_gid;
/* get pw_name */
resultbuf->pw_name = p;
p += pw_resp.pw_name_len;
/* get pw_passwd */
resultbuf->pw_passwd = p;
p += pw_resp.pw_passwd_len;
/* get pw_gecos */
resultbuf->pw_gecos = p;
p += pw_resp.pw_gecos_len;
/* get pw_dir */
resultbuf->pw_dir = p;
p += pw_resp.pw_dir_len;
/* get pw_pshell */
resultbuf->pw_shell = p;
ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
if (nbytes == (ssize_t) total)
retval = 0;
if (pw_name == NULL)
{
retval = 0;
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
{
/* 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;
}

335
nscd/nscd_helper.c Normal file
View File

@ -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;
}