Update copyright notices with scripts/update-copyrights
[glibc.git] / hurd / lookup-retry.c
blobf633e57f7fa851ed5af4f05201ef723be31cae4c
1 /* hairy bits of Hurd file name lookup
2 Copyright (C) 1992-2014 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/>. */
19 #include <hurd.h>
20 #include <hurd/lookup.h>
21 #include <hurd/term.h>
22 #include <hurd/paths.h>
23 #include <limits.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <_itoa.h>
27 #include <eloop-threshold.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_retry (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,
51 int flags, mode_t mode,
52 retry_type *do_retry, string_t retry_name,
53 mach_port_t *result),
54 enum retry_type doretry,
55 char retryname[1024],
56 int flags, mode_t mode,
57 file_t *result)
59 error_t err;
60 char *file_name;
61 int nloops;
63 error_t lookup_op (file_t startdir)
65 while (file_name[0] == '/')
66 file_name++;
68 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
69 &doretry, retryname, result));
71 error_t reauthenticate (file_t unauth)
73 error_t err;
74 mach_port_t ref = __mach_reply_port ();
75 error_t reauth (auth_t auth)
77 return __auth_user_authenticate (auth, ref,
78 MACH_MSG_TYPE_MAKE_SEND,
79 result);
81 err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
82 if (! err)
83 err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
84 __mach_port_destroy (__mach_task_self (), ref);
85 __mach_port_deallocate (__mach_task_self (), unauth);
86 return err;
89 if (! lookup)
90 lookup = __dir_lookup;
92 nloops = 0;
93 err = 0;
96 file_t startdir = MACH_PORT_NULL;
97 int dirport = INIT_PORT_CWDIR;
99 switch (doretry)
101 case FS_RETRY_REAUTH:
102 if (err = reauthenticate (*result))
103 return err;
104 /* Fall through. */
106 case FS_RETRY_NORMAL:
107 if (nloops++ >= __eloop_threshold ())
109 __mach_port_deallocate (__mach_task_self (), *result);
110 return ELOOP;
113 /* An empty RETRYNAME indicates we have the final port. */
114 if (retryname[0] == '\0' &&
115 /* If reauth'd, we must do one more retry on "" to give the new
116 translator a chance to make a new port for us. */
117 doretry == FS_RETRY_NORMAL)
119 if (flags & O_NOFOLLOW)
121 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
122 did an O_NOLINK lookup above and io_stat here to check
123 for S_IFLNK, a translator like firmlink could easily
124 spoof this check by not showing S_IFLNK, but in fact
125 redirecting the lookup to some other name
126 (i.e. opening the very same holes a symlink would).
128 Instead we do an O_NOTRANS lookup above, and stat the
129 underlying node: if it has a translator set, and its
130 owner is not root (st_uid 0) then we reject it.
131 Since the motivation for this feature is security, and
132 that security presumes we trust the containing
133 directory, this check approximates the security of
134 refusing symlinks while accepting mount points.
135 Note that we actually permit something Linux doesn't:
136 we follow root-owned symlinks; if that is deemed
137 undesireable, we can add a final check for that
138 one exception to our general translator-based rule. */
139 struct stat64 st;
140 err = __io_stat (*result, &st);
141 if (!err
142 && (st.st_mode & (S_IPTRANS|S_IATRANS)))
144 if (st.st_uid != 0)
145 err = ENOENT;
146 else if (st.st_mode & S_IPTRANS)
148 char buf[1024];
149 char *trans = buf;
150 size_t translen = sizeof buf;
151 err = __file_get_translator (*result,
152 &trans, &translen);
153 if (!err
154 && translen > sizeof _HURD_SYMLINK
155 && !memcmp (trans,
156 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
157 err = ENOENT;
162 /* We got a successful translation. Now apply any open-time
163 action flags we were passed. */
165 if (!err && (flags & O_TRUNC)) /* Asked to truncate the file. */
166 err = __file_set_size (*result, 0);
168 if (err)
169 __mach_port_deallocate (__mach_task_self (), *result);
170 return err;
173 startdir = *result;
174 file_name = retryname;
175 break;
177 case FS_RETRY_MAGICAL:
178 switch (retryname[0])
180 case '/':
181 dirport = INIT_PORT_CRDIR;
182 if (*result != MACH_PORT_NULL)
183 __mach_port_deallocate (__mach_task_self (), *result);
184 if (nloops++ >= __eloop_threshold ())
185 return ELOOP;
186 file_name = &retryname[1];
187 break;
189 case 'f':
190 if (retryname[1] == 'd' && retryname[2] == '/')
192 int fd;
193 char *end;
194 int save = errno;
195 errno = 0;
196 fd = (int) __strtoul_internal (&retryname[3], &end, 10, 0);
197 if (end == NULL || errno || /* Malformed number. */
198 /* Check for excess text after the number. A slash
199 is valid; it ends the component. Anything else
200 does not name a numeric file descriptor. */
201 (*end != '/' && *end != '\0'))
203 errno = save;
204 return ENOENT;
206 if (! get_dtable_port)
207 err = EGRATUITOUS;
208 else
210 *result = (*get_dtable_port) (fd);
211 if (*result == MACH_PORT_NULL)
213 /* If the name was a proper number, but the file
214 descriptor does not exist, we return EBADF instead
215 of ENOENT. */
216 err = errno;
217 errno = save;
220 errno = save;
221 if (err)
222 return err;
223 if (*end == '\0')
224 return 0;
225 else
227 /* Do a normal retry on the remaining components. */
228 startdir = *result;
229 file_name = end + 1; /* Skip the slash. */
230 break;
233 else
234 goto bad_magic;
235 break;
237 case 'm':
238 if (retryname[1] == 'a' && retryname[2] == 'c' &&
239 retryname[3] == 'h' && retryname[4] == 't' &&
240 retryname[5] == 'y' && retryname[6] == 'p' &&
241 retryname[7] == 'e')
243 error_t err;
244 struct host_basic_info hostinfo;
245 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
246 char *p;
247 /* XXX want client's host */
248 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
249 (integer_t *) &hostinfo,
250 &hostinfocnt))
251 return err;
252 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
253 return EGRATUITOUS;
254 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
255 *--p = '/';
256 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
257 if (p < retryname)
258 abort (); /* XXX write this right if this ever happens */
259 if (p > retryname)
260 strcpy (retryname, p);
261 startdir = *result;
263 else
264 goto bad_magic;
265 break;
267 case 't':
268 if (retryname[1] == 't' && retryname[2] == 'y')
269 switch (retryname[3])
271 error_t opentty (file_t *result)
273 error_t err;
274 error_t ctty_open (file_t port)
276 if (port == MACH_PORT_NULL)
277 return ENXIO; /* No controlling terminal. */
278 return __termctty_open_terminal (port,
279 flags,
280 result);
282 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
283 if (! err)
284 err = reauthenticate (*result);
285 return err;
288 case '\0':
289 return opentty (result);
290 case '/':
291 if (err = opentty (&startdir))
292 return err;
293 strcpy (retryname, &retryname[4]);
294 break;
295 default:
296 goto bad_magic;
298 else
299 goto bad_magic;
300 break;
302 default:
303 bad_magic:
304 return EGRATUITOUS;
306 break;
308 default:
309 return EGRATUITOUS;
312 if (startdir != MACH_PORT_NULL)
314 err = lookup_op (startdir);
315 __mach_port_deallocate (__mach_task_self (), startdir);
316 startdir = MACH_PORT_NULL;
318 else
319 err = (*use_init_port) (dirport, &lookup_op);
320 } while (! err);
322 return err;
324 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)