Hurd: Missing critical region locks.
[platform/upstream/glibc.git] / hurd / hurdlookup.c
1 /* Copyright (C) 1992,1993,1994,1995,1996,1997,1999,2001,2004,2006
2         Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
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.
9
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.
14
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/>.  */
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 if (file_name[0] == '\0')
145     return ENOENT;
146   else
147     {
148       /* "foobar" => cwdir + "foobar".  */
149       *name = (char *) file_name;
150       return (*use_init_port) (INIT_PORT_CWDIR, &addref);
151     }
152 }
153 weak_alias (__hurd_file_name_split, hurd_file_name_split)
154
155 /* This is the same as hurd_file_name_split, except that it ignores
156    trailing slashes (so *NAME is never "").  */
157 error_t
158 __hurd_directory_name_split (error_t (*use_init_port)
159                              (int which, error_t (*operate) (file_t)),
160                              file_t (*get_dtable_port) (int fd),
161                              error_t (*lookup)
162                              (file_t dir, char *name, int flags, mode_t mode,
163                               retry_type *do_retry, string_t retry_name,
164                               mach_port_t *result),
165                              const char *file_name,
166                              file_t *dir, char **name)
167 {
168   error_t addref (file_t crdir)
169     {
170       *dir = crdir;
171       return __mach_port_mod_refs (__mach_task_self (),
172                                    crdir, MACH_PORT_RIGHT_SEND, +1);
173     }
174
175   const char *lastslash = strrchr (file_name, '/');
176
177   if (lastslash != NULL && lastslash[1] == '\0')
178     {
179       /* Trailing slash doesn't count.  Look back further.  */
180
181       /* Back up over all trailing slashes.  */
182       while (lastslash > file_name && *lastslash == '/')
183         --lastslash;
184
185       /* Find the last one earlier in the string, before the trailing ones.  */
186       lastslash = __memrchr (file_name, '/', lastslash - file_name);
187     }
188
189   if (lastslash != NULL)
190     {
191       if (lastslash == file_name)
192         {
193           /* "/foobar" => crdir + "foobar".  */
194           *name = (char *) file_name + 1;
195           return (*use_init_port) (INIT_PORT_CRDIR, &addref);
196         }
197       else
198         {
199           /* "/dir1/dir2/.../file".  */
200           char dirname[lastslash - file_name + 1];
201           memcpy (dirname, file_name, lastslash - file_name);
202           dirname[lastslash - file_name] = '\0';
203           *name = (char *) lastslash + 1;
204           return
205             __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup,
206                                      dirname, 0, 0, dir);
207         }
208     }
209   else if (file_name[0] == '\0')
210     return ENOENT;
211   else
212     {
213       /* "foobar" => cwdir + "foobar".  */
214       *name = (char *) file_name;
215       return (*use_init_port) (INIT_PORT_CWDIR, &addref);
216     }
217 }
218 weak_alias (__hurd_directory_name_split, hurd_directory_name_split)
219
220 \f
221 file_t
222 __file_name_lookup (const char *file_name, int flags, mode_t mode)
223 {
224   error_t err;
225   file_t result;
226
227   err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0,
228                                  file_name, flags, mode & ~_hurd_umask,
229                                  &result);
230
231   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
232 }
233 weak_alias (__file_name_lookup, file_name_lookup)
234
235
236 file_t
237 __file_name_split (const char *file_name, char **name)
238 {
239   error_t err;
240   file_t result;
241
242   err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0,
243                                 file_name, &result, name);
244
245   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
246 }
247 weak_alias (__file_name_split, file_name_split)
248
249 file_t
250 __directory_name_split (const char *directory_name, char **name)
251 {
252   error_t err;
253   file_t result;
254
255   err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0,
256                                      directory_name, &result, name);
257
258   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
259 }
260 weak_alias (__directory_name_split, directory_name_split)
261
262
263 file_t
264 __file_name_lookup_under (file_t startdir,
265                           const char *file_name, int flags, mode_t mode)
266 {
267   error_t err;
268   file_t result;
269
270   error_t use_init_port (int which, error_t (*operate) (mach_port_t))
271     {
272       return (which == INIT_PORT_CWDIR ? (*operate) (startdir) :
273               _hurd_ports_use (which, operate));
274     }
275
276   err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0,
277                                  file_name, flags, mode & ~_hurd_umask,
278                                  &result);
279
280   return err ? (__hurd_fail (err), MACH_PORT_NULL) : result;
281 }
282 weak_alias (__file_name_lookup_under, file_name_lookup_under)