Adjust for libc_locked_map_ptr change.
[kopensolaris-gnu/glibc.git] / nscd / nscd_getpw_r.c
1 /* Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <pwd.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <sys/socket.h>
30 #include <sys/uio.h>
31 #include <sys/un.h>
32 #include <not-cancel.h>
33 #include <stdio-common/_itoa.h>
34
35 #include "nscd-client.h"
36 #include "nscd_proto.h"
37
38 int __nss_not_use_nscd_passwd;
39
40 static int nscd_getpw_r (const char *key, size_t keylen, request_type type,
41                          struct passwd *resultbuf, char *buffer,
42                          size_t buflen, struct passwd **result)
43      internal_function;
44
45 int
46 __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer,
47                    size_t buflen, struct passwd **result)
48 {
49   if (name == NULL)
50     return -1;
51
52   return nscd_getpw_r (name, strlen (name) + 1, GETPWBYNAME, resultbuf,
53                        buffer, buflen, result);
54 }
55
56 int
57 __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
58                    size_t buflen, struct passwd **result)
59 {
60   char buf[3 * sizeof (uid_t)];
61   buf[sizeof (buf) - 1] = '\0';
62   char *cp = _itoa_word (uid, buf + sizeof (buf) - 1, 10, 0);
63
64   return nscd_getpw_r (cp, buf + sizeof (buf) - cp, GETPWBYUID, resultbuf,
65                        buffer, buflen, result);
66 }
67
68
69 libc_locked_map_ptr (static, 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 (pw_map_free)
74 {
75   if (map_handle.mapped != NO_MAPPING)
76     {
77       void *p = map_handle.mapped;
78       map_handle.mapped = NO_MAPPING;
79       free (p);
80     }
81 }
82
83
84 static int
85 internal_function
86 nscd_getpw_r (const char *key, size_t keylen, request_type type,
87               struct passwd *resultbuf, char *buffer, size_t buflen,
88               struct passwd **result)
89 {
90   int gc_cycle;
91   /* If the mapping is available, try to search there instead of
92      communicating with the nscd.  */
93   struct mapped_database *mapped;
94   mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
95
96  retry:;
97   const pw_response_header *pw_resp = NULL;
98   const char *pw_name = NULL;
99   int retval = -1;
100   const char *recend = (const char *) ~UINTMAX_C (0);
101
102   if (mapped != NO_MAPPING)
103     {
104       const struct datahead *found = __nscd_cache_search (type, key, keylen,
105                                                           mapped);
106       if (found != NULL)
107         {
108           pw_resp = &found->data[0].pwdata;
109           pw_name = (const char *) (pw_resp + 1);
110           recend = (const char *) found->data + found->recsize;
111         }
112     }
113
114   pw_response_header pw_resp_mem;
115   int sock = -1;
116   if (pw_resp == NULL)
117     {
118       sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
119                                  sizeof (pw_resp_mem));
120       if (sock == -1)
121         {
122           __nss_not_use_nscd_passwd = 1;
123           goto out;
124         }
125
126       pw_resp = &pw_resp_mem;
127     }
128
129   /* No value found so far.  */
130   *result = NULL;
131
132   if (__builtin_expect (pw_resp->found == -1, 0))
133     {
134       /* The daemon does not cache this database.  */
135       __nss_not_use_nscd_passwd = 1;
136       goto out_close;
137     }
138
139   if (pw_resp->found == 1)
140     {
141       /* Set the information we already have.  */
142       resultbuf->pw_uid = pw_resp->pw_uid;
143       resultbuf->pw_gid = pw_resp->pw_gid;
144
145       char *p = buffer;
146       /* get pw_name */
147       resultbuf->pw_name = p;
148       p += pw_resp->pw_name_len;
149       /* get pw_passwd */
150       resultbuf->pw_passwd = p;
151       p += pw_resp->pw_passwd_len;
152       /* get pw_gecos */
153       resultbuf->pw_gecos = p;
154       p += pw_resp->pw_gecos_len;
155       /* get pw_dir */
156       resultbuf->pw_dir = p;
157       p += pw_resp->pw_dir_len;
158       /* get pw_pshell */
159       resultbuf->pw_shell = p;
160       p += pw_resp->pw_shell_len;
161
162       ssize_t total = p - buffer;
163       if (__builtin_expect (pw_name + total > recend, 0))
164         goto out_close;
165       if (__builtin_expect (buflen < total, 0))
166         {
167           __set_errno (ERANGE);
168           retval = ERANGE;
169           goto out_close;
170         }
171
172       retval = 0;
173       if (pw_name == NULL)
174         {
175           ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
176
177           if (__builtin_expect (nbytes != total, 0))
178             {
179               /* The `errno' to some value != ERANGE.  */
180               __set_errno (ENOENT);
181               retval = ENOENT;
182             }
183           else
184             *result = resultbuf;
185         }
186       else
187         {
188           /* Copy the various strings.  */
189           memcpy (resultbuf->pw_name, pw_name, total);
190
191           /* Try to detect corrupt databases.  */
192           if (resultbuf->pw_name[pw_resp->pw_name_len - 1] != '\0'
193               || resultbuf->pw_passwd[pw_resp->pw_passwd_len - 1] != '\0'
194               || resultbuf->pw_gecos[pw_resp->pw_gecos_len - 1] != '\0'
195               || resultbuf->pw_dir[pw_resp->pw_dir_len - 1] != '\0'
196               || resultbuf->pw_shell[pw_resp->pw_shell_len - 1] != '\0')
197             {
198               /* We cannot use the database.  */
199               retval = -1;
200               goto out_close;
201             }
202
203           *result = resultbuf;
204         }
205     }
206   else
207     {
208       /* The `errno' to some value != ERANGE.  */
209       __set_errno (ENOENT);
210       /* Even though we have not found anything, the result is zero.  */
211       retval = 0;
212     }
213
214  out_close:
215   if (sock != -1)
216     close_not_cancel_no_status (sock);
217  out:
218   if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
219     {
220       /* When we come here this means there has been a GC cycle while we
221          were looking for the data.  This means the data might have been
222          inconsistent.  Retry if possible.  */
223       if ((gc_cycle & 1) != 0)
224         {
225           /* nscd is just running gc now.  Disable using the mapping.  */
226           __nscd_unmap (mapped);
227           mapped = NO_MAPPING;
228         }
229
230       free (resultbuf);
231
232       goto retry;
233     }
234
235   return retval;
236 }