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