source: mainline/uspace/srv/vfs/vfs_lookup.c@ 1d2f85e

Last change on this file since 1d2f85e was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • 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_bytes(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_bytes(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_bytes(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.