Update.
[glibc.git] / hurd / hurdlookup.c
blob5a54cfb9d81e367329256c718e65261e220b8b92
1 /* Copyright (C) 1992,93,94,95,96,97,99,2001 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 if (nloops++ >= SYMLOOP_MAX)
169 __mach_port_deallocate (__mach_task_self (), *result);
170 return ELOOP;
173 /* An empty RETRYNAME indicates we have the final port. */
174 if (retryname[0] == '\0' &&
175 /* If reauth'd, we must do one more retry on "" to give the new
176 translator a chance to make a new port for us. */
177 doretry == FS_RETRY_NORMAL)
179 if (flags & O_NOFOLLOW)
181 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
182 did an O_NOLINK lookup above and io_stat here to check
183 for S_IFLNK, a translator like firmlink could easily
184 spoof this check by not showing S_IFLNK, but in fact
185 redirecting the lookup to some other name
186 (i.e. opening the very same holes a symlink would).
188 Instead we do an O_NOTRANS lookup above, and stat the
189 underlying node: if it has a translator set, and its
190 owner is not root (st_uid 0) then we reject it.
191 Since the motivation for this feature is security, and
192 that security presumes we trust the containing
193 directory, this check approximates the security of
194 refusing symlinks while accepting mount points.
195 Note that we actually permit something Linux doesn't:
196 we follow root-owned symlinks; if that is deemed
197 undesireable, we can add a final check for that
198 one exception to our general translator-based rule. */
199 struct stat st;
200 err = __io_stat (*result, &st);
201 if (!err
202 && (st.st_mode & (S_IPTRANS|S_IATRANS)))
204 if (st.st_uid != 0)
205 err = ENOENT;
206 else if (st.st_mode & S_IPTRANS)
208 char buf[1024];
209 char *trans = buf;
210 size_t translen = sizeof buf;
211 err = __file_get_translator (*result,
212 &trans, &translen);
213 if (!err
214 && translen > sizeof _HURD_SYMLINK
215 && !memcmp (trans,
216 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
217 err = ENOENT;
222 /* We got a successful translation. Now apply any open-time
223 action flags we were passed. */
225 if (!err && (flags & O_TRUNC)) /* Asked to truncate the file. */
226 err = __file_set_size (*result, 0);
228 if (err)
229 __mach_port_deallocate (__mach_task_self (), *result);
230 return err;
233 startdir = *result;
234 file_name = retryname;
235 break;
237 case FS_RETRY_MAGICAL:
238 switch (retryname[0])
240 case '/':
241 dirport = INIT_PORT_CRDIR;
242 if (*result != MACH_PORT_NULL)
243 __mach_port_deallocate (__mach_task_self (), *result);
244 if (nloops++ >= SYMLOOP_MAX)
245 return ELOOP;
246 file_name = &retryname[1];
247 break;
249 case 'f':
250 if (retryname[1] == 'd' && retryname[2] == '/')
252 int fd;
253 char *end;
254 int save = errno;
255 errno = 0;
256 fd = (int) strtol (&retryname[3], &end, 10);
257 if (end == NULL || errno || /* Malformed number. */
258 /* Check for excess text after the number. A slash
259 is valid; it ends the component. Anything else
260 does not name a numeric file descriptor. */
261 (*end != '/' && *end != '\0'))
263 errno = save;
264 return ENOENT;
266 if (! get_dtable_port)
267 err = EGRATUITOUS;
268 else
270 *result = (*get_dtable_port) (fd);
271 if (*result == MACH_PORT_NULL)
273 /* If the name was a proper number, but the file
274 descriptor does not exist, we return EBADF instead
275 of ENOENT. */
276 err = errno;
277 errno = save;
280 errno = save;
281 if (err)
282 return err;
283 if (*end == '\0')
284 return 0;
285 else
287 /* Do a normal retry on the remaining components. */
288 startdir = *result;
289 file_name = end + 1; /* Skip the slash. */
290 break;
293 else
294 goto bad_magic;
295 break;
297 case 'm':
298 if (retryname[1] == 'a' && retryname[2] == 'c' &&
299 retryname[3] == 'h' && retryname[4] == 't' &&
300 retryname[5] == 'y' && retryname[6] == 'p' &&
301 retryname[7] == 'e')
303 error_t err;
304 struct host_basic_info hostinfo;
305 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
306 char *p;
307 /* XXX want client's host */
308 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
309 (natural_t *) &hostinfo,
310 &hostinfocnt))
311 return err;
312 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
313 return EGRATUITOUS;
314 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
315 *--p = '/';
316 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
317 if (p < retryname)
318 abort (); /* XXX write this right if this ever happens */
319 if (p > retryname)
320 strcpy (retryname, p);
321 startdir = *result;
323 else
324 goto bad_magic;
325 break;
327 case 't':
328 if (retryname[1] == 't' && retryname[2] == 'y')
329 switch (retryname[3])
331 error_t opentty (file_t *result)
333 error_t err;
334 error_t ctty_open (file_t port)
336 if (port == MACH_PORT_NULL)
337 return ENXIO; /* No controlling terminal. */
338 return __termctty_open_terminal (port,
339 flags,
340 result);
342 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
343 if (! err)
344 err = reauthenticate (*result);
345 return err;
348 case '\0':
349 return opentty (result);
350 case '/':
351 if (err = opentty (&startdir))
352 return err;
353 strcpy (retryname, &retryname[4]);
354 break;
355 default:
356 goto bad_magic;
358 else
359 goto bad_magic;
360 break;
362 default:
363 bad_magic:
364 return EGRATUITOUS;
366 break;
368 default:
369 return EGRATUITOUS;
372 if (startdir != MACH_PORT_NULL)
374 err = lookup_op (startdir);
375 __mach_port_deallocate (__mach_task_self (), startdir);
376 startdir = MACH_PORT_NULL;
378 else
379 err = (*use_init_port) (dirport, &lookup_op);
380 } while (! err);
382 return err;
384 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
386 error_t
387 __hurd_file_name_split (error_t (*use_init_port)
388 (int which, error_t (*operate) (file_t)),
389 file_t (*get_dtable_port) (int fd),
390 error_t (*lookup)
391 (file_t dir, char *name, int flags, mode_t mode,
392 retry_type *do_retry, string_t retry_name,
393 mach_port_t *result),
394 const char *file_name,
395 file_t *dir, char **name)
397 error_t addref (file_t crdir)
399 *dir = crdir;
400 return __mach_port_mod_refs (__mach_task_self (),
401 crdir, MACH_PORT_RIGHT_SEND, +1);
404 const char *lastslash = strrchr (file_name, '/');
406 if (lastslash != NULL)
408 if (lastslash == file_name)
410 /* "/foobar" => crdir + "foobar". */
411 *name = (char *) file_name + 1;
412 return (*use_init_port) (INIT_PORT_CRDIR, &addref);
414 else
416 /* "/dir1/dir2/.../file". */
417 char dirname[lastslash - file_name + 1];
418 memcpy (dirname, file_name, lastslash - file_name);
419 dirname[lastslash - file_name] = '\0';
420 *name = (char *) lastslash + 1;
421 return
422 __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
423 dirname, 0, 0, dir);
426 else
428 /* "foobar" => cwdir + "foobar". */
429 *name = (char *) file_name;
430 return (*use_init_port) (INIT_PORT_CWDIR, &addref);
433 weak_alias (__hurd_file_name_split, hurd_file_name_split)
435 /* This is the same as hurd_file_name_split, except that it ignores
436 trailing slashes (so *NAME is never ""). */
437 error_t
438 __hurd_directory_name_split (error_t (*use_init_port)
439 (int which, error_t (*operate) (file_t)),
440 file_t (*get_dtable_port) (int fd),
441 error_t (*lookup)
442 (file_t dir, char *name, int flags, mode_t mode,
443 retry_type *do_retry, string_t retry_name,
444 mach_port_t *result),
445 const char *file_name,
446 file_t *dir, char **name)
448 error_t addref (file_t crdir)
450 *dir = crdir;
451 return __mach_port_mod_refs (__mach_task_self (),
452 crdir, MACH_PORT_RIGHT_SEND, +1);
455 const char *lastslash = strrchr (file_name, '/');
457 if (lastslash != NULL && lastslash[1] == '\0')
459 /* Trailing slash doesn't count. Look back further. */
461 /* Back up over all trailing slashes. */
462 while (lastslash > file_name && *lastslash == '/')
463 --lastslash;
465 /* Find the last one earlier in the string, before the trailing ones. */
466 #if __GLIBC__ > 2 || __GLIBC_MINOR__ >= 2
467 lastslash = __memrchr (file_name, '/', lastslash - file_name);
468 #else
469 /* Keep backing up, looking for a slash. */
471 if (lastslash == file_name)
473 /* Hit the start with no slash. */
474 lastslash = NULL;
475 break;
477 while (*lastslash-- != '/');
478 #endif
481 if (lastslash != NULL)
483 if (lastslash == file_name)
485 /* "/foobar" => crdir + "foobar". */
486 *name = (char *) file_name + 1;
487 return (*use_init_port) (INIT_PORT_CRDIR, &addref);
489 else
491 /* "/dir1/dir2/.../file". */
492 char dirname[lastslash - file_name + 1];
493 memcpy (dirname, file_name, lastslash - file_name);
494 dirname[lastslash - file_name] = '\0';
495 *name = (char *) lastslash + 1;
496 return
497 __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
498 dirname, 0, 0, dir);
501 else
503 /* "foobar" => cwdir + "foobar". */
504 *name = (char *) file_name;
505 return (*use_init_port) (INIT_PORT_CWDIR, &addref);
508 weak_alias (__hurd_directory_name_split, hurd_directory_name_split)
511 file_t
512 __file_name_lookup (const char *file_name, int flags, mode_t mode)
514 error_t err;
515 file_t result;
517 err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
518 file_name, flags, mode & ~_hurd_umask,
519 &result);
521 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
523 weak_alias (__file_name_lookup, file_name_lookup)
526 file_t
527 __file_name_split (const char *file_name, char **name)
529 error_t err;
530 file_t result;
532 err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
533 file_name, &result, name);
535 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
537 weak_alias (__file_name_split, file_name_split)
539 file_t
540 __directory_name_split (const char *directory_name, char **name)
542 error_t err;
543 file_t result;
545 err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0,
546 directory_name, &result, name);
548 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
550 weak_alias (__directory_name_split, directory_name_split)
553 file_t
554 __file_name_lookup_under (file_t startdir,
555 const char *file_name, int flags, mode_t mode)
557 error_t err;
558 file_t result;
560 error_t use_init_port (int which, error_t (*operate) (mach_port_t))
562 return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
563 _hurd_ports_use (which, operate));
566 err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
567 file_name, flags, mode & ~_hurd_umask,
568 &result);
570 return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
572 weak_alias (__file_name_lookup_under, file_name_lookup_under)