1 /* Cache handling for netgroup lookup.
2 Copyright (C) 2011-2012 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 (data
.val
.triple
.host
> data
.val
.triple
.user
196 || data
.val
.triple
.user
> data
.val
.triple
.domain
)
198 const char *last
= MAX (nhost
,
199 MAX (nuser
, ndomain
));
200 size_t bufused
= (last
+ strlen (last
) + 1
203 /* We have to make temporary copies. */
204 size_t hostlen
= strlen (nhost
) + 1;
205 size_t userlen
= strlen (nuser
) + 1;
206 size_t domainlen
= strlen (ndomain
) + 1;
207 size_t needed
= hostlen
+ userlen
+ domainlen
;
209 if (buflen
- req
->key_len
- bufused
< needed
)
211 size_t newsize
= MAX (2 * buflen
,
212 buflen
+ 2 * needed
);
213 if (use_malloc
|| newsize
> 1024 * 1024)
216 char *newbuf
= xrealloc (use_malloc
225 extend_alloca (buffer
, buflen
, newsize
);
228 nhost
= memcpy (buffer
+ bufused
,
230 nuser
= memcpy ((char *) nhost
+ hostlen
,
232 ndomain
= memcpy ((char *) nuser
+ userlen
,
236 char *wp
= buffer
+ buffilled
;
237 wp
= stpcpy (wp
, nhost
) + 1;
238 wp
= stpcpy (wp
, nuser
) + 1;
239 wp
= stpcpy (wp
, ndomain
) + 1;
240 buffilled
= wp
- buffer
;
245 /* Check that the group has not been
247 struct name_list
*runp
= data
.needed_groups
;
251 if (strcmp (runp
->name
, data
.val
.group
) == 0)
255 if (runp
== data
.needed_groups
)
264 runp
= data
.known_groups
;
266 if (strcmp (runp
->name
, data
.val
.group
) == 0)
274 /* A new group is requested. */
275 size_t namelen
= strlen (data
.val
.group
) + 1;
276 struct name_list
*newg
= alloca (sizeof (*newg
)
278 memcpy (newg
->name
, data
.val
.group
, namelen
);
279 if (data
.needed_groups
== NULL
)
280 data
.needed_groups
= newg
->next
= newg
;
283 newg
->next
= data
.needed_groups
->next
;
284 data
.needed_groups
->next
= newg
;
285 data
.needed_groups
= newg
;
290 else if (status
== NSS_STATUS_UNAVAIL
&& e
== ERANGE
)
292 size_t newsize
= 2 * buflen
;
293 if (use_malloc
|| newsize
> 1024 * 1024)
296 char *newbuf
= xrealloc (use_malloc
297 ? buffer
: NULL
, buflen
);
303 extend_alloca (buffer
, buflen
, newsize
);
307 enum nss_status (*endfct
) (struct __netgrent
*);
308 endfct
= __nss_lookup_function (nip
, "endnetgrent");
310 (void) DL_CALL_FCT (*endfct
, (&data
));
315 no_more
= __nss_next2 (&nip
, "setnetgrent", NULL
, &setfct
.ptr
,
322 /* Fill in the dataset. */
323 dataset
= (struct dataset
*) buffer
;
324 dataset
->head
.allocsize
= total
+ req
->key_len
;
325 dataset
->head
.recsize
= total
- offsetof (struct dataset
, resp
);
326 dataset
->head
.notfound
= false;
327 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
328 dataset
->head
.usable
= true;
329 dataset
->head
.ttl
= db
->postimeout
;
330 timeout
= dataset
->head
.timeout
= time (NULL
) + dataset
->head
.ttl
;
332 dataset
->resp
.version
= NSCD_VERSION
;
333 dataset
->resp
.found
= 1;
334 dataset
->resp
.nresults
= nentries
;
335 dataset
->resp
.result_len
= buffilled
- sizeof (*dataset
);
337 assert (buflen
- buffilled
>= req
->key_len
);
338 key_copy
= memcpy (buffer
+ buffilled
, key
, req
->key_len
);
339 buffilled
+= req
->key_len
;
341 /* Now we can determine whether on refill we have to create a new
347 if (dataset
->head
.allocsize
== dh
->allocsize
348 && dataset
->head
.recsize
== dh
->recsize
349 && memcmp (&dataset
->resp
, dh
->data
,
350 dh
->allocsize
- offsetof (struct dataset
, resp
)) == 0)
352 /* The data has not changed. We will just bump the timeout
353 value. Note that the new record has been allocated on
354 the stack and need not be freed. */
355 dh
->timeout
= dataset
->head
.timeout
;
356 dh
->ttl
= dataset
->head
.ttl
;
358 dataset
= (struct dataset
*) dh
;
366 = (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
, 1);
367 if (__builtin_expect (newp
!= NULL
, 1))
369 /* Adjust pointer into the memory block. */
370 key_copy
= (char *) newp
+ (key_copy
- buffer
);
372 dataset
= memcpy (newp
, dataset
, total
+ req
->key_len
);
376 /* Mark the old record as obsolete. */
381 if (he
== NULL
&& fd
!= -1)
383 /* We write the dataset before inserting it to the database
384 since while inserting this thread might block and so would
385 unnecessarily let the receiver wait. */
388 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
390 assert (db
->wr_fd
!= -1);
391 assert ((char *) &dataset
->resp
> (char *) db
->data
);
392 assert ((char *) dataset
- (char *) db
->head
+ total
393 <= (sizeof (struct database_pers_head
)
394 + db
->head
->module
* sizeof (ref_t
)
395 + db
->head
->data_size
));
396 # ifndef __ASSUME_SENDFILE
399 sendfileall (fd
, db
->wr_fd
, (char *) &dataset
->resp
400 - (char *) db
->head
, dataset
->head
.recsize
);
401 # ifndef __ASSUME_SENDFILE
402 if (written
== -1 && errno
== ENOSYS
)
409 #if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
412 writeall (fd
, &dataset
->resp
, dataset
->head
.recsize
);
418 /* If necessary, we also propagate the data to disk. */
422 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
423 msync ((void *) pval
,
424 ((uintptr_t) dataset
& pagesize_m1
) + total
+ req
->key_len
,
428 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
429 true, db
, uid
, he
== NULL
);
431 pthread_rwlock_unlock (&db
->lock
);
433 /* Mark the old entry as obsolete. */
449 addinnetgrX (struct database_dyn
*db
, int fd
, request_header
*req
,
450 char *key
, uid_t uid
, struct hashentry
*he
,
453 const char *group
= key
;
454 key
= (char *) rawmemchr (key
, '\0') + 1;
455 size_t group_len
= key
- group
- 1;
456 const char *host
= *key
++ ? key
: NULL
;
458 key
= (char *) rawmemchr (key
, '\0') + 1;
459 const char *user
= *key
++ ? key
: NULL
;
461 key
= (char *) rawmemchr (key
, '\0') + 1;
462 const char *domain
= *key
++ ? key
: NULL
;
464 if (__builtin_expect (debug_level
> 0, 0))
467 dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
468 group
, host
?: "", user
?: "", domain
?: "");
470 dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
471 group
, host
?: "", user
?: "", domain
?: "");
474 struct dataset
*result
= (struct dataset
*) cache_search (GETNETGRENT
,
479 timeout
= result
->head
.timeout
;
482 request_header req_get
=
487 timeout
= addgetnetgrentX (db
, -1, &req_get
, group
, uid
, NULL
, NULL
,
493 struct datahead head
;
494 innetgroup_response_header resp
;
496 = (struct indataset
*) mempool_alloc (db
,
497 sizeof (*dataset
) + req
->key_len
,
499 struct indataset dataset_mem
;
500 bool cacheable
= true;
501 if (__builtin_expect (dataset
== NULL
, 0))
504 dataset
= &dataset_mem
;
507 dataset
->head
.allocsize
= sizeof (*dataset
) + req
->key_len
;
508 dataset
->head
.recsize
= sizeof (innetgroup_response_header
);
509 dataset
->head
.notfound
= result
->head
.notfound
;
510 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
511 dataset
->head
.usable
= true;
512 dataset
->head
.ttl
= result
->head
.ttl
;
513 dataset
->head
.timeout
= timeout
;
515 dataset
->resp
.version
= NSCD_VERSION
;
516 dataset
->resp
.found
= result
->resp
.found
;
517 /* Until we find a matching entry the result is 0. */
518 dataset
->resp
.result
= 0;
520 char *key_copy
= memcpy ((char *) (dataset
+ 1), group
, req
->key_len
);
522 if (dataset
->resp
.found
)
524 const char *triplets
= (const char *) (&result
->resp
+ 1);
526 for (nscd_ssize_t i
= result
->resp
.nresults
; i
> 0; --i
)
531 success
= strcmp (host
, triplets
) == 0;
532 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
534 if (success
&& user
!= NULL
)
535 success
= strcmp (user
, triplets
) == 0;
536 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
538 if (success
&& (domain
== NULL
|| strcmp (domain
, triplets
) == 0))
540 dataset
->resp
.result
= 1;
543 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
547 if (he
!= NULL
&& dh
->data
[0].innetgroupdata
.result
== dataset
->resp
.result
)
549 /* The data has not changed. We will just bump the timeout
550 value. Note that the new record has been allocated on
551 the stack and need not be freed. */
552 dh
->timeout
= timeout
;
553 dh
->ttl
= dataset
->head
.ttl
;
560 /* We write the dataset before inserting it to the database
561 since while inserting this thread might block and so would
562 unnecessarily let the receiver wait. */
566 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
568 assert (db
->wr_fd
!= -1);
569 assert ((char *) &dataset
->resp
> (char *) db
->data
);
570 assert ((char *) dataset
- (char *) db
->head
+ sizeof (*dataset
)
571 <= (sizeof (struct database_pers_head
)
572 + db
->head
->module
* sizeof (ref_t
)
573 + db
->head
->data_size
));
574 # ifndef __ASSUME_SENDFILE
577 sendfileall (fd
, db
->wr_fd
,
578 (char *) &dataset
->resp
- (char *) db
->head
,
579 sizeof (innetgroup_response_header
));
580 # ifndef __ASSUME_SENDFILE
581 if (written
== -1 && errno
== ENOSYS
)
587 # ifndef __ASSUME_SENDFILE
591 writeall (fd
, &dataset
->resp
, sizeof (innetgroup_response_header
));
597 /* If necessary, we also propagate the data to disk. */
601 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
602 msync ((void *) pval
,
603 ((uintptr_t) dataset
& pagesize_m1
) + sizeof (*dataset
)
608 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
609 true, db
, uid
, he
== NULL
);
611 pthread_rwlock_unlock (&db
->lock
);
613 /* Mark the old entry as obsolete. */
623 addgetnetgrent (struct database_dyn
*db
, int fd
, request_header
*req
,
624 void *key
, uid_t uid
)
626 struct dataset
*ignore
;
628 addgetnetgrentX (db
, fd
, req
, key
, uid
, NULL
, NULL
, &ignore
);
633 readdgetnetgrent (struct database_dyn
*db
, struct hashentry
*he
,
641 struct dataset
*ignore
;
643 return addgetnetgrentX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
,
649 addinnetgr (struct database_dyn
*db
, int fd
, request_header
*req
,
650 void *key
, uid_t uid
)
652 addinnetgrX (db
, fd
, req
, key
, uid
, NULL
, NULL
);
657 readdinnetgr (struct database_dyn
*db
, struct hashentry
*he
,
666 return addinnetgrX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
);