/* Cache handling for host lookup.
- Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include <assert.h>
#include <errno.h>
#include "../grp/compat-initgroups.c"
-static void
+static time_t
addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
- void *key, uid_t uid, struct hashentry *he,
+ void *key, uid_t uid, struct hashentry *const he,
struct datahead *dh)
{
/* Search for the entry matching the key. Please note that we don't
char strdata[0];
} *dataset = NULL;
- if (__builtin_expect (debug_level > 0, 0))
+ if (__glibc_unlikely (debug_level > 0))
{
if (he == NULL)
dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
}
static service_user *group_database;
- service_user *nip = NULL;
+ service_user *nip;
int no_more;
- if (group_database != NULL)
- {
- nip = group_database;
- no_more = 0;
- }
- else
+ if (group_database == NULL)
no_more = __nss_database_lookup ("group", NULL,
- "compat [NOTFOUND=return] files", &nip);
+ "compat [NOTFOUND=return] files",
+ &group_database);
+ else
+ no_more = 0;
+ nip = group_database;
/* We always use sysconf even if NGROUPS_MAX is defined. That way, the
limit can be raised in the kernel configuration without having to
bool all_tryagain = true;
bool any_success = false;
- /* This is temporary memory, we need not (ad must not) call
+ /* This is temporary memory, we need not (and must not) call
mempool_alloc. */
// XXX This really should use alloca. need to change the backends.
gid_t *groups = (gid_t *) malloc (size * sizeof (gid_t));
- if (__builtin_expect (groups == NULL, 0))
+ if (__glibc_unlikely (groups == NULL))
/* No more memory. */
goto out;
nip = nip->next;
}
+ bool all_written;
ssize_t total;
- ssize_t written;
+ time_t timeout;
out:
+ all_written = true;
+ timeout = MAX_TIMEOUT_VALUE;
if (!any_success)
{
/* Nothing found. Create a negative result record. */
- written = total = sizeof (notfound);
+ total = sizeof (notfound);
if (he != NULL && all_tryagain)
{
if (reload_count != UINT_MAX && dh->nreloads == reload_count)
/* Do not reset the value if we never not reload the record. */
dh->nreloads = reload_count - 1;
+
+ /* Reload with the same time-to-live value. */
+ timeout = dh->timeout = time (NULL) + db->postimeout;
}
else
{
/* We have no data. This means we send the standard reply for this
case. */
- if (fd != -1)
- written = TEMP_FAILURE_RETRY (send (fd, ¬found, total,
- MSG_NOSIGNAL));
-
- dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
- /* If we cannot permanently store the result, so be it. */
- if (dataset != NULL)
+ if (fd != -1
+ && TEMP_FAILURE_RETRY (send (fd, ¬found, total,
+ MSG_NOSIGNAL)) != total)
+ all_written = false;
+
+ /* If we have a transient error or cannot permanently store
+ the result, so be it. */
+ if (all_tryagain || __builtin_expect (db->negtimeout == 0, 0))
+ {
+ /* Mark the old entry as obsolete. */
+ if (dh != NULL)
+ dh->usable = false;
+ }
+ else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
+ + req->key_len), 1)) != NULL)
{
dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
dataset->head.recsize = total;
dataset->head.usable = true;
/* Compute the timeout time. */
- dataset->head.timeout = time (NULL) + db->negtimeout;
+ timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
/* This is the reply. */
memcpy (&dataset->resp, ¬found, total);
+ sizeof (struct dataset) + req->key_len, MS_ASYNC);
}
- /* Now get the lock to safely insert the records. */
- pthread_rwlock_rdlock (&db->lock);
-
- if (cache_add (req->type, key_copy, req->key_len,
- &dataset->head, true, db, uid) < 0)
- /* Ensure the data can be recovered. */
- dataset->head.usable = false;
+ (void) cache_add (req->type, key_copy, req->key_len,
+ &dataset->head, true, db, uid, he == NULL);
pthread_rwlock_unlock (&db->lock);
if (dh != NULL)
dh->usable = false;
}
- else
- ++db->head->addfailed;
}
}
else
{
- written = total = sizeof (struct dataset) + start * sizeof (int32_t);
+ total = offsetof (struct dataset, strdata) + start * sizeof (int32_t);
/* If we refill the cache, first assume the reconrd did not
change. Allocate memory on the cache since it is likely
dataset = NULL;
if (he == NULL)
- {
- dataset = (struct dataset *) mempool_alloc (db,
- total + req->key_len);
- if (dataset == NULL)
- ++db->head->addfailed;
- }
+ dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
+ 1);
if (dataset == NULL)
{
dataset->head.usable = true;
/* Compute the timeout time. */
- dataset->head.timeout = time (NULL) + db->postimeout;
+ timeout = dataset->head.timeout = time (NULL) + db->postimeout;
dataset->resp.version = NSCD_VERSION;
dataset->resp.found = 1;
/* Finally the user name. */
memcpy (cp, key, req->key_len);
+ assert (cp == dataset->strdata + total - offsetof (struct dataset,
+ strdata));
+
/* Now we can determine whether on refill we have to create a new
record or not. */
if (he != NULL)
/* We have to create a new record. Just allocate
appropriate memory and copy it. */
struct dataset *newp
- = (struct dataset *) mempool_alloc (db, total + req->key_len);
+ = (struct dataset *) mempool_alloc (db, total + req->key_len,
+ 1);
if (newp != NULL)
{
/* Adjust pointer into the memory block. */
{
assert (db->wr_fd != -1);
assert ((char *) &dataset->resp > (char *) db->data);
- assert ((char *) &dataset->resp - (char *) db->head
+ assert ((char *) dataset - (char *) db->head
+ total
<= (sizeof (struct database_pers_head)
+ db->head->module * sizeof (ref_t)
+ db->head->data_size));
- written = sendfileall (fd, db->wr_fd,
- (char *) &dataset->resp
- - (char *) db->head, total);
+ ssize_t written = sendfileall (fd, db->wr_fd,
+ (char *) &dataset->resp
+ - (char *) db->head,
+ dataset->head.recsize);
+ if (written != dataset->head.recsize)
+ {
# ifndef __ASSUME_SENDFILE
- if (written == -1 && errno == ENOSYS)
- goto use_write;
+ if (written == -1 && errno == ENOSYS)
+ goto use_write;
# endif
+ all_written = false;
+ }
}
else
# ifndef __ASSUME_SENDFILE
use_write:
# endif
#endif
- written = writeall (fd, &dataset->resp, total);
+ if (writeall (fd, &dataset->resp, dataset->head.recsize)
+ != dataset->head.recsize)
+ all_written = false;
}
req->key_len, MS_ASYNC);
}
- /* Now get the lock to safely insert the records. */
- pthread_rwlock_rdlock (&db->lock);
-
- if (cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
- db, uid) < 0)
- /* Could not allocate memory. Make sure the data gets
- discarded. */
- dataset->head.usable = false;
+ (void) cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
+ db, uid, he == NULL);
pthread_rwlock_unlock (&db->lock);
}
free (groups);
- if (__builtin_expect (written != total, 0) && debug_level > 0)
+ if (__builtin_expect (!all_written, 0) && debug_level > 0)
{
char buf[256];
dbg_log (_("short write in %s: %s"), __FUNCTION__,
strerror_r (errno, buf, sizeof (buf)));
}
+
+ return timeout;
}
}
-void
+time_t
readdinitgroups (struct database_dyn *db, struct hashentry *he,
struct datahead *dh)
{
.key_len = he->len
};
- addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+ return addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
}