1 /* Cache handling for host lookup.
2 Copyright (C) 2004-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <https://www.gnu.org/licenses/>. */
27 #include <scratch_buffer.h>
33 #include "../nss/nsswitch.h"
35 #ifdef LINK_OBSOLETE_NSL
36 # define DEFAULT_CONFIG "compat [NOTFOUND=return] files"
38 # define DEFAULT_CONFIG "files"
41 /* Type of the lookup function. */
42 typedef enum nss_status (*initgroups_dyn_function
) (const char *, gid_t
,
43 long int *, long int *,
44 gid_t
**, long int, int *);
47 static const initgr_response_header notfound
=
49 .version
= NSCD_VERSION
,
55 #include "../grp/compat-initgroups.c"
59 addinitgroupsX (struct database_dyn
*db
, int fd
, request_header
*req
,
60 void *key
, uid_t uid
, struct hashentry
*const he
,
63 /* Search for the entry matching the key. Please note that we don't
64 look again in the table whether the dataset is now available. We
65 simply insert it. It does not matter if it is in there twice. The
66 pruning function only will look at the timestamp. */
69 /* We allocate all data in one memory block: the iov vector,
70 the response header and the dataset itself. */
74 initgr_response_header resp
;
78 if (__glibc_unlikely (debug_level
> 0))
81 dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key
);
83 dbg_log (_("Reloading \"%s\" in group cache!"), (char *) key
);
86 static service_user
*group_database
;
90 if (group_database
== NULL
)
91 no_more
= __nss_database_lookup2 ("group", NULL
, DEFAULT_CONFIG
,
97 /* We always use sysconf even if NGROUPS_MAX is defined. That way, the
98 limit can be raised in the kernel configuration without having to
100 long int limit
= __sysconf (_SC_NGROUPS_MAX
);
104 /* We limit the size of the intially allocated array. */
105 size
= MIN (limit
, 64);
107 /* No fixed limit on groups. Pick a starting buffer size. */
111 bool all_tryagain
= true;
112 bool any_success
= false;
114 /* This is temporary memory, we need not (and must not) call
116 // XXX This really should use alloca. need to change the backends.
117 gid_t
*groups
= (gid_t
*) malloc (size
* sizeof (gid_t
));
118 if (__glibc_unlikely (groups
== NULL
))
119 /* No more memory. */
122 /* Nothing added yet. */
125 long int prev_start
= start
;
126 enum nss_status status
;
127 initgroups_dyn_function fct
;
128 fct
= __nss_lookup_function (nip
, "initgroups_dyn");
132 status
= compat_call (nip
, key
, -1, &start
, &size
, &groups
,
135 if (nss_next_action (nip
, NSS_STATUS_UNAVAIL
) != NSS_ACTION_CONTINUE
)
139 status
= DL_CALL_FCT (fct
, (key
, -1, &start
, &size
, &groups
,
142 /* Remove duplicates. */
143 long int cnt
= prev_start
;
147 for (inner
= 0; inner
< prev_start
; ++inner
)
148 if (groups
[inner
] == groups
[cnt
])
151 if (inner
< prev_start
)
152 groups
[cnt
] = groups
[--start
];
157 if (status
!= NSS_STATUS_TRYAGAIN
)
158 all_tryagain
= false;
160 /* This is really only for debugging. */
161 if (NSS_STATUS_TRYAGAIN
> status
|| status
> NSS_STATUS_RETURN
)
162 __libc_fatal ("Illegal status in internal_getgrouplist.\n");
164 any_success
|= status
== NSS_STATUS_SUCCESS
;
166 if (status
!= NSS_STATUS_SUCCESS
167 && nss_next_action (nip
, status
) == NSS_ACTION_RETURN
)
170 if (nip
->next
== NULL
)
181 timeout
= MAX_TIMEOUT_VALUE
;
184 /* Nothing found. Create a negative result record. */
185 total
= sizeof (notfound
);
187 if (he
!= NULL
&& all_tryagain
)
189 /* If we have an old record available but cannot find one now
190 because the service is not available we keep the old record
191 and make sure it does not get removed. */
192 if (reload_count
!= UINT_MAX
&& dh
->nreloads
== reload_count
)
193 /* Do not reset the value if we never not reload the record. */
194 dh
->nreloads
= reload_count
- 1;
196 /* Reload with the same time-to-live value. */
197 timeout
= dh
->timeout
= time (NULL
) + db
->postimeout
;
201 /* We have no data. This means we send the standard reply for this
204 && TEMP_FAILURE_RETRY (send (fd
, ¬found
, total
,
205 MSG_NOSIGNAL
)) != total
)
208 /* If we have a transient error or cannot permanently store
209 the result, so be it. */
210 if (all_tryagain
|| __builtin_expect (db
->negtimeout
== 0, 0))
212 /* Mark the old entry as obsolete. */
216 else if ((dataset
= mempool_alloc (db
, (sizeof (struct dataset
)
217 + req
->key_len
), 1)) != NULL
)
219 timeout
= datahead_init_neg (&dataset
->head
,
220 (sizeof (struct dataset
)
221 + req
->key_len
), total
,
224 /* This is the reply. */
225 memcpy (&dataset
->resp
, ¬found
, total
);
227 /* Copy the key data. */
228 char *key_copy
= memcpy (dataset
->strdata
, key
, req
->key_len
);
230 /* If necessary, we also propagate the data to disk. */
234 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
235 msync ((void *) pval
,
236 ((uintptr_t) dataset
& pagesize_m1
)
237 + sizeof (struct dataset
) + req
->key_len
, MS_ASYNC
);
240 (void) cache_add (req
->type
, key_copy
, req
->key_len
,
241 &dataset
->head
, true, db
, uid
, he
== NULL
);
243 pthread_rwlock_unlock (&db
->lock
);
245 /* Mark the old entry as obsolete. */
254 total
= offsetof (struct dataset
, strdata
) + start
* sizeof (int32_t);
256 /* If we refill the cache, first assume the reconrd did not
257 change. Allocate memory on the cache since it is likely
258 discarded anyway. If it turns out to be necessary to have a
259 new record we can still allocate real memory. */
260 bool alloca_used
= false;
264 dataset
= (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
,
269 /* We cannot permanently add the result in the moment. But
270 we can provide the result as is. Store the data in some
272 dataset
= (struct dataset
*) alloca (total
+ req
->key_len
);
274 /* We cannot add this record to the permanent database. */
278 timeout
= datahead_init_pos (&dataset
->head
, total
+ req
->key_len
,
279 total
- offsetof (struct dataset
, resp
),
280 he
== NULL
? 0 : dh
->nreloads
+ 1,
283 dataset
->resp
.version
= NSCD_VERSION
;
284 dataset
->resp
.found
= 1;
285 dataset
->resp
.ngrps
= start
;
287 char *cp
= dataset
->strdata
;
289 /* Copy the GID values. If the size of the types match this is
291 if (sizeof (gid_t
) == sizeof (int32_t))
292 cp
= mempcpy (cp
, groups
, start
* sizeof (gid_t
));
295 gid_t
*gcp
= (gid_t
*) cp
;
297 for (int i
= 0; i
< start
; ++i
)
303 /* Finally the user name. */
304 memcpy (cp
, key
, req
->key_len
);
306 assert (cp
== dataset
->strdata
+ total
- offsetof (struct dataset
,
309 /* Now we can determine whether on refill we have to create a new
315 if (total
+ req
->key_len
== dh
->allocsize
316 && total
- offsetof (struct dataset
, resp
) == dh
->recsize
317 && memcmp (&dataset
->resp
, dh
->data
,
318 dh
->allocsize
- offsetof (struct dataset
, resp
)) == 0)
320 /* The data has not changed. We will just bump the
321 timeout value. Note that the new record has been
322 allocated on the stack and need not be freed. */
323 dh
->timeout
= dataset
->head
.timeout
;
328 /* We have to create a new record. Just allocate
329 appropriate memory and copy it. */
331 = (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
,
335 /* Adjust pointer into the memory block. */
336 cp
= (char *) newp
+ (cp
- (char *) dataset
);
338 dataset
= memcpy (newp
, dataset
, total
+ req
->key_len
);
342 /* Mark the old record as obsolete. */
348 /* We write the dataset before inserting it to the database
349 since while inserting this thread might block and so would
350 unnecessarily let the receiver wait. */
353 if (writeall (fd
, &dataset
->resp
, dataset
->head
.recsize
)
354 != dataset
->head
.recsize
)
359 /* Add the record to the database. But only if it has not been
360 stored on the stack. */
363 /* If necessary, we also propagate the data to disk. */
367 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
368 msync ((void *) pval
,
369 ((uintptr_t) dataset
& pagesize_m1
) + total
370 + req
->key_len
, MS_ASYNC
);
373 (void) cache_add (INITGROUPS
, cp
, req
->key_len
, &dataset
->head
, true,
374 db
, uid
, he
== NULL
);
376 pthread_rwlock_unlock (&db
->lock
);
382 if (__builtin_expect (!all_written
, 0) && debug_level
> 0)
385 dbg_log (_("short write in %s: %s"), __FUNCTION__
,
386 strerror_r (errno
, buf
, sizeof (buf
)));
394 addinitgroups (struct database_dyn
*db
, int fd
, request_header
*req
, void *key
,
397 addinitgroupsX (db
, fd
, req
, key
, uid
, NULL
, NULL
);
402 readdinitgroups (struct database_dyn
*db
, struct hashentry
*he
,
411 return addinitgroupsX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
);