1 | /* Copyright (C) 1992-2022 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, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <hurd.h> |
19 | #include <hurd/lookup.h> |
20 | #include <string.h> |
21 | #include <fcntl.h> |
22 | |
23 | |
24 | /* Translate the error from dir_lookup into the error the user sees. */ |
25 | static inline error_t |
26 | lookup_error (error_t error) |
27 | { |
28 | switch (error) |
29 | { |
30 | case EOPNOTSUPP: |
31 | case MIG_BAD_ID: |
32 | /* These indicate that the server does not understand dir_lookup |
33 | at all. If it were a directory, it would, by definition. */ |
34 | return ENOTDIR; |
35 | default: |
36 | return error; |
37 | } |
38 | } |
39 | |
40 | error_t |
41 | __hurd_file_name_lookup (error_t (*use_init_port) |
42 | (int which, error_t (*operate) (file_t)), |
43 | file_t (*get_dtable_port) (int fd), |
44 | error_t (*lookup) |
45 | (file_t dir, const char *name, int flags, mode_t mode, |
46 | retry_type *do_retry, string_t retry_name, |
47 | mach_port_t *result), |
48 | const char *file_name, int flags, mode_t mode, |
49 | file_t *result) |
50 | { |
51 | error_t err; |
52 | enum retry_type doretry; |
53 | char retryname[1024]; /* XXX string_t LOSES! */ |
54 | int startport; |
55 | |
56 | error_t lookup_op (mach_port_t startdir) |
57 | { |
58 | return lookup_error ((*lookup) (startdir, file_name, flags, mode, |
59 | &doretry, retryname, result)); |
60 | } |
61 | |
62 | if (! lookup) |
63 | lookup = __dir_lookup; |
64 | |
65 | if (file_name[0] == '\0') |
66 | return ENOENT; |
67 | |
68 | startport = (file_name[0] == '/') ? INIT_PORT_CRDIR : INIT_PORT_CWDIR; |
69 | while (file_name[0] == '/') |
70 | file_name++; |
71 | |
72 | if (flags & O_NOFOLLOW) /* See lookup-retry.c about O_NOFOLLOW. */ |
73 | flags |= O_NOTRANS; |
74 | |
75 | if (flags & O_DIRECTORY && (flags & O_NOFOLLOW) == 0) |
76 | { |
77 | /* The caller wants to require that the file we look up is a directory. |
78 | We can do this without an extra RPC by appending a trailing slash |
79 | to the file name we look up. */ |
80 | size_t len = strlen (s: file_name); |
81 | if (len == 0) |
82 | file_name = "/" ; |
83 | else if (file_name[len - 1] != '/') |
84 | { |
85 | char *n = alloca (len + 2); |
86 | memcpy (dest: n, src: file_name, n: len); |
87 | n[len] = '/'; |
88 | n[len + 1] = '\0'; |
89 | file_name = n; |
90 | } |
91 | } |
92 | |
93 | err = (*use_init_port) (startport, &lookup_op); |
94 | if (! err) |
95 | err = __hurd_file_name_lookup_retry (use_init_port, get_dtable_port, |
96 | lookup, doretry, retryname, |
97 | flags, mode, result); |
98 | |
99 | return err; |
100 | } |
101 | weak_alias (__hurd_file_name_lookup, hurd_file_name_lookup) |
102 | |
103 | error_t |
104 | __hurd_file_name_split (error_t (*use_init_port) |
105 | (int which, error_t (*operate) (file_t)), |
106 | file_t (*get_dtable_port) (int fd), |
107 | error_t (*lookup) |
108 | (file_t dir, const char *name, int flags, mode_t mode, |
109 | retry_type *do_retry, string_t retry_name, |
110 | mach_port_t *result), |
111 | const char *file_name, |
112 | file_t *dir, char **name) |
113 | { |
114 | error_t addref (file_t crdir) |
115 | { |
116 | *dir = crdir; |
117 | return __mach_port_mod_refs (__mach_task_self (), |
118 | crdir, MACH_PORT_RIGHT_SEND, +1); |
119 | } |
120 | |
121 | const char *lastslash = strrchr (s: file_name, c: '/'); |
122 | |
123 | if (lastslash != NULL) |
124 | { |
125 | if (lastslash == file_name) |
126 | { |
127 | /* "/foobar" => crdir + "foobar". */ |
128 | *name = (char *) file_name + 1; |
129 | return (*use_init_port) (INIT_PORT_CRDIR, &addref); |
130 | } |
131 | else |
132 | { |
133 | /* "/dir1/dir2/.../file". */ |
134 | char dirname[lastslash - file_name + 1]; |
135 | memcpy (dest: dirname, src: file_name, n: lastslash - file_name); |
136 | dirname[lastslash - file_name] = '\0'; |
137 | *name = (char *) lastslash + 1; |
138 | return |
139 | __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup, |
140 | dirname, 0, 0, dir); |
141 | } |
142 | } |
143 | else if (file_name[0] == '\0') |
144 | return ENOENT; |
145 | else |
146 | { |
147 | /* "foobar" => cwdir + "foobar". */ |
148 | *name = (char *) file_name; |
149 | return (*use_init_port) (INIT_PORT_CWDIR, &addref); |
150 | } |
151 | } |
152 | weak_alias (__hurd_file_name_split, hurd_file_name_split) |
153 | |
154 | /* This is the same as hurd_file_name_split, except that it ignores |
155 | trailing slashes (so *NAME is never ""). */ |
156 | error_t |
157 | __hurd_directory_name_split (error_t (*use_init_port) |
158 | (int which, error_t (*operate) (file_t)), |
159 | file_t (*get_dtable_port) (int fd), |
160 | error_t (*lookup) |
161 | (file_t dir, const char *name, int flags, mode_t mode, |
162 | retry_type *do_retry, string_t retry_name, |
163 | mach_port_t *result), |
164 | const char *file_name, |
165 | file_t *dir, char **name) |
166 | { |
167 | error_t addref (file_t crdir) |
168 | { |
169 | *dir = crdir; |
170 | return __mach_port_mod_refs (__mach_task_self (), |
171 | crdir, MACH_PORT_RIGHT_SEND, +1); |
172 | } |
173 | |
174 | const char *lastslash = strrchr (s: file_name, c: '/'); |
175 | |
176 | if (lastslash != NULL && lastslash[1] == '\0') |
177 | { |
178 | /* Trailing slash doesn't count. Look back further. */ |
179 | |
180 | /* Back up over all trailing slashes. */ |
181 | while (lastslash > file_name && *lastslash == '/') |
182 | --lastslash; |
183 | |
184 | /* Find the last one earlier in the string, before the trailing ones. */ |
185 | lastslash = __memrchr (s: file_name, c: '/', n: lastslash - file_name); |
186 | } |
187 | |
188 | if (lastslash != NULL) |
189 | { |
190 | if (lastslash == file_name) |
191 | { |
192 | /* "/foobar" => crdir + "foobar". */ |
193 | *name = (char *) file_name + 1; |
194 | return (*use_init_port) (INIT_PORT_CRDIR, &addref); |
195 | } |
196 | else |
197 | { |
198 | /* "/dir1/dir2/.../file". */ |
199 | char dirname[lastslash - file_name + 1]; |
200 | memcpy (dest: dirname, src: file_name, n: lastslash - file_name); |
201 | dirname[lastslash - file_name] = '\0'; |
202 | *name = (char *) lastslash + 1; |
203 | return |
204 | __hurd_file_name_lookup (use_init_port, get_dtable_port, lookup, |
205 | dirname, 0, 0, dir); |
206 | } |
207 | } |
208 | else if (file_name[0] == '\0') |
209 | return ENOENT; |
210 | else |
211 | { |
212 | /* "foobar" => cwdir + "foobar". */ |
213 | *name = (char *) file_name; |
214 | return (*use_init_port) (INIT_PORT_CWDIR, &addref); |
215 | } |
216 | } |
217 | weak_alias (__hurd_directory_name_split, hurd_directory_name_split) |
218 | |
219 | |
220 | file_t |
221 | __file_name_lookup (const char *file_name, int flags, mode_t mode) |
222 | { |
223 | error_t err; |
224 | file_t result; |
225 | |
226 | err = __hurd_file_name_lookup (&_hurd_ports_use, &__getdport, 0, |
227 | file_name, flags, mode & ~_hurd_umask, |
228 | &result); |
229 | |
230 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
231 | } |
232 | weak_alias (__file_name_lookup, file_name_lookup) |
233 | |
234 | |
235 | file_t |
236 | __file_name_split (const char *file_name, char **name) |
237 | { |
238 | error_t err; |
239 | file_t result; |
240 | |
241 | err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0, |
242 | file_name, &result, name); |
243 | |
244 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
245 | } |
246 | weak_alias (__file_name_split, file_name_split) |
247 | |
248 | file_t |
249 | __directory_name_split (const char *directory_name, char **name) |
250 | { |
251 | error_t err; |
252 | file_t result; |
253 | |
254 | err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0, |
255 | directory_name, &result, name); |
256 | |
257 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
258 | } |
259 | weak_alias (__directory_name_split, directory_name_split) |
260 | |
261 | |
262 | file_t |
263 | __file_name_lookup_under (file_t startdir, |
264 | const char *file_name, int flags, mode_t mode) |
265 | { |
266 | error_t err; |
267 | file_t result; |
268 | |
269 | error_t use_init_port (int which, error_t (*operate) (mach_port_t)) |
270 | { |
271 | return (which == INIT_PORT_CWDIR ? (*operate) (startdir) |
272 | : _hurd_ports_use (which, operate)); |
273 | } |
274 | |
275 | err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0, |
276 | file_name, flags, mode & ~_hurd_umask, |
277 | &result); |
278 | |
279 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
280 | } |
281 | weak_alias (__file_name_lookup_under, file_name_lookup_under) |
282 | |