1999-03-04 Roland McGrath <roland@baalperazim.frob.com>
[kopensolaris-gnu/glibc.git] / hurd / hurdlookup.c
1 /* Copyright (C) 1992, 93, 94, 95, 96, 97, 99 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
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.
8
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.
13
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.  */
18
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
27
28 /* Translate the error from dir_lookup into the error the user sees.  */
29 static inline error_t
30 lookup_error (error_t error)
31 {
32   switch (error)
33     {
34     case EOPNOTSUPP:
35     case MIG_BAD_ID:
36       /* These indicate that the server does not understand dir_lookup
37          at all.  If it were a directory, it would, by definition.  */
38       return ENOTDIR;
39     default:
40       return error;
41     }
42 }
43
44 error_t
45 __hurd_file_name_lookup (error_t (*use_init_port)
46                            (int which, error_t (*operate) (file_t)),
47                          file_t (*get_dtable_port) (int fd),
48                          error_t (*lookup)
49                            (file_t dir, char *name, int flags, mode_t mode,
50                             retry_type *do_retry, string_t retry_name,
51                             mach_port_t *result),
52                          const char *file_name, int flags, mode_t mode,
53                          file_t *result)
54 {
55   error_t err;
56   enum retry_type doretry;
57   char retryname[1024];         /* XXX string_t LOSES! */
58   int startport;
59
60   error_t lookup_op (mach_port_t startdir)
61     {
62       return lookup_error ((*lookup) (startdir, file_name, flags, mode,
63                                       &doretry, retryname, result));
64     }
65
66   if (! lookup)
67     lookup = __dir_lookup;
68
69   startport = (file_name[0] == '/') ? INIT_PORT_CRDIR : INIT_PORT_CWDIR;
70   while (file_name[0] == '/')
71     file_name++;
72
73   if (flags & O_NOFOLLOW)       /* See comments below about O_NOFOLLOW.  */
74     flags |= O_NOTRANS;
75
76   if (flags & O_DIRECTORY)
77     {
78       /* The caller wants to require that the file we look up is a directory.
79          We can do this without an extra RPC by appending a trailing slash
80          to the file name we look up.  */
81       size_t len = strlen (file_name);
82       if (len == 0)
83         file_name = "/";
84       else if (file_name[len - 1] != '/')
85         {
86           char *n = alloca (len + 2);
87           memcpy (n, file_name, len);
88           n[len] = '/';
89           n[len + 1] = '\0';
90           file_name = n;
91         }
92     }
93
94   err = (*use_init_port) (startport, &lookup_op);
95   if (! err)
96     err = __hurd_file_name_lookup_retry (use_init_port, get_dtable_port,
97                                          lookup, doretry, retryname,
98                                          flags, mode, result);
99
100   return err;
101 }
102 weak_alias (__hurd_file_name_lookup, hurd_file_name_lookup)
103
104 error_t
105 __hurd_file_name_lookup_retry (error_t (*use_init_port)
106                                  (int which, error_t (*operate) (file_t)),
107                                file_t (*get_dtable_port) (int fd),
108                                error_t (*lookup)
109                                  (file_t dir, char *name,
110                                   int flags, mode_t mode,
111                                   retry_type *do_retry, string_t retry_name,
112                                   mach_port_t *result),
113                                enum retry_type doretry,
114                                char retryname[1024],
115                                int flags, mode_t mode,
116                                file_t *result)
117 {
118   error_t err;
119   char *file_name;
120   int nloops;
121
122   error_t lookup_op (file_t startdir)
123     {
124       while (file_name[0] == '/')
125         file_name++;
126
127       return lookup_error ((*lookup) (startdir, file_name, flags, mode,
128                                       &doretry, retryname, result));
129     }
130   error_t reauthenticate (file_t unauth)
131     {
132       error_t err;
133       mach_port_t ref = __mach_reply_port ();
134       error_t reauth (auth_t auth)
135         {
136           return __auth_user_authenticate (auth, ref,
137                                            MACH_MSG_TYPE_MAKE_SEND,
138                                            result);
139         }
140       err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
141       if (! err)
142         err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
143       __mach_port_destroy (__mach_task_self (), ref);
144       __mach_port_deallocate (__mach_task_self (), unauth);
145       return err;
146     }
147
148   if (! lookup)
149     lookup = __dir_lookup;
150
151   nloops = 0;
152   err = 0;
153   do
154     {
155       file_t startdir = MACH_PORT_NULL;
156       int dirport = INIT_PORT_CWDIR;
157
158       switch (doretry)
159         {
160         case FS_RETRY_REAUTH:
161           if (err = reauthenticate (*result))
162             return err;
163           /* Fall through.  */
164
165         case FS_RETRY_NORMAL:
166 #ifdef SYMLOOP_MAX
167           if (nloops++ >= SYMLOOP_MAX)
168             return ELOOP;
169 #endif
170
171           /* An empty RETRYNAME indicates we have the final port.  */
172           if (retryname[0] == '\0' &&
173               /* If reauth'd, we must do one more retry on "" to give the new
174                  translator a chance to make a new port for us.  */
175               doretry == FS_RETRY_NORMAL)
176             {
177               if (flags & O_NOFOLLOW)
178                 {
179                   /* In Linux, O_NOFOLLOW means to reject symlinks.  If we
180                      did an O_NOLINK lookup above and io_stat here to check
181                      for S_IFLNK, a translator like firmlink could easily
182                      spoof this check by not showing S_IFLNK, but in fact
183                      redirecting the lookup to some other name
184                      (i.e. opening the very same holes a symlink would).
185
186                      Instead we do an O_NOTRANS lookup above, and stat the
187                      underlying node: if it has a translator set, and its
188                      owner is not root (st_uid 0) then we reject it.
189                      Since the motivation for this feature is security, and
190                      that security presumes we trust the containing
191                      directory, this check approximates the security of
192                      refusing symlinks while accepting mount points.
193                      Note that we actually permit something Linux doesn't:
194                      we follow root-owned symlinks; if that is deemed
195                      undesireable, we can add a final check for that
196                      one exception to our general translator-based rule.  */
197                   struct stat st;
198                   err = __io_stat (*result, &st);
199                   if (!err
200                       && st.st_uid != 0
201                       && (st.st_mode & (S_IPTRANS|S_IATRANS)))
202                     err = ENOENT;
203                 }
204
205               /* We got a successful translation.  Now apply any open-time
206                  action flags we were passed.  */
207
208               if (!err && (flags & O_TRUNC)) /* Asked to truncate the file.  */
209                 err = __file_set_size (*result, 0);
210
211               if (err)
212                 __mach_port_deallocate (__mach_task_self (), *result);
213               return err;
214             }
215
216           startdir = *result;
217           file_name = retryname;
218           break;
219
220         case FS_RETRY_MAGICAL:
221           switch (retryname[0])
222             {
223             case '/':
224               dirport = INIT_PORT_CRDIR;
225               if (*result != MACH_PORT_NULL)
226                 __mach_port_deallocate (__mach_task_self (), *result);
227               file_name = &retryname[1];
228               break;
229
230             case 'f':
231               if (retryname[1] == 'd' && retryname[2] == '/')
232                 {
233                   int fd;
234                   char *end;
235                   int save = errno;
236                   errno = 0;
237                   fd = (int) strtol (&retryname[3], &end, 10);
238                   if (end == NULL || errno || /* Malformed number.  */
239                       /* Check for excess text after the number.  A slash
240                          is valid; it ends the component.  Anything else
241                          does not name a numeric file descriptor.  */
242                       (*end != '/' && *end != '\0'))
243                     {
244                       errno = save;
245                       return ENOENT;
246                     }
247                   if (! get_dtable_port)
248                     err = EGRATUITOUS;
249                   else
250                     {
251                       *result = (*get_dtable_port) (fd);
252                       if (*result == MACH_PORT_NULL)
253                         {
254                           /* If the name was a proper number, but the file
255                              descriptor does not exist, we return EBADF instead
256                              of ENOENT.  */
257                           err = errno;
258                           errno = save;
259                         }
260                     }
261                   errno = save;
262                   if (err)
263                     return err;
264                   if (*end == '\0')
265                     return 0;
266                   else
267                     {
268                       /* Do a normal retry on the remaining components.  */
269                       startdir = *result;
270                       file_name = end + 1; /* Skip the slash.  */
271                       break;
272                     }
273                 }
274               else
275                 goto bad_magic;
276               break;
277
278             case 'm':
279               if (retryname[1] == 'a' && retryname[2] == 'c' &&
280                   retryname[3] == 'h' && retryname[4] == 't' &&
281                   retryname[5] == 'y' && retryname[6] == 'p' &&
282                   retryname[7] == 'e')
283                 {
284                   error_t err;
285                   struct host_basic_info hostinfo;
286                   mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
287                   char *p;
288                   /* XXX want client's host */
289                   if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
290                                          (natural_t *) &hostinfo,
291                                          &hostinfocnt))
292                     return err;
293                   if (hostinfocnt != HOST_BASIC_INFO_COUNT)
294                     return EGRATUITOUS;
295                   p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
296                   *--p = '/';
297                   p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
298                   if (p < retryname)
299                     abort ();   /* XXX write this right if this ever happens */
300                   if (p > retryname)
301                     strcpy (retryname, p);
302                   startdir = *result;
303                 }
304               else
305                 goto bad_magic;
306               break;
307
308             case 't':
309               if (retryname[1] == 't' && retryname[2] == 'y')
310                 switch (retryname[3])
311                   {
312                     error_t opentty (file_t *result)
313                       {
314                         error_t err;
315                         error_t ctty_open (file_t port)
316                           {
317                             if (port == MACH_PORT_NULL)
318                               return ENXIO; /* No controlling terminal.  */
319                             return __termctty_open_terminal (port,
320                                                              flags,
321                                                              result);
322                           }
323                         err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
324                         if (! err)
325                           err = reauthenticate (*result);
326                         return err;
327                       }
328
329                   case '\0':
330                     return opentty (result);
331                   case '/':
332                     if (err = opentty (&startdir))
333                       return err;
334                     strcpy (retryname, &retryname[4]);
335                     break;
336                   default:
337                     goto bad_magic;
338                   }
339               else
340                 goto bad_magic;
341               break;
342
343             default:
344             bad_magic:
345               return EGRATUITOUS;
346             }
347           break;
348
349         default:
350           return EGRATUITOUS;
351         }
352
353       if (startdir != MACH_PORT_NULL)
354         {
355           err = lookup_op (startdir);
356           __mach_port_deallocate (__mach_task_self (), startdir);
357           startdir = MACH_PORT_NULL;
358         }
359       else
360         err = (*use_init_port) (dirport, &lookup_op);
361     } while (! err);
362
363   return err;
364 }
365 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
366
367 error_t
368 __hurd_file_name_split (error_t (*use_init_port)
369                           (int which, error_t (*operate) (file_t)),
370                         file_t (*get_dtable_port) (int fd),
371                         error_t (*lookup)
372                           (file_t dir, char *name, int flags, mode_t mode,
373                            retry_type *do_retry, string_t retry_name,
374                            mach_port_t *result),
375                         const char *file_name,
376                         file_t *dir, char **name)
377 {
378   error_t addref (file_t crdir)
379     {
380       *dir = crdir;
381       return __mach_port_mod_refs (__mach_task_self (),
382                                    crdir, MACH_PORT_RIGHT_SEND, +1);
383     }
384
385   const char *lastslash = strrchr (file_name, '/');
386
387   if (lastslash != NULL)
388     {
389       if (lastslash == file_name)
390         {
391           /* "/foobar" => crdir + "foobar".  */
392           *name = (char *) file_name + 1;
393           return (*use_init_port) (INIT_PORT_CRDIR, &addref);
394         }
395       else
396         {
397           /* "/dir1/dir2/.../file".  */
398           char dirname[lastslash - file_name + 1];
399           memcpy (dirname, file_name, lastslash - file_name);
400           dirname[lastslash - file_name] = '\0';
401           *name = (char *) lastslash + 1;
402           return
403             __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
404                                      dirname, 0, 0, dir);
405         }
406     }
407   else
408     {
409       /* "foobar" => cwdir + "foobar".  */
410       *name = (char *) file_name;
411       return (*use_init_port) (INIT_PORT_CWDIR, &addref);
412     }
413 }
414 weak_alias (__hurd_file_name_split, hurd_file_name_split)
415
416 \f
417 file_t
418 __file_name_lookup (const char *file_name, int flags, mode_t mode)
419 {
420   error_t err;
421   file_t result;
422
423   err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
424                                  file_name, flags, mode & ~_hurd_umask,
425                                  &result);
426
427   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
428 }
429 weak_alias (__file_name_lookup, file_name_lookup)
430
431
432 file_t
433 __file_name_split (const char *file_name, char **name)
434 {
435   error_t err;
436   file_t result;
437
438   err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
439                                 file_name, &result, name);
440
441   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
442 }
443 weak_alias (__file_name_split, file_name_split)
444
445
446 file_t
447 __file_name_lookup_under (file_t startdir,
448                           const char *file_name, int flags, mode_t mode)
449 {
450   error_t err;
451   file_t result;
452
453   error_t use_init_port (int which, error_t (*operate) (mach_port_t))
454     {
455       return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
456               _hurd_ports_use (which, operate));
457     }
458
459   err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
460                                  file_name, flags, mode & ~_hurd_umask,
461                                  &result);
462
463   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
464 }
465 weak_alias (__file_name_lookup_under, file_name_lookup_under)