* sysdeps/m68k/dl-machine.h (RTLD_START): Readd _dl_start_user
[glibc/pb-stable.git] / hurd / hurdlookup.c
blob7e5f9afa995fc796898e4f5e660494880dae60c8
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)
432 /* This is the same as hurd_file_name_split, except that it ignores
433 trailing slashes (so *NAME is never ""). */
434 error_t
435 __hurd_directory_name_split (error_t (*use_init_port)
436 (int which, error_t (*operate) (file_t)),
437 file_t (*get_dtable_port) (int fd),
438 error_t (*lookup)
439 (file_t dir, char *name, int flags, mode_t mode,
440 retry_type *do_retry, string_t retry_name,
441 mach_port_t *result),
442 const char *file_name,
443 file_t *dir, char **name)
445 error_t addref (file_t crdir)
447 *dir = crdir;
448 return __mach_port_mod_refs (__mach_task_self (),
449 crdir, MACH_PORT_RIGHT_SEND, +1);
452 const char *lastslash = strrchr (file_name, '/');
454 if (lastslash != NULL && lastslash[1] == '\0')
456 /* Trailing slash doesn't count. Look back further. */
458 /* Back up over all trailing slashes. */
459 while (lastslash > file_name && *lastslash == '/')
460 --lastslash;
462 /* Find the last one earlier in the string, before the trailing ones. */
463 #if __GLIBC__ > 2 || __GLIBC_MINOR__ >= 2
464 lastslash = __memrchr (file_name, '/', lastslash - file_name);
465 #else
466 /* Keep backing up, looking for a slash. */
468 if (lastslash == file_name)
470 /* Hit the start with no slash. */
471 lastslash = NULL;
472 break;
474 while (*lastslash-- != '/');
475 #endif
478 if (lastslash != NULL)
480 if (lastslash == file_name)
482 /* "/foobar" => crdir + "foobar". */
483 *name = (char *) file_name + 1;
484 return (*use_init_port) (INIT_PORT_CRDIR, &addref);
486 else
488 /* "/dir1/dir2/.../file". */
489 char dirname[lastslash - file_name + 1];
490 memcpy (dirname, file_name, lastslash - file_name);
491 dirname[lastslash - file_name] = '\0';
492 *name = (char *) lastslash + 1;
493 return
494 __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
495 dirname, 0, 0, dir);
498 else
500 /* "foobar" => cwdir + "foobar". */
501 *name = (char *) file_name;
502 return (*use_init_port) (INIT_PORT_CWDIR, &addref);
505 weak_alias (__hurd_directory_name_split, hurd_directory_name_split)
508 file_t
509 __file_name_lookup (const char *file_name, int flags, mode_t mode)
511 error_t err;
512 file_t result;
514 err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
515 file_name, flags, mode & ~_hurd_umask,
516 &result);
518 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
520 weak_alias (__file_name_lookup, file_name_lookup)
523 file_t
524 __file_name_split (const char *file_name, char **name)
526 error_t err;
527 file_t result;
529 err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
530 file_name, &result, name);
532 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
534 weak_alias (__file_name_split, file_name_split)
536 file_t
537 __directory_name_split (const char *directory_name, char **name)
539 error_t err;
540 file_t result;
542 err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0,
543 directory_name, &result, name);
545 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
547 weak_alias (__directory_name_split, directory_name_split)
550 file_t
551 __file_name_lookup_under (file_t startdir,
552 const char *file_name, int flags, mode_t mode)
554 error_t err;
555 file_t result;
557 error_t use_init_port (int which, error_t (*operate) (mach_port_t))
559 return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
560 _hurd_ports_use (which, operate));
563 err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
564 file_name, flags, mode & ~_hurd_umask,
565 &result);
567 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
569 weak_alias (__file_name_lookup_under, file_name_lookup_under)