Implement r/o sharing of nscd's cache with client processes via shared memory.
[kopensolaris-gnu/glibc.git] / nscd / nscd_getgr_r.c
1 /* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004
2    Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library 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 GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <grp.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <sys/socket.h>
31 #include <sys/uio.h>
32 #include <sys/un.h>
33 #include <not-cancel.h>
34 #include <stdio-common/_itoa.h>
35
36 #include "nscd-client.h"
37 #include "nscd_proto.h"
38
39 int __nss_not_use_nscd_group;
40
41 static int nscd_getgr_r (const char *key, size_t keylen, request_type type,
42                          struct group *resultbuf, char *buffer,
43                          size_t buflen, struct group **result)
44      internal_function;
45
46
47 int
48 __nscd_getgrnam_r (const char *name, struct group *resultbuf, char *buffer,
49                    size_t buflen, struct group **result)
50 {
51   return nscd_getgr_r (name, strlen (name) + 1, GETGRBYNAME, resultbuf,
52                        buffer, buflen, result);
53 }
54
55
56 int
57 __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
58                    size_t buflen, struct group **result)
59 {
60   char buf[3 * sizeof (gid_t)];
61   buf[sizeof (buf) - 1] = '\0';
62   char *cp = _itoa_word (gid, buf + sizeof (buf) - 1, 10, 0);
63
64   return nscd_getgr_r (cp, buf + sizeof (buf) - cp, GETGRBYGID, resultbuf,
65                        buffer, buflen, result);
66 }
67
68
69 libc_locked_map_ptr (map_handle);
70 /* Note that we only free the structure if necessary.  The memory
71    mapping is not removed since it is not visible to the malloc
72    handling.  */
73 libc_freeres_fn (gr_map_free)
74 {
75
76   if (map_handle.mapped != NO_MAPPING)
77     free (map_handle.mapped);
78 }
79
80
81 static int
82 internal_function
83 nscd_getgr_r (const char *key, size_t keylen, request_type type,
84               struct group *resultbuf, char *buffer, size_t buflen,
85               struct group **result)
86 {
87   const gr_response_header *gr_resp = NULL;
88   const uint32_t *len = NULL;
89   const char *gr_name = NULL;
90   size_t gr_name_len = 0;
91   int retval = -1;
92   int gc_cycle;
93   const char *recend = (const char *) ~UINTMAX_C (0);
94
95   /* If the mapping is available, try to search there instead of
96      communicating with the nscd.  */
97   struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
98                                                        &map_handle, &gc_cycle);
99  retry:
100   if (mapped != NO_MAPPING)
101     {
102       const struct datahead *found = __nscd_cache_search (type, key, keylen,
103                                                           mapped);
104       if (found != NULL)
105         {
106           gr_resp = &found->data[0].grdata;
107           len = (const uint32_t *) (gr_resp + 1);
108           /* The alignment is always sufficient.  */
109           assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
110           gr_name = ((const char *) len
111                      + gr_resp->gr_mem_cnt * sizeof (uint32_t));
112           gr_name_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
113           recend = (const char *) found->data + found->recsize;
114         }
115     }
116
117   gr_response_header gr_resp_mem;
118   int sock = -1;
119   if (gr_resp == NULL)
120     {
121       sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
122                                  sizeof (gr_resp_mem));
123       if (sock == -1)
124         {
125           __nss_not_use_nscd_group = 1;
126           goto out;
127         }
128
129       gr_resp = &gr_resp_mem;
130     }
131
132   /* No value found so far.  */
133   *result = NULL;
134
135   if (__builtin_expect (gr_resp->found == -1, 0))
136     {
137       /* The daemon does not cache this database.  */
138       __nss_not_use_nscd_group = 1;
139       goto out_close;
140     }
141
142   if (gr_resp->found == 1)
143     {
144       struct iovec vec[2];
145       char *p = buffer;
146       size_t total_len;
147       uintptr_t align;
148       nscd_ssize_t cnt;
149
150       /* Now allocate the buffer the array for the group members.  We must
151          align the pointer.  */
152       align = ((__alignof__ (char *) - (p - ((char *) 0)))
153                & (__alignof__ (char *) - 1));
154       total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *)
155                    + gr_resp->gr_name_len + gr_resp->gr_passwd_len);
156       if (__builtin_expect (buflen < total_len, 0))
157         {
158         no_room:
159           __set_errno (ERANGE);
160           retval = ERANGE;
161           goto out_close;
162         }
163       buflen -= total_len;
164
165       p += align;
166       resultbuf->gr_mem = (char **) p;
167       p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *);
168
169       /* Set pointers for strings.  */
170       resultbuf->gr_name = p;
171       p += gr_resp->gr_name_len;
172       resultbuf->gr_passwd = p;
173       p += gr_resp->gr_passwd_len;
174
175       /* Fill in what we know now.  */
176       resultbuf->gr_gid = gr_resp->gr_gid;
177
178       /* Read the length information, group name, and password.  */
179       if (len == NULL)
180         {
181           /* Allocate array to store lengths.  */
182           len = (uint32_t *) alloca (gr_resp->gr_mem_cnt * sizeof (uint32_t));
183
184           vec[0].iov_base = (void *) len;
185           vec[0].iov_len = gr_resp->gr_mem_cnt * sizeof (uint32_t);
186           vec[1].iov_base = resultbuf->gr_name;
187           vec[1].iov_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
188           total_len = vec[0].iov_len + vec[1].iov_len;
189
190           /* Get this data.  */
191           size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
192           if (__builtin_expect (n != total_len, 0))
193             goto out_close;
194         }
195       else
196         /* We already have the data.  Just copy the group name and
197            password.  */
198         memcpy (resultbuf->gr_name, gr_name, gr_name_len);
199
200       /* Clear the terminating entry.  */
201       resultbuf->gr_mem[gr_resp->gr_mem_cnt] = NULL;
202
203       /* Prepare reading the group members.  */
204       total_len = 0;
205       for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
206         {
207           resultbuf->gr_mem[cnt] = p;
208           total_len += len[cnt];
209           p += len[cnt];
210         }
211
212       if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
213         goto out_close;
214       if (__builtin_expect (total_len > buflen, 0))
215         goto no_room;
216
217       retval = 0;
218       if (gr_name == NULL)
219         {
220           size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
221                                                  total_len));
222           if (__builtin_expect (n != total_len, 0))
223             {
224               /* The `errno' to some value != ERANGE.  */
225               __set_errno (ENOENT);
226               retval = ENOENT;
227             }
228           else
229             *result = resultbuf;
230         }
231       else
232         {
233           /* Copy the group member names.  */
234           memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
235
236           *result = resultbuf;
237         }
238     }
239   else
240     {
241       /* The `errno' to some value != ERANGE.  */
242       __set_errno (ENOENT);
243       /* Even though we have not found anything, the result is zero.  */
244       retval = 0;
245     }
246
247  out_close:
248   if (sock != -1)
249     close_not_cancel_no_status (sock);
250  out:
251   if (__nscd_drop_map_ref (mapped, gc_cycle) != 0)
252     /* When we come here this means there has been a GC cycle while we
253        were looking for the data.  This means the data might have been
254        inconsistent.  Retry.  */
255     goto retry;
256
257   return retval;
258 }