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, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28 #include "../inet/netgroup.h"
32 # include <kernel-features.h>
36 /* This is the standard reply in case the service is disabled. */
37 static const netgroup_response_header disabled
=
39 .version
= NSCD_VERSION
,
45 /* This is the struct describing how to write this record. */
46 const struct iovec netgroup_iov_disabled
=
48 .iov_base
= (void *) &disabled
,
49 .iov_len
= sizeof (disabled
)
53 /* This is the standard reply in case we haven't found the dataset. */
54 static const netgroup_response_header notfound
=
56 .version
= NSCD_VERSION
,
66 netgroup_response_header resp
;
72 addgetnetgrentX (struct database_dyn
*db
, int fd
, request_header
*req
,
73 const char *key
, uid_t uid
, struct hashentry
*he
,
74 struct datahead
*dh
, struct dataset
**resultp
)
76 if (__builtin_expect (debug_level
> 0, 0))
79 dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key
);
81 dbg_log (_("Reloading \"%s\" in netgroup cache!"), key
);
84 static service_user
*netgroup_database
;
86 struct dataset
*dataset
;
87 bool cacheable
= false;
90 char *key_copy
= NULL
;
91 struct __netgrent data
;
92 size_t buflen
= MAX (1024, sizeof (*dataset
) + req
->key_len
);
93 size_t buffilled
= sizeof (*dataset
);
96 bool use_malloc
= false;
97 size_t group_len
= strlen (key
) + 1;
100 struct name_list elem
;
101 char mem
[sizeof (struct name_list
) + group_len
];
104 if (netgroup_database
== NULL
105 && __nss_database_lookup ("netgroup", NULL
, NULL
, &netgroup_database
))
107 /* No such service. */
108 total
= sizeof (notfound
);
109 timeout
= time (NULL
) + db
->negtimeout
;
112 TEMP_FAILURE_RETRY (send (fd
, ¬found
, total
, MSG_NOSIGNAL
));
114 dataset
= mempool_alloc (db
, sizeof (struct dataset
) + req
->key_len
, 1);
115 /* If we cannot permanently store the result, so be it. */
118 dataset
->head
.allocsize
= sizeof (struct dataset
) + req
->key_len
;
119 dataset
->head
.recsize
= total
;
120 dataset
->head
.notfound
= true;
121 dataset
->head
.nreloads
= 0;
122 dataset
->head
.usable
= true;
124 /* Compute the timeout time. */
125 timeout
= dataset
->head
.timeout
= time (NULL
) + db
->negtimeout
;
126 dataset
->head
.ttl
= db
->negtimeout
;
128 /* This is the reply. */
129 memcpy (&dataset
->resp
, ¬found
, total
);
131 /* Copy the key data. */
132 memcpy (dataset
->strdata
, key
, req
->key_len
);
140 memset (&data
, '\0', sizeof (data
));
141 buffer
= alloca (buflen
);
142 first_needed
.elem
.next
= &first_needed
.elem
;
143 memcpy (first_needed
.elem
.name
, key
, group_len
);
144 data
.needed_groups
= &first_needed
.elem
;
146 while (data
.needed_groups
!= NULL
)
148 /* Add the next group to the list of those which are known. */
149 struct name_list
*this_group
= data
.needed_groups
->next
;
150 if (this_group
== data
.needed_groups
)
151 data
.needed_groups
= NULL
;
153 data
.needed_groups
->next
= this_group
->next
;
154 this_group
->next
= data
.known_groups
;
155 data
.known_groups
= this_group
;
159 enum nss_status (*f
) (const char *, struct __netgrent
*);
163 service_user
*nip
= netgroup_database
;
164 int no_more
= __nss_lookup (&nip
, "setnetgrent", NULL
, &setfct
.ptr
);
167 enum nss_status status
168 = DL_CALL_FCT (*setfct
.f
, (data
.known_groups
->name
, &data
));
170 if (status
== NSS_STATUS_SUCCESS
)
174 enum nss_status (*f
) (struct __netgrent
*, char *, size_t,
178 getfct
.ptr
= __nss_lookup_function (nip
, "getnetgrent_r");
179 if (getfct
.f
!= NULL
)
183 status
= getfct
.f (&data
, buffer
+ buffilled
,
184 buflen
- buffilled
, &e
);
185 if (status
== NSS_STATUS_RETURN
)
186 /* This was the last one for this group. Look
187 at next group if available. */
189 if (status
== NSS_STATUS_SUCCESS
)
191 if (data
.type
== triple_val
)
193 const char *nhost
= data
.val
.triple
.host
;
194 const char *nuser
= data
.val
.triple
.user
;
195 const char *ndomain
= data
.val
.triple
.domain
;
197 if (data
.val
.triple
.host
> data
.val
.triple
.user
198 || data
.val
.triple
.user
> data
.val
.triple
.domain
)
200 const char *last
= MAX (nhost
,
201 MAX (nuser
, ndomain
));
202 size_t bufused
= (last
+ strlen (last
) + 1
205 /* We have to make temporary copies. */
206 size_t hostlen
= strlen (nhost
) + 1;
207 size_t userlen
= strlen (nuser
) + 1;
208 size_t domainlen
= strlen (ndomain
) + 1;
209 size_t needed
= hostlen
+ userlen
+ domainlen
;
211 if (buflen
- req
->key_len
- bufused
< needed
)
213 size_t newsize
= MAX (2 * buflen
,
214 buflen
+ 2 * needed
);
215 if (use_malloc
|| newsize
> 1024 * 1024)
218 char *newbuf
= xrealloc (use_malloc
227 extend_alloca (buffer
, buflen
, newsize
);
230 nhost
= memcpy (buffer
+ bufused
,
232 nuser
= memcpy ((char *) nhost
+ hostlen
,
234 ndomain
= memcpy ((char *) nuser
+ userlen
,
238 char *wp
= buffer
+ buffilled
;
239 wp
= stpcpy (wp
, nhost
) + 1;
240 wp
= stpcpy (wp
, nuser
) + 1;
241 wp
= stpcpy (wp
, ndomain
) + 1;
242 buffilled
= wp
- buffer
;
247 /* Check that the group has not been
249 struct name_list
*runp
= data
.needed_groups
;
253 if (strcmp (runp
->name
, data
.val
.group
) == 0)
257 if (runp
== data
.needed_groups
)
266 runp
= data
.known_groups
;
268 if (strcmp (runp
->name
, data
.val
.group
) == 0)
276 /* A new group is requested. */
277 size_t namelen
= strlen (data
.val
.group
) + 1;
278 struct name_list
*newg
= alloca (sizeof (*newg
)
280 memcpy (newg
->name
, data
.val
.group
, namelen
);
281 if (data
.needed_groups
== NULL
)
282 data
.needed_groups
= newg
->next
= newg
;
285 newg
->next
= data
.needed_groups
->next
;
286 data
.needed_groups
->next
= newg
;
287 data
.needed_groups
= newg
;
292 else if (status
== NSS_STATUS_UNAVAIL
&& e
== ERANGE
)
294 size_t newsize
= 2 * buflen
;
295 if (use_malloc
|| newsize
> 1024 * 1024)
298 char *newbuf
= xrealloc (use_malloc
299 ? buffer
: NULL
, buflen
);
305 extend_alloca (buffer
, buflen
, newsize
);
309 enum nss_status (*endfct
) (struct __netgrent
*);
310 endfct
= __nss_lookup_function (nip
, "endnetgrent");
312 (void) DL_CALL_FCT (*endfct
, (&data
));
317 no_more
= __nss_next2 (&nip
, "setnetgrent", NULL
, &setfct
.ptr
,
324 /* Fill in the dataset. */
325 dataset
= (struct dataset
*) buffer
;
326 dataset
->head
.allocsize
= total
+ req
->key_len
;
327 dataset
->head
.recsize
= total
- offsetof (struct dataset
, resp
);
328 dataset
->head
.notfound
= false;
329 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
330 dataset
->head
.usable
= true;
331 dataset
->head
.ttl
= db
->postimeout
;
332 timeout
= dataset
->head
.timeout
= time (NULL
) + dataset
->head
.ttl
;
334 dataset
->resp
.version
= NSCD_VERSION
;
335 dataset
->resp
.found
= 1;
336 dataset
->resp
.nresults
= nentries
;
337 dataset
->resp
.result_len
= buffilled
- sizeof (*dataset
);
339 assert (buflen
- buffilled
>= req
->key_len
);
340 key_copy
= memcpy (buffer
+ buffilled
, key
, req
->key_len
);
341 buffilled
+= req
->key_len
;
343 /* Now we can determine whether on refill we have to create a new
349 if (dataset
->head
.allocsize
== dh
->allocsize
350 && dataset
->head
.recsize
== dh
->recsize
351 && memcmp (&dataset
->resp
, dh
->data
,
352 dh
->allocsize
- offsetof (struct dataset
, resp
)) == 0)
354 /* The data has not changed. We will just bump the timeout
355 value. Note that the new record has been allocated on
356 the stack and need not be freed. */
357 dh
->timeout
= dataset
->head
.timeout
;
358 dh
->ttl
= dataset
->head
.ttl
;
360 dataset
= (struct dataset
*) dh
;
368 = (struct dataset
*) mempool_alloc (db
, total
+ req
->key_len
, 1);
369 if (__builtin_expect (newp
!= NULL
, 1))
371 /* Adjust pointer into the memory block. */
372 key_copy
= (char *) newp
+ (key_copy
- buffer
);
374 dataset
= memcpy (newp
, dataset
, total
+ req
->key_len
);
378 /* Mark the old record as obsolete. */
383 if (he
== NULL
&& fd
!= -1)
385 /* We write the dataset before inserting it to the database
386 since while inserting this thread might block and so would
387 unnecessarily let the receiver wait. */
390 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
392 assert (db
->wr_fd
!= -1);
393 assert ((char *) &dataset
->resp
> (char *) db
->data
);
394 assert ((char *) dataset
- (char *) db
->head
+ total
395 <= (sizeof (struct database_pers_head
)
396 + db
->head
->module
* sizeof (ref_t
)
397 + db
->head
->data_size
));
398 # ifndef __ASSUME_SENDFILE
401 sendfileall (fd
, db
->wr_fd
, (char *) &dataset
->resp
402 - (char *) db
->head
, dataset
->head
.recsize
);
403 # ifndef __ASSUME_SENDFILE
404 if (written
== -1 && errno
== ENOSYS
)
411 #if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
414 writeall (fd
, &dataset
->resp
, dataset
->head
.recsize
);
420 /* If necessary, we also propagate the data to disk. */
424 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
425 msync ((void *) pval
,
426 ((uintptr_t) dataset
& pagesize_m1
) + total
+ req
->key_len
,
430 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
431 true, db
, uid
, he
== NULL
);
433 pthread_rwlock_unlock (&db
->lock
);
435 /* Mark the old entry as obsolete. */
451 addinnetgrX (struct database_dyn
*db
, int fd
, request_header
*req
,
452 char *key
, uid_t uid
, struct hashentry
*he
,
455 const char *group
= key
;
456 key
= (char *) rawmemchr (key
, '\0') + 1;
457 size_t group_len
= key
- group
- 1;
458 const char *host
= *key
++ ? key
: NULL
;
460 key
= (char *) rawmemchr (key
, '\0') + 1;
461 const char *user
= *key
++ ? key
: NULL
;
463 key
= (char *) rawmemchr (key
, '\0') + 1;
464 const char *domain
= *key
++ ? key
: NULL
;
466 if (__builtin_expect (debug_level
> 0, 0))
469 dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
470 group
, host
?: "", user
?: "", domain
?: "");
472 dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
473 group
, host
?: "", user
?: "", domain
?: "");
476 struct dataset
*result
= (struct dataset
*) cache_search (GETNETGRENT
,
481 timeout
= result
->head
.timeout
;
484 request_header req_get
=
489 timeout
= addgetnetgrentX (db
, -1, &req_get
, group
, uid
, NULL
, NULL
,
495 struct datahead head
;
496 innetgroup_response_header resp
;
498 = (struct indataset
*) mempool_alloc (db
,
499 sizeof (*dataset
) + req
->key_len
,
501 struct indataset dataset_mem
;
502 bool cacheable
= true;
503 if (__builtin_expect (dataset
== NULL
, 0))
506 dataset
= &dataset_mem
;
509 dataset
->head
.allocsize
= sizeof (*dataset
) + req
->key_len
;
510 dataset
->head
.recsize
= sizeof (innetgroup_response_header
);
511 dataset
->head
.notfound
= result
->head
.notfound
;
512 dataset
->head
.nreloads
= he
== NULL
? 0 : (dh
->nreloads
+ 1);
513 dataset
->head
.usable
= true;
514 dataset
->head
.ttl
= result
->head
.ttl
;
515 dataset
->head
.timeout
= timeout
;
517 dataset
->resp
.version
= NSCD_VERSION
;
518 dataset
->resp
.found
= result
->resp
.found
;
519 /* Until we find a matching entry the result is 0. */
520 dataset
->resp
.result
= 0;
522 char *key_copy
= memcpy ((char *) (dataset
+ 1), group
, req
->key_len
);
524 if (dataset
->resp
.found
)
526 const char *triplets
= (const char *) (&result
->resp
+ 1);
528 for (nscd_ssize_t i
= result
->resp
.nresults
; i
> 0; --i
)
533 success
= strcmp (host
, triplets
) == 0;
534 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
536 if (success
&& user
!= NULL
)
537 success
= strcmp (user
, triplets
) == 0;
538 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
540 if (success
&& (domain
== NULL
|| strcmp (domain
, triplets
) == 0))
542 dataset
->resp
.result
= 1;
545 triplets
= (const char *) rawmemchr (triplets
, '\0') + 1;
549 if (he
!= NULL
&& dh
->data
[0].innetgroupdata
.result
== dataset
->resp
.result
)
551 /* The data has not changed. We will just bump the timeout
552 value. Note that the new record has been allocated on
553 the stack and need not be freed. */
554 dh
->timeout
= timeout
;
555 dh
->ttl
= dataset
->head
.ttl
;
562 /* We write the dataset before inserting it to the database
563 since while inserting this thread might block and so would
564 unnecessarily let the receiver wait. */
568 if (__builtin_expect (db
->mmap_used
, 1) && cacheable
)
570 assert (db
->wr_fd
!= -1);
571 assert ((char *) &dataset
->resp
> (char *) db
->data
);
572 assert ((char *) dataset
- (char *) db
->head
+ sizeof (*dataset
)
573 <= (sizeof (struct database_pers_head
)
574 + db
->head
->module
* sizeof (ref_t
)
575 + db
->head
->data_size
));
576 # ifndef __ASSUME_SENDFILE
579 sendfileall (fd
, db
->wr_fd
,
580 (char *) &dataset
->resp
- (char *) db
->head
,
581 sizeof (innetgroup_response_header
));
582 # ifndef __ASSUME_SENDFILE
583 if (written
== -1 && errno
== ENOSYS
)
589 # ifndef __ASSUME_SENDFILE
593 writeall (fd
, &dataset
->resp
, sizeof (innetgroup_response_header
));
599 /* If necessary, we also propagate the data to disk. */
603 uintptr_t pval
= (uintptr_t) dataset
& ~pagesize_m1
;
604 msync ((void *) pval
,
605 ((uintptr_t) dataset
& pagesize_m1
) + sizeof (*dataset
)
610 (void) cache_add (req
->type
, key_copy
, req
->key_len
, &dataset
->head
,
611 true, db
, uid
, he
== NULL
);
613 pthread_rwlock_unlock (&db
->lock
);
615 /* Mark the old entry as obsolete. */
625 addgetnetgrent (struct database_dyn
*db
, int fd
, request_header
*req
,
626 void *key
, uid_t uid
)
628 struct dataset
*ignore
;
630 addgetnetgrentX (db
, fd
, req
, key
, uid
, NULL
, NULL
, &ignore
);
635 readdgetnetgrent (struct database_dyn
*db
, struct hashentry
*he
,
643 struct dataset
*ignore
;
645 return addgetnetgrentX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
,
651 addinnetgr (struct database_dyn
*db
, int fd
, request_header
*req
,
652 void *key
, uid_t uid
)
654 addinnetgrX (db
, fd
, req
, key
, uid
, NULL
, NULL
);
659 readdinnetgr (struct database_dyn
*db
, struct hashentry
*he
,
668 return addinnetgrX (db
, -1, &req
, db
->data
+ he
->key
, he
->owner
, he
, dh
);