Compare owner of cache entry if in secure mode.
[kopensolaris-gnu/glibc.git] / nscd / cache.c
1 /* Copyright (c) 1998, 1999 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA. */
19
20 #include <atomicity.h>
21 #include <errno.h>
22 #include <error.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <arpa/inet.h>
27 #include <rpcsvc/nis.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <sys/uio.h>
31
32 #include "nscd.h"
33 #include "dbg_log.h"
34
35 /* Search the cache for a matching entry and return it when found.  If
36    this fails search the negative cache and return (void *) -1 if this
37    search was successful.  Otherwise return NULL.
38
39    This function must be called with the read-lock held.  */
40 struct hashentry *
41 cache_search (int type, void *key, size_t len, struct database *table,
42               uid_t owner)
43 {
44   unsigned long int hash = __nis_hash (key, len) % table->module;
45   struct hashentry *work;
46
47   work = table->array[hash];
48
49   while (work != NULL)
50     {
51       if (type == work->type && len == work->len
52           && memcmp (key, work->key, len) == 0 && work->owner == owner)
53         {
54           /* We found the entry.  Increment the appropriate counter.  */
55           if (work->data == (void *) -1)
56             ++table->neghit;
57           else
58             ++table->poshit;
59
60           return work;
61         }
62
63       work = work->next;
64     }
65
66   return NULL;
67 }
68
69 /* Add a new entry to the cache.  The return value is zero if the function
70    call was successful.
71
72    This function must be called with the read-lock held.
73
74    We modify the table but we nevertheless only acquire a read-lock.
75    This is ok since we use operations which would be safe even without
76    locking, given that the `prune_cache' function never runs.  Using
77    the readlock reduces the chance of conflicts.  */
78 void
79 cache_add (int type, void *key, size_t len, const void *packet, size_t total,
80            void *data, int last, time_t t, struct database *table, uid_t owner)
81 {
82   unsigned long int hash = __nis_hash (key, len) % table->module;
83   struct hashentry *newp;
84
85   newp = malloc (sizeof (struct hashentry));
86   if (newp == NULL)
87     error (EXIT_FAILURE, errno, _("while allocating hash table entry"));
88
89   newp->type = type;
90   newp->len = len;
91   newp->key = key;
92   newp->owner = owner;
93   newp->data = data;
94   newp->timeout = t;
95   newp->packet = packet;
96   newp->total = total;
97
98   newp->last = last;
99
100   /* Put the new entry in the first position.  */
101   do
102     newp->next = table->array[hash];
103   while (! compare_and_swap ((volatile long int *) &table->array[hash],
104                              (long int) newp->next, (long int) newp));
105
106   /* Update the statistics.  */
107   if (data == (void *) -1)
108     ++table->negmiss;
109   else if (last)
110     ++table->posmiss;
111 }
112
113 /* Walk through the table and remove all entries which lifetime ended.
114
115    We have a problem here.  To actually remove the entries we must get
116    the write-lock.  But since we want to keep the time we have the
117    lock as short as possible we cannot simply acquire the lock when we
118    start looking for timedout entries.
119
120    Therefore we do it in two stages: first we look for entries which
121    must be invalidated and remember them.  Then we get the lock and
122    actually remove them.  This is complicated by the way we have to
123    free the data structures since some hash table entries share the same
124    data.  */
125 void
126 prune_cache (struct database *table, time_t now)
127 {
128   size_t cnt = table->module;
129   int mark[cnt];
130   int anything = 0;
131   size_t first = cnt + 1;
132   size_t last = 0;
133
134   /* If this table is not actually used don't do anything.  */
135   if (cnt == 0)
136     return;
137
138   /* If we check for the modification of the underlying file we invalidate
139      the entries also in this case.  */
140   if (table->check_file)
141     {
142       struct stat st;
143
144       if (stat (table->filename, &st) < 0)
145         {
146           char buf[128];
147           /* We cannot stat() the file, disable file checking if the
148              file does not exist.  */
149           dbg_log (_("cannot stat() file `%s': %s"),
150                    table->filename, strerror_r (errno, buf, sizeof (buf)));
151           if (errno == ENOENT)
152             table->check_file = 0;
153         }
154       else
155         {
156           if (st.st_mtime != table->file_mtime)
157             {
158               /* The file changed.  Invalidate all entries.  */
159               now = LONG_MAX;
160               table->file_mtime = st.st_mtime;
161             }
162         }
163     }
164
165   /* We run through the table and find values which are not valid anymore.
166
167    Note that for the initial step, finding the entries to be removed,
168    we don't need to get any lock.  It is at all timed assured that the
169    linked lists are set up correctly and that no second thread prunes
170    the cache.  */
171   do
172     {
173       struct hashentry *runp = table->array[--cnt];
174
175       mark[cnt] = 0;
176
177       while (runp != NULL)
178         {
179           if (runp->timeout < now)
180             {
181               ++mark[cnt];
182               anything = 1;
183               first = MIN (first, cnt);
184               last = MAX (last, cnt);
185             }
186           runp = runp->next;
187         }
188     }
189   while (cnt > 0);
190
191   if (anything)
192     {
193       struct hashentry *head = NULL;
194
195       /* Now we have to get the write lock since we are about to modify
196          the table.  */
197       pthread_rwlock_wrlock (&table->lock);
198
199       while (first <= last)
200         {
201           if (mark[first] > 0)
202             {
203               struct hashentry *runp;
204
205               while (table->array[first]->timeout < now)
206                 {
207                   table->array[first]->dellist = head;
208                   head = table->array[first];
209                   table->array[first] = head->next;
210                   if (--mark[first] == 0)
211                     break;
212                 }
213
214               runp = table->array[first];
215               while (mark[first] > 0)
216                 {
217                   if (runp->next->timeout < now)
218                     {
219                       runp->next->dellist = head;
220                       head = runp->next;
221                       runp->next = head->next;
222                       --mark[first];
223                     }
224                   else
225                     runp = runp->next;
226                 }
227             }
228           ++first;
229         }
230
231       /* It's all done.  */
232       pthread_rwlock_unlock (&table->lock);
233
234       /* And another run to free the data.  */
235       do
236         {
237           struct hashentry *old = head;
238
239           if (debug_level > 0)
240             {
241               char buf[INET6_ADDRSTRLEN];
242               const char *str;
243
244               if ((old->type == GETHOSTBYADDR || old->type == GETHOSTBYADDRv6)
245                   && (old->last || old->data == (void *) -1))
246                 {
247                   inet_ntop (old->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
248                              old->key, buf, sizeof (buf));
249                   str = buf;
250                 }
251               else
252                 str = old->last ? old->key : (old->data == (void *) -1
253                                               ? old->key : "???");
254
255               dbg_log ("remove %s entry \"%s\"", serv2str[old->type], str);
256             }
257
258           /* Free the data structures.  */
259           if (old->data == (void *) -1)
260             free (old->key);
261           else if (old->last)
262             free (old->data);
263
264           head = head->dellist;
265
266           free (old);
267         }
268       while (head != NULL);
269     }
270 }