Update.
[glibc.git] / hurd / hurdlookup.c
blob0714e4aa86e181337142c24bb6aec3bd6ebd1657
1 /* Copyright (C) 1992, 93, 94, 95, 96, 97, 99 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 #include <hurd.h>
20 #include <hurd/lookup.h>
21 #include <string.h>
22 #include <limits.h>
23 #include <fcntl.h>
24 #include "stdio-common/_itoa.h"
25 #include <hurd/term.h>
26 #include <hurd/paths.h>
29 /* Translate the error from dir_lookup into the error the user sees. */
30 static inline error_t
31 lookup_error (error_t error)
33 switch (error)
35 case EOPNOTSUPP:
36 case MIG_BAD_ID:
37 /* These indicate that the server does not understand dir_lookup
38 at all. If it were a directory, it would, by definition. */
39 return ENOTDIR;
40 default:
41 return error;
45 error_t
46 __hurd_file_name_lookup (error_t (*use_init_port)
47 (int which, error_t (*operate) (file_t)),
48 file_t (*get_dtable_port) (int fd),
49 error_t (*lookup)
50 (file_t dir, char *name, int flags, mode_t mode,
51 retry_type *do_retry, string_t retry_name,
52 mach_port_t *result),
53 const char *file_name, int flags, mode_t mode,
54 file_t *result)
56 error_t err;
57 enum retry_type doretry;
58 char retryname[1024]; /* XXX string_t LOSES! */
59 int startport;
61 error_t lookup_op (mach_port_t startdir)
63 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
64 &doretry, retryname, result));
67 if (! lookup)
68 lookup = __dir_lookup;
70 startport = (file_name[0] == '/') ? INIT_PORT_CRDIR : INIT_PORT_CWDIR;
71 while (file_name[0] == '/')
72 file_name++;
74 if (flags & O_NOFOLLOW) /* See comments below about O_NOFOLLOW. */
75 flags |= O_NOTRANS;
77 if (flags & O_DIRECTORY)
79 /* The caller wants to require that the file we look up is a directory.
80 We can do this without an extra RPC by appending a trailing slash
81 to the file name we look up. */
82 size_t len = strlen (file_name);
83 if (len == 0)
84 file_name = "/";
85 else if (file_name[len - 1] != '/')
87 char *n = alloca (len + 2);
88 memcpy (n, file_name, len);
89 n[len] = '/';
90 n[len + 1] = '\0';
91 file_name = n;
95 err = (*use_init_port) (startport, &lookup_op);
96 if (! err)
97 err = __hurd_file_name_lookup_retry (use_init_port, get_dtable_port,
98 lookup, doretry, retryname,
99 flags, mode, result);
101 return err;
103 weak_alias (__hurd_file_name_lookup, hurd_file_name_lookup)
105 error_t
106 __hurd_file_name_lookup_retry (error_t (*use_init_port)
107 (int which, error_t (*operate) (file_t)),
108 file_t (*get_dtable_port) (int fd),
109 error_t (*lookup)
110 (file_t dir, char *name,
111 int flags, mode_t mode,
112 retry_type *do_retry, string_t retry_name,
113 mach_port_t *result),
114 enum retry_type doretry,
115 char retryname[1024],
116 int flags, mode_t mode,
117 file_t *result)
119 error_t err;
120 char *file_name;
121 int nloops;
123 error_t lookup_op (file_t startdir)
125 while (file_name[0] == '/')
126 file_name++;
128 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
129 &doretry, retryname, result));
131 error_t reauthenticate (file_t unauth)
133 error_t err;
134 mach_port_t ref = __mach_reply_port ();
135 error_t reauth (auth_t auth)
137 return __auth_user_authenticate (auth, ref,
138 MACH_MSG_TYPE_MAKE_SEND,
139 result);
141 err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
142 if (! err)
143 err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
144 __mach_port_destroy (__mach_task_self (), ref);
145 __mach_port_deallocate (__mach_task_self (), unauth);
146 return err;
149 if (! lookup)
150 lookup = __dir_lookup;
152 nloops = 0;
153 err = 0;
156 file_t startdir = MACH_PORT_NULL;
157 int dirport = INIT_PORT_CWDIR;
159 switch (doretry)
161 case FS_RETRY_REAUTH:
162 if (err = reauthenticate (*result))
163 return err;
164 /* Fall through. */
166 case FS_RETRY_NORMAL:
167 #ifdef SYMLOOP_MAX
168 if (nloops++ >= SYMLOOP_MAX)
169 return ELOOP;
170 #endif
172 /* An empty RETRYNAME indicates we have the final port. */
173 if (retryname[0] == '\0' &&
174 /* If reauth'd, we must do one more retry on "" to give the new
175 translator a chance to make a new port for us. */
176 doretry == FS_RETRY_NORMAL)
178 if (flags & O_NOFOLLOW)
180 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
181 did an O_NOLINK lookup above and io_stat here to check
182 for S_IFLNK, a translator like firmlink could easily
183 spoof this check by not showing S_IFLNK, but in fact
184 redirecting the lookup to some other name
185 (i.e. opening the very same holes a symlink would).
187 Instead we do an O_NOTRANS lookup above, and stat the
188 underlying node: if it has a translator set, and its
189 owner is not root (st_uid 0) then we reject it.
190 Since the motivation for this feature is security, and
191 that security presumes we trust the containing
192 directory, this check approximates the security of
193 refusing symlinks while accepting mount points.
194 Note that we actually permit something Linux doesn't:
195 we follow root-owned symlinks; if that is deemed
196 undesireable, we can add a final check for that
197 one exception to our general translator-based rule. */
198 struct stat st;
199 err = __io_stat (*result, &st);
200 if (!err
201 && (st.st_mode & (S_IPTRANS|S_IATRANS)))
203 if (st.st_uid != 0)
204 err = ENOENT;
205 else if (st.st_mode & S_IPTRANS)
207 char buf[1024];
208 char *trans = buf;
209 size_t translen = sizeof buf;
210 err = __file_get_translator (*result,
211 &trans, &translen);
212 if (!err
213 && translen > sizeof _HURD_SYMLINK
214 && !memcmp (trans,
215 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
216 err = ENOENT;
221 /* We got a successful translation. Now apply any open-time
222 action flags we were passed. */
224 if (!err && (flags & O_TRUNC)) /* Asked to truncate the file. */
225 err = __file_set_size (*result, 0);
227 if (err)
228 __mach_port_deallocate (__mach_task_self (), *result);
229 return err;
232 startdir = *result;
233 file_name = retryname;
234 break;
236 case FS_RETRY_MAGICAL:
237 switch (retryname[0])
239 case '/':
240 dirport = INIT_PORT_CRDIR;
241 if (*result != MACH_PORT_NULL)
242 __mach_port_deallocate (__mach_task_self (), *result);
243 file_name = &retryname[1];
244 break;
246 case 'f':
247 if (retryname[1] == 'd' && retryname[2] == '/')
249 int fd;
250 char *end;
251 int save = errno;
252 errno = 0;
253 fd = (int) strtol (&retryname[3], &end, 10);
254 if (end == NULL || errno || /* Malformed number. */
255 /* Check for excess text after the number. A slash
256 is valid; it ends the component. Anything else
257 does not name a numeric file descriptor. */
258 (*end != '/' && *end != '\0'))
260 errno = save;
261 return ENOENT;
263 if (! get_dtable_port)
264 err = EGRATUITOUS;
265 else
267 *result = (*get_dtable_port) (fd);
268 if (*result == MACH_PORT_NULL)
270 /* If the name was a proper number, but the file
271 descriptor does not exist, we return EBADF instead
272 of ENOENT. */
273 err = errno;
274 errno = save;
277 errno = save;
278 if (err)
279 return err;
280 if (*end == '\0')
281 return 0;
282 else
284 /* Do a normal retry on the remaining components. */
285 startdir = *result;
286 file_name = end + 1; /* Skip the slash. */
287 break;
290 else
291 goto bad_magic;
292 break;
294 case 'm':
295 if (retryname[1] == 'a' && retryname[2] == 'c' &&
296 retryname[3] == 'h' && retryname[4] == 't' &&
297 retryname[5] == 'y' && retryname[6] == 'p' &&
298 retryname[7] == 'e')
300 error_t err;
301 struct host_basic_info hostinfo;
302 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
303 char *p;
304 /* XXX want client's host */
305 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
306 (natural_t *) &hostinfo,
307 &hostinfocnt))
308 return err;
309 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
310 return EGRATUITOUS;
311 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
312 *--p = '/';
313 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
314 if (p < retryname)
315 abort (); /* XXX write this right if this ever happens */
316 if (p > retryname)
317 strcpy (retryname, p);
318 startdir = *result;
320 else
321 goto bad_magic;
322 break;
324 case 't':
325 if (retryname[1] == 't' && retryname[2] == 'y')
326 switch (retryname[3])
328 error_t opentty (file_t *result)
330 error_t err;
331 error_t ctty_open (file_t port)
333 if (port == MACH_PORT_NULL)
334 return ENXIO; /* No controlling terminal. */
335 return __termctty_open_terminal (port,
336 flags,
337 result);
339 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
340 if (! err)
341 err = reauthenticate (*result);
342 return err;
345 case '\0':
346 return opentty (result);
347 case '/':
348 if (err = opentty (&startdir))
349 return err;
350 strcpy (retryname, &retryname[4]);
351 break;
352 default:
353 goto bad_magic;
355 else
356 goto bad_magic;
357 break;
359 default:
360 bad_magic:
361 return EGRATUITOUS;
363 break;
365 default:
366 return EGRATUITOUS;
369 if (startdir != MACH_PORT_NULL)
371 err = lookup_op (startdir);
372 __mach_port_deallocate (__mach_task_self (), startdir);
373 startdir = MACH_PORT_NULL;
375 else
376 err = (*use_init_port) (dirport, &lookup_op);
377 } while (! err);
379 return err;
381 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
383 error_t
384 __hurd_file_name_split (error_t (*use_init_port)
385 (int which, error_t (*operate) (file_t)),
386 file_t (*get_dtable_port) (int fd),
387 error_t (*lookup)
388 (file_t dir, char *name, int flags, mode_t mode,
389 retry_type *do_retry, string_t retry_name,
390 mach_port_t *result),
391 const char *file_name,
392 file_t *dir, char **name)
394 error_t addref (file_t crdir)
396 *dir = crdir;
397 return __mach_port_mod_refs (__mach_task_self (),
398 crdir, MACH_PORT_RIGHT_SEND, +1);
401 const char *lastslash = strrchr (file_name, '/');
403 if (lastslash != NULL)
405 if (lastslash == file_name)
407 /* "/foobar" => crdir + "foobar". */
408 *name = (char *) file_name + 1;
409 return (*use_init_port) (INIT_PORT_CRDIR, &addref);
411 else
413 /* "/dir1/dir2/.../file". */
414 char dirname[lastslash - file_name + 1];
415 memcpy (dirname, file_name, lastslash - file_name);
416 dirname[lastslash - file_name] = '\0';
417 *name = (char *) lastslash + 1;
418 return
419 __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
420 dirname, 0, 0, dir);
423 else
425 /* "foobar" => cwdir + "foobar". */
426 *name = (char *) file_name;
427 return (*use_init_port) (INIT_PORT_CWDIR, &addref);
430 weak_alias (__hurd_file_name_split, hurd_file_name_split)
433 file_t
434 __file_name_lookup (const char *file_name, int flags, mode_t mode)
436 error_t err;
437 file_t result;
439 err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
440 file_name, flags, mode & ~_hurd_umask,
441 &result);
443 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
445 weak_alias (__file_name_lookup, file_name_lookup)
448 file_t
449 __file_name_split (const char *file_name, char **name)
451 error_t err;
452 file_t result;
454 err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
455 file_name, &result, name);
457 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
459 weak_alias (__file_name_split, file_name_split)
462 file_t
463 __file_name_lookup_under (file_t startdir,
464 const char *file_name, int flags, mode_t mode)
466 error_t err;
467 file_t result;
469 error_t use_init_port (int which, error_t (*operate) (mach_port_t))
471 return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
472 _hurd_ports_use (which, operate));
475 err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
476 file_name, flags, mode & ~_hurd_umask,
477 &result);
479 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
481 weak_alias (__file_name_lookup_under, file_name_lookup_under)