source: mainline/uspace/srv/vfs/vfs_lookup.c@ 2ba48bf1

Last change on this file since 2ba48bf1 was fafb8e5, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Mechanically lowercase IPC_SET_*/IPC_GET_*

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup vfs
30 * @{
31 */
32
33/**
34 * @file vfs_lookup.c
35 * @brief
36 */
37
38#include "vfs.h"
39#include <macros.h>
40#include <async.h>
41#include <errno.h>
42#include <str.h>
43#include <stdarg.h>
44#include <stdbool.h>
45#include <fibril_synch.h>
46#include <adt/list.h>
47#include <vfs/canonify.h>
48#include <dirent.h>
49#include <assert.h>
50
51FIBRIL_MUTEX_INITIALIZE(plb_mutex);
52LIST_INITIALIZE(plb_entries); /**< PLB entry ring buffer. */
53uint8_t *plb = NULL;
54
55static errno_t plb_insert_entry(plb_entry_t *entry, char *path, size_t *start,
56 size_t len)
57{
58 fibril_mutex_lock(&plb_mutex);
59
60 link_initialize(&entry->plb_link);
61 entry->len = len;
62
63 size_t first; /* the first free index */
64 size_t last; /* the last free index */
65
66 if (list_empty(&plb_entries)) {
67 first = 0;
68 last = PLB_SIZE - 1;
69 } else {
70 plb_entry_t *oldest = list_get_instance(
71 list_first(&plb_entries), plb_entry_t, plb_link);
72 plb_entry_t *newest = list_get_instance(
73 list_last(&plb_entries), plb_entry_t, plb_link);
74
75 first = (newest->index + newest->len) % PLB_SIZE;
76 last = (oldest->index - 1) % PLB_SIZE;
77 }
78
79 if (first <= last) {
80 if ((last - first) + 1 < len) {
81 /*
82 * The buffer cannot absorb the path.
83 */
84 fibril_mutex_unlock(&plb_mutex);
85 return ELIMIT;
86 }
87 } else {
88 if (PLB_SIZE - ((first - last) + 1) < len) {
89 /*
90 * The buffer cannot absorb the path.
91 */
92 fibril_mutex_unlock(&plb_mutex);
93 return ELIMIT;
94 }
95 }
96
97 /*
98 * We know the first free index in PLB and we also know that there is
99 * enough space in the buffer to hold our path.
100 */
101
102 entry->index = first;
103 entry->len = len;
104
105 /*
106 * Claim PLB space by inserting the entry into the PLB entry ring
107 * buffer.
108 */
109 list_append(&entry->plb_link, &plb_entries);
110
111 fibril_mutex_unlock(&plb_mutex);
112
113 /*
114 * Copy the path into PLB.
115 */
116 size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
117 size_t cnt2 = len - cnt1;
118
119 memcpy(&plb[first], path, cnt1);
120 memcpy(plb, &path[cnt1], cnt2);
121
122 *start = first;
123 return EOK;
124}
125
126static void plb_clear_entry(plb_entry_t *entry, size_t first, size_t len)
127{
128 fibril_mutex_lock(&plb_mutex);
129 list_remove(&entry->plb_link);
130 /*
131 * Erasing the path from PLB will come handy for debugging purposes.
132 */
133 size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
134 size_t cnt2 = len - cnt1;
135 memset(&plb[first], 0, cnt1);
136 memset(plb, 0, cnt2);
137 fibril_mutex_unlock(&plb_mutex);
138}
139
140errno_t vfs_link_internal(vfs_node_t *base, char *path, vfs_triplet_t *child)
141{
142 assert(base != NULL);
143 assert(child != NULL);
144 assert(base->fs_handle);
145 assert(child->fs_handle);
146 assert(path != NULL);
147
148 vfs_lookup_res_t res;
149 char component[NAME_MAX + 1];
150 errno_t rc;
151
152 size_t len;
153 char *npath = canonify(path, &len);
154 if (!npath) {
155 rc = EINVAL;
156 goto out;
157 }
158 path = npath;
159
160 vfs_triplet_t *triplet;
161
162 char *slash = str_rchr(path, L'/');
163 if (slash && slash != path) {
164 if (slash[1] == 0) {
165 rc = EINVAL;
166 goto out;
167 }
168
169 memcpy(component, slash + 1, str_size(slash));
170 *slash = 0;
171
172 rc = vfs_lookup_internal(base, path, L_DIRECTORY, &res);
173 if (rc != EOK)
174 goto out;
175 triplet = &res.triplet;
176
177 *slash = '/';
178 } else {
179 if (base->mount != NULL) {
180 rc = EINVAL;
181 goto out;
182 }
183
184 memcpy(component, path + 1, str_size(path));
185 triplet = (vfs_triplet_t *) base;
186 }
187
188 if (triplet->fs_handle != child->fs_handle ||
189 triplet->service_id != child->service_id) {
190 rc = EXDEV;
191 goto out;
192 }
193
194 async_exch_t *exch = vfs_exchange_grab(triplet->fs_handle);
195 aid_t req = async_send_3(exch, VFS_OUT_LINK, triplet->service_id,
196 triplet->index, child->index, NULL);
197
198 rc = async_data_write_start(exch, component, str_size(component) + 1);
199 errno_t orig_rc;
200 async_wait_for(req, &orig_rc);
201 vfs_exchange_release(exch);
202 if (orig_rc != EOK)
203 rc = orig_rc;
204
205out:
206 return rc;
207}
208
209static errno_t out_lookup(vfs_triplet_t *base, size_t *pfirst, size_t *plen,
210 int lflag, vfs_lookup_res_t *result)
211{
212 assert(base);
213 assert(result);
214
215 errno_t rc;
216 ipc_call_t answer;
217 async_exch_t *exch = vfs_exchange_grab(base->fs_handle);
218 aid_t req = async_send_5(exch, VFS_OUT_LOOKUP, (sysarg_t) *pfirst,
219 (sysarg_t) *plen, (sysarg_t) base->service_id,
220 (sysarg_t) base->index, (sysarg_t) lflag, &answer);
221 async_wait_for(req, &rc);
222 vfs_exchange_release(exch);
223
224 if (rc != EOK)
225 return rc;
226
227 unsigned last = *pfirst + *plen;
228 *pfirst = ipc_get_arg3(&answer) & 0xffff;
229 *plen = last - *pfirst;
230
231 result->triplet.fs_handle = (fs_handle_t) ipc_get_arg1(&answer);
232 result->triplet.service_id = base->service_id;
233 result->triplet.index = (fs_index_t) ipc_get_arg2(&answer);
234 result->size = MERGE_LOUP32(ipc_get_arg4(&answer), ipc_get_arg5(&answer));
235 result->type = (ipc_get_arg3(&answer) >> 16) ?
236 VFS_NODE_DIRECTORY : VFS_NODE_FILE;
237 return EOK;
238}
239
240static errno_t _vfs_lookup_internal(vfs_node_t *base, char *path, int lflag,
241 vfs_lookup_res_t *result, size_t len)
242{
243 size_t first;
244 errno_t rc;
245
246 plb_entry_t entry;
247 rc = plb_insert_entry(&entry, path, &first, len);
248 if (rc != EOK)
249 return rc;
250
251 size_t next = first;
252 size_t nlen = len;
253
254 vfs_lookup_res_t res;
255
256 /* Resolve path as long as there are mount points to cross. */
257 while (nlen > 0) {
258 while (base->mount) {
259 if (lflag & L_DISABLE_MOUNTS) {
260 rc = EXDEV;
261 goto out;
262 }
263
264 base = base->mount;
265 }
266
267 rc = out_lookup((vfs_triplet_t *) base, &next, &nlen, lflag,
268 &res);
269 if (rc != EOK)
270 goto out;
271
272 if (nlen > 0) {
273 base = vfs_node_peek(&res);
274 if (!base) {
275 rc = ENOENT;
276 goto out;
277 }
278 if (!base->mount) {
279 vfs_node_put(base);
280 rc = ENOENT;
281 goto out;
282 }
283 vfs_node_put(base);
284 if (lflag & L_DISABLE_MOUNTS) {
285 rc = EXDEV;
286 goto out;
287 }
288 }
289 }
290
291 assert(nlen == 0);
292 rc = EOK;
293
294 if (result != NULL) {
295 /* The found file may be a mount point. Try to cross it. */
296 if (!(lflag & (L_MP | L_DISABLE_MOUNTS))) {
297 base = vfs_node_peek(&res);
298 if (base && base->mount) {
299 while (base->mount) {
300 vfs_node_addref(base->mount);
301 vfs_node_t *nbase = base->mount;
302 vfs_node_put(base);
303 base = nbase;
304 }
305
306 result->triplet = *((vfs_triplet_t *) base);
307 result->type = base->type;
308 result->size = base->size;
309 vfs_node_put(base);
310 goto out;
311 }
312 if (base)
313 vfs_node_put(base);
314 }
315
316 *result = res;
317 }
318
319out:
320 plb_clear_entry(&entry, first, len);
321 return rc;
322}
323
324/** Perform a path lookup.
325 *
326 * @param base The file from which to perform the lookup.
327 * @param path Path to be resolved; it must be a NULL-terminated
328 * string.
329 * @param lflag Flags to be used during lookup.
330 * @param result Empty structure where the lookup result will be stored.
331 * Can be NULL.
332 *
333 * @return EOK on success or an error code from errno.h.
334 *
335 */
336errno_t vfs_lookup_internal(vfs_node_t *base, char *path, int lflag,
337 vfs_lookup_res_t *result)
338{
339 assert(base != NULL);
340 assert(path != NULL);
341
342 size_t len;
343 errno_t rc;
344 char *npath = canonify(path, &len);
345 if (!npath) {
346 rc = EINVAL;
347 return rc;
348 }
349 path = npath;
350
351 assert(path[0] == '/');
352
353 if (lflag & (L_CREATE | L_UNLINK)) {
354
355 /*
356 * Creation and destruction of names must be done in two
357 * separate steps: lookup of the parent node and the name
358 * link/unlink operation itself. Otherwise the parent
359 * filesystem would not be able to tell when a mountpoint is
360 * crossed. It would attempt to perform the link/unlink in
361 * itself instead of letting the mounted filesystem do it,
362 * resulting in wrong behavior. This is the wages of server-side
363 * mountpoints.
364 */
365
366 char *slash = str_rchr(path, L'/');
367 vfs_node_t *parent = base;
368
369 if (slash != path) {
370 int tflag = lflag;
371 vfs_lookup_res_t tres;
372
373 tflag &= ~(L_CREATE | L_EXCLUSIVE | L_UNLINK | L_FILE);
374 tflag |= L_DIRECTORY;
375 rc = _vfs_lookup_internal(base, path, tflag, &tres,
376 slash - path);
377 if (rc != EOK)
378 return rc;
379 parent = vfs_node_get(&tres);
380 if (!parent)
381 return ENOMEM;
382 } else
383 vfs_node_addref(parent);
384
385 rc = _vfs_lookup_internal(parent, slash, lflag, result,
386 len - (slash - path));
387
388 vfs_node_put(parent);
389
390 } else {
391 rc = _vfs_lookup_internal(base, path, lflag, result, len);
392 }
393
394 return rc;
395}
396
397/**
398 * @}
399 */
Note: See TracBrowser for help on using the repository browser.