Update.
* 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:
parent
0a3ad40da9
commit
c207f23b0b
24
ChangeLog
24
ChangeLog
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
subdir := nscd
|
||||
|
||||
routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
|
||||
aux := nscd_helper
|
||||
|
||||
include ../Makeconfig
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
165
nscd/hstcache.c
165
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);
|
||||
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)
|
||||
|
|
11
nscd/mem.c
11
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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
82
nscd/nscd.h
82
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue