1 /* hairy bits of Hurd file name lookup
2 Copyright (C) 1992-2012 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
20 #include <hurd/lookup.h>
21 #include <hurd/term.h>
22 #include <hurd/paths.h>
28 /* Translate the error from dir_lookup into the error the user sees. */
30 lookup_error (error_t error)
36 /* These indicate that the server does not understand dir_lookup
37 at all. If it were a directory, it would, by definition. */
45 __hurd_file_name_lookup_retry (error_t (*use_init_port)
46 (int which, error_t (*operate) (file_t)),
47 file_t (*get_dtable_port) (int fd),
49 (file_t dir, char *name,
50 int flags, mode_t mode,
51 retry_type *do_retry, string_t retry_name,
53 enum retry_type doretry,
55 int flags, mode_t mode,
62 error_t lookup_op (file_t startdir)
64 while (file_name[0] == '/')
67 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
68 &doretry, retryname, result));
70 error_t reauthenticate (file_t unauth)
73 mach_port_t ref = __mach_reply_port ();
74 error_t reauth (auth_t auth)
76 return __auth_user_authenticate (auth, ref,
77 MACH_MSG_TYPE_MAKE_SEND,
80 err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
82 err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
83 __mach_port_destroy (__mach_task_self (), ref);
84 __mach_port_deallocate (__mach_task_self (), unauth);
89 lookup = __dir_lookup;
95 file_t startdir = MACH_PORT_NULL;
96 int dirport = INIT_PORT_CWDIR;
100 case FS_RETRY_REAUTH:
101 if (err = reauthenticate (*result))
105 case FS_RETRY_NORMAL:
106 if (nloops++ >= SYMLOOP_MAX)
108 __mach_port_deallocate (__mach_task_self (), *result);
112 /* An empty RETRYNAME indicates we have the final port. */
113 if (retryname[0] == '\0' &&
114 /* If reauth'd, we must do one more retry on "" to give the new
115 translator a chance to make a new port for us. */
116 doretry == FS_RETRY_NORMAL)
118 if (flags & O_NOFOLLOW)
120 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
121 did an O_NOLINK lookup above and io_stat here to check
122 for S_IFLNK, a translator like firmlink could easily
123 spoof this check by not showing S_IFLNK, but in fact
124 redirecting the lookup to some other name
125 (i.e. opening the very same holes a symlink would).
127 Instead we do an O_NOTRANS lookup above, and stat the
128 underlying node: if it has a translator set, and its
129 owner is not root (st_uid 0) then we reject it.
130 Since the motivation for this feature is security, and
131 that security presumes we trust the containing
132 directory, this check approximates the security of
133 refusing symlinks while accepting mount points.
134 Note that we actually permit something Linux doesn't:
135 we follow root-owned symlinks; if that is deemed
136 undesireable, we can add a final check for that
137 one exception to our general translator-based rule. */
139 err = __io_stat (*result, &st);
141 && (st.st_mode & (S_IPTRANS|S_IATRANS)))
145 else if (st.st_mode & S_IPTRANS)
149 size_t translen = sizeof buf;
150 err = __file_get_translator (*result,
153 && translen > sizeof _HURD_SYMLINK
155 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
161 /* We got a successful translation. Now apply any open-time
162 action flags we were passed. */
164 if (!err && (flags & O_TRUNC)) /* Asked to truncate the file. */
165 err = __file_set_size (*result, 0);
168 __mach_port_deallocate (__mach_task_self (), *result);
173 file_name = retryname;
176 case FS_RETRY_MAGICAL:
177 switch (retryname[0])
180 dirport = INIT_PORT_CRDIR;
181 if (*result != MACH_PORT_NULL)
182 __mach_port_deallocate (__mach_task_self (), *result);
183 if (nloops++ >= SYMLOOP_MAX)
185 file_name = &retryname[1];
189 if (retryname[1] == 'd' && retryname[2] == '/')
195 fd = (int) __strtoul_internal (&retryname[3], &end, 10, 0);
196 if (end == NULL || errno || /* Malformed number. */
197 /* Check for excess text after the number. A slash
198 is valid; it ends the component. Anything else
199 does not name a numeric file descriptor. */
200 (*end != '/' && *end != '\0'))
205 if (! get_dtable_port)
209 *result = (*get_dtable_port) (fd);
210 if (*result == MACH_PORT_NULL)
212 /* If the name was a proper number, but the file
213 descriptor does not exist, we return EBADF instead
226 /* Do a normal retry on the remaining components. */
228 file_name = end + 1; /* Skip the slash. */
237 if (retryname[1] == 'a' && retryname[2] == 'c' &&
238 retryname[3] == 'h' && retryname[4] == 't' &&
239 retryname[5] == 'y' && retryname[6] == 'p' &&
243 struct host_basic_info hostinfo;
244 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
246 /* XXX want client's host */
247 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
248 (integer_t *) &hostinfo,
251 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
253 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
255 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
257 abort (); /* XXX write this right if this ever happens */
259 strcpy (retryname, p);
267 if (retryname[1] == 't' && retryname[2] == 'y')
268 switch (retryname[3])
270 error_t opentty (file_t *result)
273 error_t ctty_open (file_t port)
275 if (port == MACH_PORT_NULL)
276 return ENXIO; /* No controlling terminal. */
277 return __termctty_open_terminal (port,
281 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
283 err = reauthenticate (*result);
288 return opentty (result);
290 if (err = opentty (&startdir))
292 strcpy (retryname, &retryname[4]);
311 if (startdir != MACH_PORT_NULL)
313 err = lookup_op (startdir);
314 __mach_port_deallocate (__mach_task_self (), startdir);
315 startdir = MACH_PORT_NULL;
318 err = (*use_init_port) (dirport, &lookup_op);
323 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)