2005-12-27 Roland McGrath <roland@redhat.com>
[glibc.git] / hurd / lookup-retry.c
blob1f53f911a78b6da0bfbd2ac6d2755d6315d3cc7b
1 /* hairy bits of Hurd file name lookup
2 Copyright (C) 1992,1993,1994,1995,1996,1997,1999,2001,2002,2003
3 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
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.
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.
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. */
21 #include <hurd.h>
22 #include <hurd/lookup.h>
23 #include <hurd/term.h>
24 #include <hurd/paths.h>
25 #include <limits.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include "stdio-common/_itoa.h"
30 /* Translate the error from dir_lookup into the error the user sees. */
31 static inline error_t
32 lookup_error (error_t error)
34 switch (error)
36 case EOPNOTSUPP:
37 case MIG_BAD_ID:
38 /* These indicate that the server does not understand dir_lookup
39 at all. If it were a directory, it would, by definition. */
40 return ENOTDIR;
41 default:
42 return error;
46 error_t
47 __hurd_file_name_lookup_retry (error_t (*use_init_port)
48 (int which, error_t (*operate) (file_t)),
49 file_t (*get_dtable_port) (int fd),
50 error_t (*lookup)
51 (file_t dir, char *name,
52 int flags, mode_t mode,
53 retry_type *do_retry, string_t retry_name,
54 mach_port_t *result),
55 enum retry_type doretry,
56 char retryname[1024],
57 int flags, mode_t mode,
58 file_t *result)
60 error_t err;
61 char *file_name;
62 int nloops;
64 error_t lookup_op (file_t startdir)
66 while (file_name[0] == '/')
67 file_name++;
69 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
70 &doretry, retryname, result));
72 error_t reauthenticate (file_t unauth)
74 error_t err;
75 mach_port_t ref = __mach_reply_port ();
76 error_t reauth (auth_t auth)
78 return __auth_user_authenticate (auth, ref,
79 MACH_MSG_TYPE_MAKE_SEND,
80 result);
82 err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
83 if (! err)
84 err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
85 __mach_port_destroy (__mach_task_self (), ref);
86 __mach_port_deallocate (__mach_task_self (), unauth);
87 return err;
90 if (! lookup)
91 lookup = __dir_lookup;
93 nloops = 0;
94 err = 0;
97 file_t startdir = MACH_PORT_NULL;
98 int dirport = INIT_PORT_CWDIR;
100 switch (doretry)
102 case FS_RETRY_REAUTH:
103 if (err = reauthenticate (*result))
104 return err;
105 /* Fall through. */
107 case FS_RETRY_NORMAL:
108 if (nloops++ >= SYMLOOP_MAX)
110 __mach_port_deallocate (__mach_task_self (), *result);
111 return ELOOP;
114 /* An empty RETRYNAME indicates we have the final port. */
115 if (retryname[0] == '\0' &&
116 /* If reauth'd, we must do one more retry on "" to give the new
117 translator a chance to make a new port for us. */
118 doretry == FS_RETRY_NORMAL)
120 if (flags & O_NOFOLLOW)
122 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
123 did an O_NOLINK lookup above and io_stat here to check
124 for S_IFLNK, a translator like firmlink could easily
125 spoof this check by not showing S_IFLNK, but in fact
126 redirecting the lookup to some other name
127 (i.e. opening the very same holes a symlink would).
129 Instead we do an O_NOTRANS lookup above, and stat the
130 underlying node: if it has a translator set, and its
131 owner is not root (st_uid 0) then we reject it.
132 Since the motivation for this feature is security, and
133 that security presumes we trust the containing
134 directory, this check approximates the security of
135 refusing symlinks while accepting mount points.
136 Note that we actually permit something Linux doesn't:
137 we follow root-owned symlinks; if that is deemed
138 undesireable, we can add a final check for that
139 one exception to our general translator-based rule. */
140 struct stat64 st;
141 err = __io_stat (*result, &st);
142 if (!err
143 && (st.st_mode & (S_IPTRANS|S_IATRANS)))
145 if (st.st_uid != 0)
146 err = ENOENT;
147 else if (st.st_mode & S_IPTRANS)
149 char buf[1024];
150 char *trans = buf;
151 size_t translen = sizeof buf;
152 err = __file_get_translator (*result,
153 &trans, &translen);
154 if (!err
155 && translen > sizeof _HURD_SYMLINK
156 && !memcmp (trans,
157 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
158 err = ENOENT;
163 /* We got a successful translation. Now apply any open-time
164 action flags we were passed. */
166 if (!err && (flags & O_TRUNC)) /* Asked to truncate the file. */
167 err = __file_set_size (*result, 0);
169 if (err)
170 __mach_port_deallocate (__mach_task_self (), *result);
171 return err;
174 startdir = *result;
175 file_name = retryname;
176 break;
178 case FS_RETRY_MAGICAL:
179 switch (retryname[0])
181 case '/':
182 dirport = INIT_PORT_CRDIR;
183 if (*result != MACH_PORT_NULL)
184 __mach_port_deallocate (__mach_task_self (), *result);
185 if (nloops++ >= SYMLOOP_MAX)
186 return ELOOP;
187 file_name = &retryname[1];
188 break;
190 case 'f':
191 if (retryname[1] == 'd' && retryname[2] == '/')
193 int fd;
194 char *end;
195 int save = errno;
196 errno = 0;
197 fd = (int) strtoul (&retryname[3], &end, 10);
198 if (end == NULL || errno || /* Malformed number. */
199 /* Check for excess text after the number. A slash
200 is valid; it ends the component. Anything else
201 does not name a numeric file descriptor. */
202 (*end != '/' && *end != '\0'))
204 errno = save;
205 return ENOENT;
207 if (! get_dtable_port)
208 err = EGRATUITOUS;
209 else
211 *result = (*get_dtable_port) (fd);
212 if (*result == MACH_PORT_NULL)
214 /* If the name was a proper number, but the file
215 descriptor does not exist, we return EBADF instead
216 of ENOENT. */
217 err = errno;
218 errno = save;
221 errno = save;
222 if (err)
223 return err;
224 if (*end == '\0')
225 return 0;
226 else
228 /* Do a normal retry on the remaining components. */
229 startdir = *result;
230 file_name = end + 1; /* Skip the slash. */
231 break;
234 else
235 goto bad_magic;
236 break;
238 case 'm':
239 if (retryname[1] == 'a' && retryname[2] == 'c' &&
240 retryname[3] == 'h' && retryname[4] == 't' &&
241 retryname[5] == 'y' && retryname[6] == 'p' &&
242 retryname[7] == 'e')
244 error_t err;
245 struct host_basic_info hostinfo;
246 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
247 char *p;
248 /* XXX want client's host */
249 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
250 (natural_t *) &hostinfo,
251 &hostinfocnt))
252 return err;
253 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
254 return EGRATUITOUS;
255 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
256 *--p = '/';
257 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
258 if (p < retryname)
259 abort (); /* XXX write this right if this ever happens */
260 if (p > retryname)
261 strcpy (retryname, p);
262 startdir = *result;
264 else
265 goto bad_magic;
266 break;
268 case 't':
269 if (retryname[1] == 't' && retryname[2] == 'y')
270 switch (retryname[3])
272 error_t opentty (file_t *result)
274 error_t err;
275 error_t ctty_open (file_t port)
277 if (port == MACH_PORT_NULL)
278 return ENXIO; /* No controlling terminal. */
279 return __termctty_open_terminal (port,
280 flags,
281 result);
283 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
284 if (! err)
285 err = reauthenticate (*result);
286 return err;
289 case '\0':
290 return opentty (result);
291 case '/':
292 if (err = opentty (&startdir))
293 return err;
294 strcpy (retryname, &retryname[4]);
295 break;
296 default:
297 goto bad_magic;
299 else
300 goto bad_magic;
301 break;
303 default:
304 bad_magic:
305 return EGRATUITOUS;
307 break;
309 default:
310 return EGRATUITOUS;
313 if (startdir != MACH_PORT_NULL)
315 err = lookup_op (startdir);
316 __mach_port_deallocate (__mach_task_self (), startdir);
317 startdir = MACH_PORT_NULL;
319 else
320 err = (*use_init_port) (dirport, &lookup_op);
321 } while (! err);
323 return err;
325 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)