1 | /* Copyright (C) 1992-2024 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/fd.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, const 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 && (flags & O_NOFOLLOW) == 0) |
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 (s: 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 (dest: n, src: file_name, n: 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, const 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 (s: file_name, c: '/'); |
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 (dest: dirname, src: file_name, n: 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, const 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 (s: file_name, c: '/'); |
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 (s: file_name, c: '/', n: 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 (dest: dirname, src: file_name, n: 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 | |
221 | file_t |
222 | __file_name_lookup (const char *file_name, int flags, mode_t mode) |
223 | { |
224 | return __file_name_lookup_at (AT_FDCWD, 0, file_name, flags, mode); |
225 | } |
226 | weak_alias (__file_name_lookup, file_name_lookup) |
227 | |
228 | |
229 | file_t |
230 | __file_name_split (const char *file_name, char **name) |
231 | { |
232 | error_t err; |
233 | file_t result; |
234 | |
235 | err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0, |
236 | file_name, &result, name); |
237 | |
238 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
239 | } |
240 | weak_alias (__file_name_split, file_name_split) |
241 | |
242 | file_t |
243 | __directory_name_split (const char *directory_name, char **name) |
244 | { |
245 | error_t err; |
246 | file_t result; |
247 | |
248 | err = __hurd_directory_name_split (&_hurd_ports_use, &__getdport, 0, |
249 | directory_name, &result, name); |
250 | |
251 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
252 | } |
253 | weak_alias (__directory_name_split, directory_name_split) |
254 | |
255 | |
256 | file_t |
257 | __file_name_lookup_under (file_t startdir, |
258 | const char *file_name, int flags, mode_t mode) |
259 | { |
260 | error_t err; |
261 | file_t result; |
262 | |
263 | error_t use_init_port (int which, error_t (*operate) (mach_port_t)) |
264 | { |
265 | return (which == INIT_PORT_CWDIR ? (*operate) (startdir) |
266 | : _hurd_ports_use (which, operate)); |
267 | } |
268 | |
269 | err = __hurd_file_name_lookup (&use_init_port, &__getdport, 0, |
270 | file_name, flags, mode & ~_hurd_umask, |
271 | &result); |
272 | |
273 | return err ? (__hurd_fail (err), MACH_PORT_NULL) : result; |
274 | } |
275 | weak_alias (__file_name_lookup_under, file_name_lookup_under) |
276 | |