1 /* Cache handling for netgroup lookup.
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
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 <http://www.gnu.org/licenses/>. */
27 #include "../inet/netgroup.h"
31 # include <kernel-features.h>
35 /* This is the standard reply in case the service is disabled. */
36 static const netgroup_response_header disabled
=
38 .version
= NSCD_VERSION
,
44 /* This is the struct describing how to write this record. */
45 const struct iovec netgroup_iov_disabled
=
47 .iov_base
= (void *) &disabled
,
48 .iov_len
= sizeof (disabled
)
52 /* This is the standard reply in case we haven't found the dataset. */
53 static const netgroup_response_header notfound
=
55 .version
= NSCD_VERSION
,
65 netgroup_response_header resp
;
71 addgetnetgrentX (struct database_dyn
*db
, int fd
, request_header
*req
,
72 const char *key
, uid_t uid
, struct hashentry
*he
,
73 struct datahead
*dh
, struct dataset
**resultp
)
75 if (__builtin_expect (debug_level
> 0, 0))
78 dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key
);
80 dbg_log (_("Reloading \"%s\" in netgroup cache!"), key
);
83 static service_user
*netgroup_database
;
85 struct dataset
*dataset
;
86 bool cacheable
= false;
89 char *key_copy
= NULL
;
90 struct __netgrent data
;
91 size_t buflen
= MAX (1024, sizeof (*dataset
) + req
->key_len
);
92 size_t buffilled
= sizeof (*dataset
);
95 bool use_malloc
= false;
96 size_t group_len
= strlen (key
) + 1;
99 struct name_list elem
;
100 char mem
[sizeof (struct name_list
) + group_len
];
103 if (netgroup_database
== NULL
104 && __nss_database_lookup ("netgroup", NULL
, NULL
, &netgroup_database
))
106 /* No such service. */
107 total
= sizeof (notfound
);
108 timeout
= time (NULL
) + db
->negtimeout
;
111 TEMP_FAILURE_RETRY (send (fd
, ¬found
, total
, MSG_NOSIGNAL
));
113 dataset
= mempool_alloc (db
, sizeof (struct dataset
) + req
->key_len
, 1);
114 /* If we cannot permanently store the result, so be it. */
117 dataset
->head
.allocsize
= sizeof (struct dataset
) + req
->key_len
;
118 dataset
->head
.recsize
= total
;
119 dataset
->head
.notfound
= true;
120 dataset
->head
.nreloads
= 0;
121 dataset
->head
.usable
= true;
123 /* Compute the timeout time. */
124 timeout
= dataset
->head
.timeout
= time (NULL
) + db
->negtimeout
;
125 dataset
->head
.ttl
= db
->negtimeout
;
127 /* This is the reply. */
128 memcpy (&dataset
->resp
, ¬found
, total
);
130 /* Copy the key data. */
131 memcpy (dataset
->strdata
, key
, req
->key_len
);
139 memset (&data
, '\0', sizeof (data
));
140 buffer
= alloca (buflen
);
141 first_needed
.elem
.next
= &first_needed
.elem
;
142 memcpy (first_needed
.elem
.name
, key
, group_len
);
143 data
.needed_groups
= &first_needed
.elem
;
145 while (data
.needed_groups
!= NULL
)
147 /* Add the next group to the list of those which are known. */
148 struct name_list
*this_group
= data
.needed_groups
->next
;
149 if (this_group
== data
.needed_groups
)
150 data
.needed_groups
= NULL
;
152 data
.needed_groups
->next
= this_group
->next
;
153 this_group
->next
= data
.known_groups
;
154 data
.known_groups
= this_group
;
158 enum nss_status (*f
) (const char *, struct __netgrent
*);
162 service_user
*nip
= netgroup_database
;
163 int no_more
= __nss_lookup (&nip
, "setnetgrent", NULL
, &setfct
.ptr
);
166 enum nss_status status
167 = DL_CALL_FCT (*setfct
.f
, (data
.known_groups
->name
, &data
));
169 if (status
== NSS_STATUS_SUCCESS
)
173 enum nss_status (*f
) (struct __netgrent
*, char *, size_t,
177 getfct
.ptr
= __nss_lookup_function (nip
, "getnetgrent_r");
178 if (getfct
.f
!= NULL
)
182 status
= getfct
.f (&data
, buffer
+ buffilled
,
183 buflen
- buffilled
, &e
);
184 if (status
== NSS_STATUS_RETURN
)
185 /* This was the last one for this group. Look
186 at next group if available. */
188 if (status
== NSS_STATUS_SUCCESS
)
190 if (data
.type
== triple_val
)
192 const char *nhost
= data
.val
.triple
.host
;
193 const char *nuser
= data
.val
.triple
.user
;
194 const char *ndomain
= data
.val
.triple
.domain
;
196 if (data
.val
.triple
.host
> data
.val
.triple
.user
197 || data
.val
.triple
.user
> data
.val
.triple
.domain
)
199 const char *last
= MAX (nhost
,
200 MAX (nuser
, ndomain
));
201 size_t bufused
= (last
+ strlen (last
) + 1
204 /* We have to make temporary copies. */
205 size_t hostlen
= strlen (nhost
) + 1;
206 size_t userlen
= strlen (nuser
) + 1;
207 size_t domainlen
= strlen (ndomain
) + 1;
208 size_t needed
= hostlen
+ userlen
+ domainlen
;
210 if (buflen
- req
->key_len
- bufused
< needed
)
212 size_t newsize
= MAX (2 * buflen
,
213 buflen
+ 2 * needed
);
214 if (use_malloc
|| newsize
> 1024 * 1024)
217 char *newbuf
= xrealloc (use_malloc
226 extend_alloca (buffer
, buflen
, newsize
);
229 nhost
= memcpy (buffer
+ bufused
,
231 nuser
= memcpy ((char *) nhost
+ hostlen
,
233 ndomain
= memcpy ((char *) nuser
+ userlen
,
237 char *wp
= buffer
+ buffilled
;
238 wp
= stpcpy (wp
, nhost
) + 1;
239 wp
= stpcpy (wp
, nuser
) + 1;
240 wp
= stpcpy (wp
, ndomain
) + 1;
241 buffilled
= wp
- buffer
;
246 /* Check that the group has not been
248 struct name_list
*runp
= data
.needed_groups
;
252 if (strcmp (runp
->name
, data
.val
.group
) == 0)
256 if (runp
== data
.needed_groups
)
265 runp
= data
.known_groups
;
267 if (strcmp (runp
->name
, data
.val
.group
) == 0)
275 /* A new group is requested. */
276 size_t namelen
= strlen (data
.val
.group
) + 1;
277 struct name_list
*newg
= alloca (sizeof (*newg
)
279 memcpy (newg
->name
, data
.val
.group
, namelen
);
280 if (data
.needed_groups
== NULL
)
281 data
.needed_groups
= newg
->next
= newg
;
284 newg
->next
= data
.needed_groups
->next
;
285 data
.needed_groups
->next
= newg
;
286 data
.needed_groups
= newg
;
291 else if (status
== NSS_STATUS_UNAVAIL
&& e
== ERANGE
)
293 size_t newsize
= 2 * buflen
;
294 if (use_malloc
|| newsize
> 1024 * 1024)
297 char *newbuf
= xrealloc (use_malloc
298 ? buffer
: NULL
, buflen
);
304 extend_alloca (buffer
, buflen
, newsize
);
308 enum nss_status (*endfct
) (struct __netgrent
*);
309 endfct
= __nss_lookup_function (nip
, "endnetgrent");
311 (void) DL_CALL_FCT (*endfct
, (&data
));
316 no_more
= __nss_next2 (&nip
, "setnetgrent", NULL
, &setfct
.ptr
,
323 /* Fill in the dataset. */
324 dataset
= (struct dataset
*) buffer
;
325 dataset
->head
.allocsize
= total
+ req
->key_len
;
326 dataset
->head
.recsize
= total
- offsetof (struct dataset
, resp
);
327 dataset
->head
.notfound
= false;
328 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
329 dataset
->head
.usable
= true;
330 dataset
->head
.ttl
= db
->postimeout
;
331 timeout
= dataset
->head
.timeout
= time (NULL
) + dataset
->head
.ttl
;
333 dataset
->resp
.version
= NSCD_VERSION
;
334 dataset
->resp
.found
= 1;
335 dataset
->resp
.nresults
= nentries
;
336 dataset
->resp
.result_len
= buffilled
- sizeof (*dataset
);
338 assert (buflen
- buffilled
>= req
->key_len
);
339 key_copy
= memcpy (buffer
+ buffilled
, key
, req
->key_len
);
340 buffilled
+= req
->key_len
;
342 /* Now we can determine whether on refill we have to create a new
348 if (dataset
->head
.allocsize
== dh
->allocsize
349 && dataset
->head
.recsize
== dh
->recsize
350 && memcmp (&dataset
->resp
, dh
->data
,
351 dh
->allocsize
- offsetof (struct dataset
, resp
)) == 0)
353 /* The data has not changed. We will just bump the timeout
354 value. Note that the new record has been allocated on
355 the stack and need not be freed. */
356 dh
->timeout
= dataset
->head
.timeout
;
357 dh
->ttl
= dataset
->head
.ttl
;
359 dataset
= (struct dataset
*) dh
;
367 = (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
, 1);
368 if (__builtin_expect (newp
!= NULL
, 1))
370 /* Adjust pointer into the memory block. */
371 key_copy
= (char *) newp
+ (key_copy
- buffer
);
373 dataset
= memcpy (newp
, dataset
, total
+ req
->key_len
);
377 /* Mark the old record as obsolete. */
382 if (he
== NULL
&& fd
!= -1)
384 /* We write the dataset before inserting it to the database
385 since while inserting this thread might block and so would
386 unnecessarily let the receiver wait. */
389 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
391 assert (db
->wr_fd
!= -1);
392 assert ((char *) &dataset
->resp
> (char *) db
->data
);
393 assert ((char *) dataset
- (char *) db
->head
+ total
394 <= (sizeof (struct database_pers_head
)
395 + db
->head
->module
* sizeof (ref_t
)
396 + db
->head
->data_size
));
397 # ifndef __ASSUME_SENDFILE
400 sendfileall (fd
, db
->wr_fd
, (char *) &dataset
->resp
401 - (char *) db
->head
, dataset
->head
.recsize
);
402 # ifndef __ASSUME_SENDFILE
403 if (written
== -1 && errno
== ENOSYS
)
410 #if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
413 writeall (fd
, &dataset
->resp
, dataset
->head
.recsize
);
419 /* If necessary, we also propagate the data to disk. */
423 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
424 msync ((void *) pval
,
425 ((uintptr_t) dataset
& pagesize_m1
) + total
+ req
->key_len
,
429 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
430 true, db
, uid
, he
== NULL
);
432 pthread_rwlock_unlock (&db
->lock
);
434 /* Mark the old entry as obsolete. */
450 addinnetgrX (struct database_dyn
*db
, int fd
, request_header
*req
,
451 char *key
, uid_t uid
, struct hashentry
*he
,
454 const char *group
= key
;
455 key
= (char *) rawmemchr (key
, '\0') + 1;
456 size_t group_len
= key
- group
- 1;
457 const char *host
= *key
++ ? key
: NULL
;
459 key
= (char *) rawmemchr (key
, '\0') + 1;
460 const char *user
= *key
++ ? key
: NULL
;
462 key
= (char *) rawmemchr (key
, '\0') + 1;
463 const char *domain
= *key
++ ? key
: NULL
;
465 if (__builtin_expect (debug_level
> 0, 0))
468 dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
469 group
, host
?: "", user
?: "", domain
?: "");
471 dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
472 group
, host
?: "", user
?: "", domain
?: "");
475 struct dataset
*result
= (struct dataset
*) cache_search (GETNETGRENT
,
480 timeout
= result
->head
.timeout
;
483 request_header req_get
=
488 timeout
= addgetnetgrentX (db
, -1, &req_get
, group
, uid
, NULL
, NULL
,
494 struct datahead head
;
495 innetgroup_response_header resp
;
497 = (struct indataset
*) mempool_alloc (db
,
498 sizeof (*dataset
) + req
->key_len
,
500 struct indataset dataset_mem
;
501 bool cacheable
= true;
502 if (__builtin_expect (dataset
== NULL
, 0))
505 dataset
= &dataset_mem
;
508 dataset
->head
.allocsize
= sizeof (*dataset
) + req
->key_len
;
509 dataset
->head
.recsize
= sizeof (innetgroup_response_header
);
510 dataset
->head
.notfound
= result
->head
.notfound
;
511 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
512 dataset
->head
.usable
= true;
513 dataset
->head
.ttl
= result
->head
.ttl
;
514 dataset
->head
.timeout
= timeout
;
516 dataset
->resp
.version
= NSCD_VERSION
;
517 dataset
->resp
.found
= result
->resp
.found
;
518 /* Until we find a matching entry the result is 0. */
519 dataset
->resp
.result
= 0;
521 char *key_copy
= memcpy ((char *) (dataset
+ 1), group
, req
->key_len
);
523 if (dataset
->resp
.found
)
525 const char *triplets
= (const char *) (&result
->resp
+ 1);
527 for (nscd_ssize_t i
= result
->resp
.nresults
; i
> 0; --i
)
532 success
= strcmp (host
, triplets
) == 0;
533 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
535 if (success
&& user
!= NULL
)
536 success
= strcmp (user
, triplets
) == 0;
537 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
539 if (success
&& (domain
== NULL
|| strcmp (domain
, triplets
) == 0))
541 dataset
->resp
.result
= 1;
544 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
548 if (he
!= NULL
&& dh
->data
[0].innetgroupdata
.result
== dataset
->resp
.result
)
550 /* The data has not changed. We will just bump the timeout
551 value. Note that the new record has been allocated on
552 the stack and need not be freed. */
553 dh
->timeout
= timeout
;
554 dh
->ttl
= dataset
->head
.ttl
;
561 /* We write the dataset before inserting it to the database
562 since while inserting this thread might block and so would
563 unnecessarily let the receiver wait. */
567 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
569 assert (db
->wr_fd
!= -1);
570 assert ((char *) &dataset
->resp
> (char *) db
->data
);
571 assert ((char *) dataset
- (char *) db
->head
+ sizeof (*dataset
)
572 <= (sizeof (struct database_pers_head
)
573 + db
->head
->module
* sizeof (ref_t
)
574 + db
->head
->data_size
));
575 # ifndef __ASSUME_SENDFILE
578 sendfileall (fd
, db
->wr_fd
,
579 (char *) &dataset
->resp
- (char *) db
->head
,
580 sizeof (innetgroup_response_header
));
581 # ifndef __ASSUME_SENDFILE
582 if (written
== -1 && errno
== ENOSYS
)
588 # ifndef __ASSUME_SENDFILE
592 writeall (fd
, &dataset
->resp
, sizeof (innetgroup_response_header
));
598 /* If necessary, we also propagate the data to disk. */
602 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
603 msync ((void *) pval
,
604 ((uintptr_t) dataset
& pagesize_m1
) + sizeof (*dataset
)
609 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
610 true, db
, uid
, he
== NULL
);
612 pthread_rwlock_unlock (&db
->lock
);
614 /* Mark the old entry as obsolete. */
624 addgetnetgrent (struct database_dyn
*db
, int fd
, request_header
*req
,
625 void *key
, uid_t uid
)
627 struct dataset
*ignore
;
629 addgetnetgrentX (db
, fd
, req
, key
, uid
, NULL
, NULL
, &ignore
);
634 readdgetnetgrent (struct database_dyn
*db
, struct hashentry
*he
,
642 struct dataset
*ignore
;
644 return addgetnetgrentX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
,
650 addinnetgr (struct database_dyn
*db
, int fd
, request_header
*req
,
651 void *key
, uid_t uid
)
653 addinnetgrX (db
, fd
, req
, key
, uid
, NULL
, NULL
);
658 readdinnetgr (struct database_dyn
*db
, struct hashentry
*he
,
667 return addinnetgrX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
);