1 /* Cache handling for netgroup lookup.
2 Copyright (C) 2011-2013 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>
34 /* This is the standard reply in case the service is disabled. */
35 static const netgroup_response_header disabled
=
37 .version
= NSCD_VERSION
,
43 /* This is the struct describing how to write this record. */
44 const struct iovec netgroup_iov_disabled
=
46 .iov_base
= (void *) &disabled
,
47 .iov_len
= sizeof (disabled
)
51 /* This is the standard reply in case we haven't found the dataset. */
52 static const netgroup_response_header notfound
=
54 .version
= NSCD_VERSION
,
64 netgroup_response_header resp
;
70 addgetnetgrentX (struct database_dyn
*db
, int fd
, request_header
*req
,
71 const char *key
, uid_t uid
, struct hashentry
*he
,
72 struct datahead
*dh
, struct dataset
**resultp
)
74 if (__builtin_expect (debug_level
> 0, 0))
77 dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key
);
79 dbg_log (_("Reloading \"%s\" in netgroup cache!"), key
);
82 static service_user
*netgroup_database
;
84 struct dataset
*dataset
;
85 bool cacheable
= false;
88 char *key_copy
= NULL
;
89 struct __netgrent data
;
90 size_t buflen
= MAX (1024, sizeof (*dataset
) + req
->key_len
);
91 size_t buffilled
= sizeof (*dataset
);
94 bool use_malloc
= false;
95 size_t group_len
= strlen (key
) + 1;
98 struct name_list elem
;
99 char mem
[sizeof (struct name_list
) + group_len
];
102 if (netgroup_database
== NULL
103 && __nss_database_lookup ("netgroup", NULL
, NULL
, &netgroup_database
))
105 /* No such service. */
106 total
= sizeof (notfound
);
107 timeout
= time (NULL
) + db
->negtimeout
;
110 TEMP_FAILURE_RETRY (send (fd
, ¬found
, total
, MSG_NOSIGNAL
));
112 dataset
= mempool_alloc (db
, sizeof (struct dataset
) + req
->key_len
, 1);
113 /* If we cannot permanently store the result, so be it. */
116 dataset
->head
.allocsize
= sizeof (struct dataset
) + req
->key_len
;
117 dataset
->head
.recsize
= total
;
118 dataset
->head
.notfound
= true;
119 dataset
->head
.nreloads
= 0;
120 dataset
->head
.usable
= true;
122 /* Compute the timeout time. */
123 timeout
= dataset
->head
.timeout
= time (NULL
) + db
->negtimeout
;
124 dataset
->head
.ttl
= db
->negtimeout
;
126 /* This is the reply. */
127 memcpy (&dataset
->resp
, ¬found
, total
);
129 /* Copy the key data. */
130 memcpy (dataset
->strdata
, key
, req
->key_len
);
138 memset (&data
, '\0', sizeof (data
));
139 buffer
= alloca (buflen
);
140 first_needed
.elem
.next
= &first_needed
.elem
;
141 memcpy (first_needed
.elem
.name
, key
, group_len
);
142 data
.needed_groups
= &first_needed
.elem
;
144 while (data
.needed_groups
!= NULL
)
146 /* Add the next group to the list of those which are known. */
147 struct name_list
*this_group
= data
.needed_groups
->next
;
148 if (this_group
== data
.needed_groups
)
149 data
.needed_groups
= NULL
;
151 data
.needed_groups
->next
= this_group
->next
;
152 this_group
->next
= data
.known_groups
;
153 data
.known_groups
= this_group
;
157 enum nss_status (*f
) (const char *, struct __netgrent
*);
161 service_user
*nip
= netgroup_database
;
162 int no_more
= __nss_lookup (&nip
, "setnetgrent", NULL
, &setfct
.ptr
);
165 enum nss_status status
166 = DL_CALL_FCT (*setfct
.f
, (data
.known_groups
->name
, &data
));
168 if (status
== NSS_STATUS_SUCCESS
)
172 enum nss_status (*f
) (struct __netgrent
*, char *, size_t,
176 getfct
.ptr
= __nss_lookup_function (nip
, "getnetgrent_r");
177 if (getfct
.f
!= NULL
)
181 status
= getfct
.f (&data
, buffer
+ buffilled
,
182 buflen
- buffilled
, &e
);
183 if (status
== NSS_STATUS_RETURN
)
184 /* This was the last one for this group. Look
185 at next group if available. */
187 if (status
== NSS_STATUS_SUCCESS
)
189 if (data
.type
== triple_val
)
191 const char *nhost
= data
.val
.triple
.host
;
192 const char *nuser
= data
.val
.triple
.user
;
193 const char *ndomain
= data
.val
.triple
.domain
;
195 if (nhost
== NULL
|| nuser
== NULL
|| ndomain
== NULL
196 || nhost
> nuser
|| nuser
> ndomain
)
198 const char *last
= nhost
;
200 || (nuser
!= NULL
&& nuser
> last
))
203 || (ndomain
!= NULL
&& ndomain
> last
))
209 : last
+ strlen (last
) + 1 - buffer
);
211 /* We have to make temporary copies. */
212 size_t hostlen
= strlen (nhost
?: "") + 1;
213 size_t userlen
= strlen (nuser
?: "") + 1;
214 size_t domainlen
= strlen (ndomain
?: "") + 1;
215 size_t needed
= hostlen
+ userlen
+ domainlen
;
217 if (buflen
- req
->key_len
- bufused
< needed
)
219 size_t newsize
= MAX (2 * buflen
,
220 buflen
+ 2 * needed
);
221 if (use_malloc
|| newsize
> 1024 * 1024)
224 char *newbuf
= xrealloc (use_malloc
233 extend_alloca (buffer
, buflen
, newsize
);
236 nhost
= memcpy (buffer
+ bufused
,
237 nhost
?: "", hostlen
);
238 nuser
= memcpy ((char *) nhost
+ hostlen
,
239 nuser
?: "", userlen
);
240 ndomain
= memcpy ((char *) nuser
+ userlen
,
241 ndomain
?: "", domainlen
);
244 char *wp
= buffer
+ buffilled
;
245 wp
= stpcpy (wp
, nhost
) + 1;
246 wp
= stpcpy (wp
, nuser
) + 1;
247 wp
= stpcpy (wp
, ndomain
) + 1;
248 buffilled
= wp
- buffer
;
253 /* Check that the group has not been
255 struct name_list
*runp
= data
.needed_groups
;
259 if (strcmp (runp
->name
, data
.val
.group
) == 0)
263 if (runp
== data
.needed_groups
)
272 runp
= data
.known_groups
;
274 if (strcmp (runp
->name
, data
.val
.group
) == 0)
282 /* A new group is requested. */
283 size_t namelen
= strlen (data
.val
.group
) + 1;
284 struct name_list
*newg
= alloca (sizeof (*newg
)
286 memcpy (newg
->name
, data
.val
.group
, namelen
);
287 if (data
.needed_groups
== NULL
)
288 data
.needed_groups
= newg
->next
= newg
;
291 newg
->next
= data
.needed_groups
->next
;
292 data
.needed_groups
->next
= newg
;
293 data
.needed_groups
= newg
;
298 else if (status
== NSS_STATUS_UNAVAIL
&& e
== ERANGE
)
300 size_t newsize
= 2 * buflen
;
301 if (use_malloc
|| newsize
> 1024 * 1024)
304 char *newbuf
= xrealloc (use_malloc
305 ? buffer
: NULL
, buflen
);
311 extend_alloca (buffer
, buflen
, newsize
);
315 enum nss_status (*endfct
) (struct __netgrent
*);
316 endfct
= __nss_lookup_function (nip
, "endnetgrent");
318 (void) DL_CALL_FCT (*endfct
, (&data
));
323 no_more
= __nss_next2 (&nip
, "setnetgrent", NULL
, &setfct
.ptr
,
330 /* Fill in the dataset. */
331 dataset
= (struct dataset
*) buffer
;
332 dataset
->head
.allocsize
= total
+ req
->key_len
;
333 dataset
->head
.recsize
= total
- offsetof (struct dataset
, resp
);
334 dataset
->head
.notfound
= false;
335 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
336 dataset
->head
.usable
= true;
337 dataset
->head
.ttl
= db
->postimeout
;
338 timeout
= dataset
->head
.timeout
= time (NULL
) + dataset
->head
.ttl
;
340 dataset
->resp
.version
= NSCD_VERSION
;
341 dataset
->resp
.found
= 1;
342 dataset
->resp
.nresults
= nentries
;
343 dataset
->resp
.result_len
= buffilled
- sizeof (*dataset
);
345 assert (buflen
- buffilled
>= req
->key_len
);
346 key_copy
= memcpy (buffer
+ buffilled
, key
, req
->key_len
);
347 buffilled
+= req
->key_len
;
349 /* Now we can determine whether on refill we have to create a new
355 if (dataset
->head
.allocsize
== dh
->allocsize
356 && dataset
->head
.recsize
== dh
->recsize
357 && memcmp (&dataset
->resp
, dh
->data
,
358 dh
->allocsize
- offsetof (struct dataset
, resp
)) == 0)
360 /* The data has not changed. We will just bump the timeout
361 value. Note that the new record has been allocated on
362 the stack and need not be freed. */
363 dh
->timeout
= dataset
->head
.timeout
;
364 dh
->ttl
= dataset
->head
.ttl
;
366 dataset
= (struct dataset
*) dh
;
374 = (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
, 1);
375 if (__builtin_expect (newp
!= NULL
, 1))
377 /* Adjust pointer into the memory block. */
378 key_copy
= (char *) newp
+ (key_copy
- buffer
);
380 dataset
= memcpy (newp
, dataset
, total
+ req
->key_len
);
384 /* Mark the old record as obsolete. */
389 if (he
== NULL
&& fd
!= -1)
391 /* We write the dataset before inserting it to the database
392 since while inserting this thread might block and so would
393 unnecessarily let the receiver wait. */
396 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
398 assert (db
->wr_fd
!= -1);
399 assert ((char *) &dataset
->resp
> (char *) db
->data
);
400 assert ((char *) dataset
- (char *) db
->head
+ total
401 <= (sizeof (struct database_pers_head
)
402 + db
->head
->module
* sizeof (ref_t
)
403 + db
->head
->data_size
));
404 # ifndef __ASSUME_SENDFILE
407 sendfileall (fd
, db
->wr_fd
, (char *) &dataset
->resp
408 - (char *) db
->head
, dataset
->head
.recsize
);
409 # ifndef __ASSUME_SENDFILE
410 if (written
== -1 && errno
== ENOSYS
)
417 #if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
420 writeall (fd
, &dataset
->resp
, dataset
->head
.recsize
);
426 /* If necessary, we also propagate the data to disk. */
430 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
431 msync ((void *) pval
,
432 ((uintptr_t) dataset
& pagesize_m1
) + total
+ req
->key_len
,
436 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
437 true, db
, uid
, he
== NULL
);
439 pthread_rwlock_unlock (&db
->lock
);
441 /* Mark the old entry as obsolete. */
457 addinnetgrX (struct database_dyn
*db
, int fd
, request_header
*req
,
458 char *key
, uid_t uid
, struct hashentry
*he
,
461 const char *group
= key
;
462 key
= (char *) rawmemchr (key
, '\0') + 1;
463 size_t group_len
= key
- group
- 1;
464 const char *host
= *key
++ ? key
: NULL
;
466 key
= (char *) rawmemchr (key
, '\0') + 1;
467 const char *user
= *key
++ ? key
: NULL
;
469 key
= (char *) rawmemchr (key
, '\0') + 1;
470 const char *domain
= *key
++ ? key
: NULL
;
472 if (__builtin_expect (debug_level
> 0, 0))
475 dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
476 group
, host
?: "", user
?: "", domain
?: "");
478 dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
479 group
, host
?: "", user
?: "", domain
?: "");
482 struct dataset
*result
= (struct dataset
*) cache_search (GETNETGRENT
,
487 timeout
= result
->head
.timeout
;
490 request_header req_get
=
495 timeout
= addgetnetgrentX (db
, -1, &req_get
, group
, uid
, NULL
, NULL
,
501 struct datahead head
;
502 innetgroup_response_header resp
;
504 = (struct indataset
*) mempool_alloc (db
,
505 sizeof (*dataset
) + req
->key_len
,
507 struct indataset dataset_mem
;
508 bool cacheable
= true;
509 if (__builtin_expect (dataset
== NULL
, 0))
512 dataset
= &dataset_mem
;
515 dataset
->head
.allocsize
= sizeof (*dataset
) + req
->key_len
;
516 dataset
->head
.recsize
= sizeof (innetgroup_response_header
);
517 dataset
->head
.notfound
= result
->head
.notfound
;
518 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
519 dataset
->head
.usable
= true;
520 dataset
->head
.ttl
= result
->head
.ttl
;
521 dataset
->head
.timeout
= timeout
;
523 dataset
->resp
.version
= NSCD_VERSION
;
524 dataset
->resp
.found
= result
->resp
.found
;
525 /* Until we find a matching entry the result is 0. */
526 dataset
->resp
.result
= 0;
528 char *key_copy
= memcpy ((char *) (dataset
+ 1), group
, req
->key_len
);
530 if (dataset
->resp
.found
)
532 const char *triplets
= (const char *) (&result
->resp
+ 1);
534 for (nscd_ssize_t i
= result
->resp
.nresults
; i
> 0; --i
)
539 success
= strcmp (host
, triplets
) == 0;
540 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
542 if (success
&& user
!= NULL
)
543 success
= strcmp (user
, triplets
) == 0;
544 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
546 if (success
&& (domain
== NULL
|| strcmp (domain
, triplets
) == 0))
548 dataset
->resp
.result
= 1;
551 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
555 if (he
!= NULL
&& dh
->data
[0].innetgroupdata
.result
== dataset
->resp
.result
)
557 /* The data has not changed. We will just bump the timeout
558 value. Note that the new record has been allocated on
559 the stack and need not be freed. */
560 dh
->timeout
= timeout
;
561 dh
->ttl
= dataset
->head
.ttl
;
568 /* We write the dataset before inserting it to the database
569 since while inserting this thread might block and so would
570 unnecessarily let the receiver wait. */
574 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
576 assert (db
->wr_fd
!= -1);
577 assert ((char *) &dataset
->resp
> (char *) db
->data
);
578 assert ((char *) dataset
- (char *) db
->head
+ sizeof (*dataset
)
579 <= (sizeof (struct database_pers_head
)
580 + db
->head
->module
* sizeof (ref_t
)
581 + db
->head
->data_size
));
582 # ifndef __ASSUME_SENDFILE
585 sendfileall (fd
, db
->wr_fd
,
586 (char *) &dataset
->resp
- (char *) db
->head
,
587 sizeof (innetgroup_response_header
));
588 # ifndef __ASSUME_SENDFILE
589 if (written
== -1 && errno
== ENOSYS
)
595 # ifndef __ASSUME_SENDFILE
599 writeall (fd
, &dataset
->resp
, sizeof (innetgroup_response_header
));
605 /* If necessary, we also propagate the data to disk. */
609 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
610 msync ((void *) pval
,
611 ((uintptr_t) dataset
& pagesize_m1
) + sizeof (*dataset
)
616 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
617 true, db
, uid
, he
== NULL
);
619 pthread_rwlock_unlock (&db
->lock
);
621 /* Mark the old entry as obsolete. */
631 addgetnetgrent (struct database_dyn
*db
, int fd
, request_header
*req
,
632 void *key
, uid_t uid
)
634 struct dataset
*ignore
;
636 addgetnetgrentX (db
, fd
, req
, key
, uid
, NULL
, NULL
, &ignore
);
641 readdgetnetgrent (struct database_dyn
*db
, struct hashentry
*he
,
649 struct dataset
*ignore
;
651 return addgetnetgrentX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
,
657 addinnetgr (struct database_dyn
*db
, int fd
, request_header
*req
,
658 void *key
, uid_t uid
)
660 addinnetgrX (db
, fd
, req
, key
, uid
, NULL
, NULL
);
665 readdinnetgr (struct database_dyn
*db
, struct hashentry
*he
,
674 return addinnetgrX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
);