2002-03-25 Roland McGrath <roland@frob.com>
[platform/upstream/glibc.git] / hurd / hurdlookup.c
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.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the 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    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <hurd.h>
20 #include <hurd/lookup.h>
21 #include <string.h>
22 #include <fcntl.h>
23
24
25 /* Translate the error from dir_lookup into the error the user sees.  */
26 static inline error_t
27 lookup_error (error_t error)
28 {
29   switch (error)
30     {
31     case EOPNOTSUPP:
32     case MIG_BAD_ID:
33       /* These indicate that the server does not understand dir_lookup
34          at all.  If it were a directory, it would, by definition.  */
35       return ENOTDIR;
36     default:
37       return error;
38     }
39 }
40
41 error_t
42 __hurd_file_name_lookup (error_t (*use_init_port)
43                            (int which, error_t (*operate) (file_t)),
44                          file_t (*get_dtable_port) (int fd),
45                          error_t (*lookup)
46                            (file_t dir, char *name, int flags, mode_t mode,
47                             retry_type *do_retry, string_t retry_name,
48                             mach_port_t *result),
49                          const char *file_name, int flags, mode_t mode,
50                          file_t *result)
51 {
52   error_t err;
53   enum retry_type doretry;
54   char retryname[1024];         /* XXX string_t LOSES! */
55   int startport;
56
57   error_t lookup_op (mach_port_t startdir)
58     {
59       return lookup_error ((*lookup) (startdir, file_name, flags, mode,
60                                       &doretry, retryname, result));
61     }
62
63   if (! lookup)
64     lookup = __dir_lookup;
65
66   if (file_name[0] == '\0')
67     return ENOENT;
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 lookup-retry.c 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_split (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, int flags, mode_t mode,
110                            retry_type *do_retry, string_t retry_name,
111                            mach_port_t *result),
112                         const char *file_name,
113                         file_t *dir, char **name)
114 {
115   error_t addref (file_t crdir)
116     {
117       *dir = crdir;
118       return __mach_port_mod_refs (__mach_task_self (),
119                                    crdir, MACH_PORT_RIGHT_SEND, +1);
120     }
121
122   const char *lastslash = strrchr (file_name, '/');
123
124   if (lastslash != NULL)
125     {
126       if (lastslash == file_name)
127         {
128           /* "/foobar" => crdir + "foobar".  */
129           *name = (char *) file_name + 1;
130           return (*use_init_port) (INIT_PORT_CRDIR, &addref);
131         }
132       else
133         {
134           /* "/dir1/dir2/.../file".  */
135           char dirname[lastslash - file_name + 1];
136           memcpy (dirname, file_name, lastslash - file_name);
137           dirname[lastslash - file_name] = '\0';
138           *name = (char *) lastslash + 1;
139           return
140             __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
141                                      dirname, 0, 0, dir);
142         }
143     }
144   else
145     {
146       /* "foobar" => cwdir + "foobar".  */
147       *name = (char *) file_name;
148       return (*use_init_port) (INIT_PORT_CWDIR, &addref);
149     }
150 }
151 weak_alias (__hurd_file_name_split, hurd_file_name_split)
152
153 /* This is the same as hurd_file_name_split, except that it ignores
154    trailing slashes (so *NAME is never "").  */
155 error_t
156 __hurd_directory_name_split (error_t (*use_init_port)
157                              (int which, error_t (*operate) (file_t)),
158                              file_t (*get_dtable_port) (int fd),
159                              error_t (*lookup)
160                              (file_t dir, char *name, int flags, mode_t mode,
161                               retry_type *do_retry, string_t retry_name,
162                               mach_port_t *result),
163                              const char *file_name,
164                              file_t *dir, char **name)
165 {
166   error_t addref (file_t crdir)
167     {
168       *dir = crdir;
169       return __mach_port_mod_refs (__mach_task_self (),
170                                    crdir, MACH_PORT_RIGHT_SEND, +1);
171     }
172
173   const char *lastslash = strrchr (file_name, '/');
174
175   if (lastslash != NULL && lastslash[1] == '\0')
176     {
177       /* Trailing slash doesn't count.  Look back further.  */
178
179       /* Back up over all trailing slashes.  */
180       while (lastslash > file_name && *lastslash == '/')
181         --lastslash;
182
183       /* Find the last one earlier in the string, before the trailing ones.  */
184 #if __GLIBC__ > 2 || __GLIBC_MINOR__ >= 2
185       lastslash = __memrchr (file_name, '/', lastslash - file_name);
186 #else
187       /* Keep backing up, looking for a slash.  */
188       do
189         if (lastslash == file_name)
190           {
191             /* Hit the start with no slash.  */
192             lastslash = NULL;
193             break;
194           }
195       while (*lastslash-- != '/');
196 #endif
197     }
198
199   if (lastslash != NULL)
200     {
201       if (lastslash == file_name)
202         {
203           /* "/foobar" => crdir + "foobar".  */
204           *name = (char *) file_name + 1;
205           return (*use_init_port) (INIT_PORT_CRDIR, &addref);
206         }
207       else
208         {
209           /* "/dir1/dir2/.../file".  */
210           char dirname[lastslash - file_name + 1];
211           memcpy (dirname, file_name, lastslash - file_name);
212           dirname[lastslash - file_name] = '\0';
213           *name = (char *) lastslash + 1;
214           return
215             __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
216                                      dirname, 0, 0, dir);
217         }
218     }
219   else
220     {
221       /* "foobar" => cwdir + "foobar".  */
222       *name = (char *) file_name;
223       return (*use_init_port) (INIT_PORT_CWDIR, &addref);
224     }
225 }
226 weak_alias (__hurd_directory_name_split, hurd_directory_name_split)
227
228 \f
229 file_t
230 __file_name_lookup (const char *file_name, int flags, mode_t mode)
231 {
232   error_t err;
233   file_t result;
234
235   err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
236                                  file_name, flags, mode & ~_hurd_umask,
237                                  &result);
238
239   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
240 }
241 weak_alias (__file_name_lookup, file_name_lookup)
242
243
244 file_t
245 __file_name_split (const char *file_name, char **name)
246 {
247   error_t err;
248   file_t result;
249
250   err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
251                                 file_name, &result, name);
252
253   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
254 }
255 weak_alias (__file_name_split, file_name_split)
256
257 file_t
258 __directory_name_split (const char *directory_name, char **name)
259 {
260   error_t err;
261   file_t result;
262
263   err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0,
264                                      directory_name, &result, name);
265
266   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
267 }
268 weak_alias (__directory_name_split, directory_name_split)
269
270
271 file_t
272 __file_name_lookup_under (file_t startdir,
273                           const char *file_name, int flags, mode_t mode)
274 {
275   error_t err;
276   file_t result;
277
278   error_t use_init_port (int which, error_t (*operate) (mach_port_t))
279     {
280       return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
281               _hurd_ports_use (which, operate));
282     }
283
284   err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
285                                  file_name, flags, mode & ~_hurd_umask,
286                                  &result);
287
288   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
289 }
290 weak_alias (__file_name_lookup_under, file_name_lookup_under)