source: mainline/uspace/srv/vfs/vfs_ops.c@ 1fe186f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1fe186f was ae78b530, checked in by Jakub Jermar <jakub@…>, 17 years ago

Introduce the notion of lflag (i.e. lookup flags) to support the ability to
limit the scope of VFS node types that can be opened by open() and opendir(). In
the future, lflag will also specify actions for VFS_LOOKUP handlers that will be
carried out in situations such as the VFS node is not found (e.g. implementation
of mkdir() and open() with O_CREAT in oflag).

  • Property mode set to 100644
File size: 14.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 fs
30 * @{
31 */
32
33/**
34 * @file vfs_ops.c
35 * @brief Operations that VFS offers to its clients.
36 */
37
38#include <ipc/ipc.h>
39#include <async.h>
40#include <errno.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <bool.h>
45#include <futex.h>
46#include <rwlock.h>
47#include <libadt/list.h>
48#include <unistd.h>
49#include <ctype.h>
50#include <assert.h>
51#include <atomic.h>
52#include "vfs.h"
53
54/**
55 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
56 * concurrent VFS operation which modifies the file system namespace.
57 */
58RWLOCK_INITIALIZE(namespace_rwlock);
59
60atomic_t rootfs_futex = FUTEX_INITIALIZER;
61vfs_triplet_t rootfs = {
62 .fs_handle = 0,
63 .dev_handle = 0,
64 .index = 0,
65};
66
67static int lookup_root(int fs_handle, int dev_handle, vfs_lookup_res_t *result)
68{
69 vfs_pair_t altroot = {
70 .fs_handle = fs_handle,
71 .dev_handle = dev_handle,
72 };
73
74 return vfs_lookup_internal("/", strlen("/"), L_DIRECTORY, result,
75 &altroot);
76}
77
78void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
79{
80 int dev_handle;
81 vfs_node_t *mp_node = NULL;
82
83 /*
84 * We expect the library to do the device-name to device-handle
85 * translation for us, thus the device handle will arrive as ARG1
86 * in the request.
87 */
88 dev_handle = IPC_GET_ARG1(*request);
89
90 /*
91 * For now, don't make use of ARG2 and ARG3, but they can be used to
92 * carry mount options in the future.
93 */
94
95 ipc_callid_t callid;
96 size_t size;
97
98 /*
99 * Now, we expect the client to send us data with the name of the file
100 * system.
101 */
102 if (!ipc_data_write_receive(&callid, &size)) {
103 ipc_answer_0(callid, EINVAL);
104 ipc_answer_0(rid, EINVAL);
105 return;
106 }
107
108 /*
109 * Don't receive more than is necessary for storing a full file system
110 * name.
111 */
112 if (size < 1 || size > FS_NAME_MAXLEN) {
113 ipc_answer_0(callid, EINVAL);
114 ipc_answer_0(rid, EINVAL);
115 return;
116 }
117
118 /*
119 * Deliver the file system name.
120 */
121 char fs_name[FS_NAME_MAXLEN + 1];
122 (void) ipc_data_write_finalize(callid, fs_name, size);
123 fs_name[size] = '\0';
124
125 /*
126 * Check if we know a file system with the same name as is in fs_name.
127 * This will also give us its file system handle.
128 */
129 int fs_handle = fs_name_to_handle(fs_name, true);
130 if (!fs_handle) {
131 ipc_answer_0(rid, ENOENT);
132 return;
133 }
134
135 /*
136 * Now, we want the client to send us the mount point.
137 */
138 if (!ipc_data_write_receive(&callid, &size)) {
139 ipc_answer_0(callid, EINVAL);
140 ipc_answer_0(rid, EINVAL);
141 return;
142 }
143
144 /*
145 * Check whether size is reasonable wrt. the mount point.
146 */
147 if (size < 1 || size > MAX_PATH_LEN) {
148 ipc_answer_0(callid, EINVAL);
149 ipc_answer_0(rid, EINVAL);
150 return;
151 }
152 /*
153 * Allocate buffer for the mount point data being received.
154 */
155 uint8_t *buf;
156 buf = malloc(size);
157 if (!buf) {
158 ipc_answer_0(callid, ENOMEM);
159 ipc_answer_0(rid, ENOMEM);
160 return;
161 }
162
163 /*
164 * Deliver the mount point.
165 */
166 (void) ipc_data_write_finalize(callid, buf, size);
167
168 /*
169 * Lookup the root node of the filesystem being mounted.
170 * In this case, we don't need to take the namespace_futex as the root
171 * node cannot be removed. However, we do take a reference to it so
172 * that we can track how many times it has been mounted.
173 */
174 int rc;
175 vfs_lookup_res_t mr_res;
176 rc = lookup_root(fs_handle, dev_handle, &mr_res);
177 if (rc != EOK) {
178 free(buf);
179 ipc_answer_0(rid, rc);
180 return;
181 }
182 vfs_node_t *mr_node = vfs_node_get(&mr_res);
183 if (!mr_node) {
184 free(buf);
185 ipc_answer_0(rid, ENOMEM);
186 return;
187 }
188
189 /*
190 * Finally, we need to resolve the path to the mountpoint.
191 */
192 vfs_lookup_res_t mp_res;
193 futex_down(&rootfs_futex);
194 if (rootfs.fs_handle) {
195 /*
196 * We already have the root FS.
197 */
198 rwlock_write_lock(&namespace_rwlock);
199 rc = vfs_lookup_internal(buf, size, L_DIRECTORY, &mp_res,
200 NULL);
201 if (rc != EOK) {
202 /*
203 * The lookup failed for some reason.
204 */
205 rwlock_write_unlock(&namespace_rwlock);
206 futex_up(&rootfs_futex);
207 vfs_node_put(mr_node); /* failed -> drop reference */
208 free(buf);
209 ipc_answer_0(rid, rc);
210 return;
211 }
212 mp_node = vfs_node_get(&mp_res);
213 if (!mp_node) {
214 rwlock_write_unlock(&namespace_rwlock);
215 futex_up(&rootfs_futex);
216 vfs_node_put(mr_node); /* failed -> drop reference */
217 free(buf);
218 ipc_answer_0(rid, ENOMEM);
219 return;
220 }
221 /*
222 * Now we hold a reference to mp_node.
223 * It will be dropped upon the corresponding VFS_UNMOUNT.
224 * This prevents the mount point from being deleted.
225 */
226 rwlock_write_unlock(&namespace_rwlock);
227 } else {
228 /*
229 * We still don't have the root file system mounted.
230 */
231 if ((size == 1) && (buf[0] == '/')) {
232 /*
233 * For this simple, but important case, we are done.
234 */
235 rootfs = mr_res.triplet;
236 futex_up(&rootfs_futex);
237 free(buf);
238 ipc_answer_0(rid, EOK);
239 return;
240 } else {
241 /*
242 * We can't resolve this without the root filesystem
243 * being mounted first.
244 */
245 futex_up(&rootfs_futex);
246 free(buf);
247 vfs_node_put(mr_node); /* failed -> drop reference */
248 ipc_answer_0(rid, ENOENT);
249 return;
250 }
251 }
252 futex_up(&rootfs_futex);
253
254 free(buf); /* The buffer is not needed anymore. */
255
256 /*
257 * At this point, we have all necessary pieces: file system and device
258 * handles, and we know the mount point VFS node and also the root node
259 * of the file system being mounted.
260 */
261
262 int phone = vfs_grab_phone(mp_res.triplet.fs_handle);
263 /* Later we can use ARG3 to pass mode/flags. */
264 aid_t req1 = async_send_3(phone, VFS_MOUNT,
265 (ipcarg_t) mp_res.triplet.dev_handle,
266 (ipcarg_t) mp_res.triplet.index, 0, NULL);
267 /* The second call uses the same method. */
268 aid_t req2 = async_send_3(phone, VFS_MOUNT,
269 (ipcarg_t) mr_res.triplet.fs_handle,
270 (ipcarg_t) mr_res.triplet.dev_handle,
271 (ipcarg_t) mr_res.triplet.index, NULL);
272 vfs_release_phone(phone);
273
274 ipcarg_t rc1;
275 ipcarg_t rc2;
276 async_wait_for(req1, &rc1);
277 async_wait_for(req2, &rc2);
278
279 if ((rc1 != EOK) || (rc2 != EOK)) {
280 /* Mount failed, drop references to mr_node and mp_node. */
281 vfs_node_put(mr_node);
282 if (mp_node)
283 vfs_node_put(mp_node);
284 }
285
286 if (rc2 == EOK)
287 ipc_answer_0(rid, rc1);
288 else if (rc1 == EOK)
289 ipc_answer_0(rid, rc2);
290 else
291 ipc_answer_0(rid, rc1);
292}
293
294void vfs_open(ipc_callid_t rid, ipc_call_t *request)
295{
296 if (!vfs_files_init()) {
297 ipc_answer_0(rid, ENOMEM);
298 return;
299 }
300
301 /*
302 * The POSIX interface is open(path, oflag, mode).
303 * We can receive oflags and mode along with the VFS_OPEN call; the path
304 * will need to arrive in another call.
305 *
306 * We also receive one private, non-POSIX set of flags called lflag
307 * used to pass information to vfs_lookup_internal().
308 */
309 int lflag = IPC_GET_ARG1(*request);
310 int oflag = IPC_GET_ARG2(*request);
311 int mode = IPC_GET_ARG3(*request);
312 size_t len;
313
314 ipc_callid_t callid;
315
316 if (!ipc_data_write_receive(&callid, &len)) {
317 ipc_answer_0(callid, EINVAL);
318 ipc_answer_0(rid, EINVAL);
319 return;
320 }
321
322 /*
323 * Now we are on the verge of accepting the path.
324 *
325 * There is one optimization we could do in the future: copy the path
326 * directly into the PLB using some kind of a callback.
327 */
328 char *path = malloc(len);
329
330 if (!path) {
331 ipc_answer_0(callid, ENOMEM);
332 ipc_answer_0(rid, ENOMEM);
333 return;
334 }
335
336 int rc;
337 if ((rc = ipc_data_write_finalize(callid, path, len))) {
338 ipc_answer_0(rid, rc);
339 free(path);
340 return;
341 }
342
343 /*
344 * Avoid the race condition in which the file can be deleted before we
345 * find/create-and-lock the VFS node corresponding to the looked-up
346 * triplet.
347 */
348 rwlock_read_lock(&namespace_rwlock);
349
350 /*
351 * The path is now populated and we can call vfs_lookup_internal().
352 */
353 vfs_lookup_res_t lr;
354 rc = vfs_lookup_internal(path, len, lflag, &lr, NULL);
355 if (rc) {
356 rwlock_read_unlock(&namespace_rwlock);
357 ipc_answer_0(rid, rc);
358 free(path);
359 return;
360 }
361
362 /*
363 * Path is no longer needed.
364 */
365 free(path);
366
367 vfs_node_t *node = vfs_node_get(&lr);
368 rwlock_read_unlock(&namespace_rwlock);
369
370 /*
371 * Get ourselves a file descriptor and the corresponding vfs_file_t
372 * structure.
373 */
374 int fd = vfs_fd_alloc();
375 if (fd < 0) {
376 vfs_node_put(node);
377 ipc_answer_0(rid, fd);
378 return;
379 }
380 vfs_file_t *file = vfs_file_get(fd);
381 file->node = node;
382
383 /*
384 * The following increase in reference count is for the fact that the
385 * file is being opened and that a file structure is pointing to it.
386 * It is necessary so that the file will not disappear when
387 * vfs_node_put() is called. The reference will be dropped by the
388 * respective VFS_CLOSE.
389 */
390 vfs_node_addref(node);
391 vfs_node_put(node);
392
393 /*
394 * Success! Return the new file descriptor to the client.
395 */
396 ipc_answer_1(rid, EOK, fd);
397}
398
399static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
400{
401
402 /*
403 * The following code strongly depends on the fact that the files data
404 * structure can be only accessed by a single fibril and all file
405 * operations are serialized (i.e. the reads and writes cannot
406 * interleave and a file cannot be closed while it is being read).
407 *
408 * Additional synchronization needs to be added once the table of
409 * open files supports parallel access!
410 */
411
412 int fd = IPC_GET_ARG1(*request);
413
414 /*
415 * Lookup the file structure corresponding to the file descriptor.
416 */
417 vfs_file_t *file = vfs_file_get(fd);
418 if (!file) {
419 ipc_answer_0(rid, ENOENT);
420 return;
421 }
422
423 /*
424 * Now we need to receive a call with client's
425 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
426 */
427 ipc_callid_t callid;
428 int res;
429 if (read)
430 res = ipc_data_read_receive(&callid, NULL);
431 else
432 res = ipc_data_write_receive(&callid, NULL);
433 if (!res) {
434 ipc_answer_0(callid, EINVAL);
435 ipc_answer_0(rid, EINVAL);
436 return;
437 }
438
439 /*
440 * Lock the open file structure so that no other thread can manipulate
441 * the same open file at a time.
442 */
443 futex_down(&file->lock);
444
445 /*
446 * Lock the file's node so that no other client can read/write to it at
447 * the same time.
448 */
449 if (read)
450 rwlock_read_lock(&file->node->contents_rwlock);
451 else
452 rwlock_write_lock(&file->node->contents_rwlock);
453
454 int fs_phone = vfs_grab_phone(file->node->fs_handle);
455
456 /*
457 * Make a VFS_READ/VFS_WRITE request at the destination FS server.
458 */
459 aid_t msg;
460 ipc_call_t answer;
461 msg = async_send_3(fs_phone, IPC_GET_METHOD(*request),
462 file->node->dev_handle, file->node->index, file->pos, &answer);
463
464 /*
465 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
466 * destination FS server. The call will be routed as if sent by
467 * ourselves. Note that call arguments are immutable in this case so we
468 * don't have to bother.
469 */
470 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
471
472 vfs_release_phone(fs_phone);
473
474 /*
475 * Wait for reply from the FS server.
476 */
477 ipcarg_t rc;
478 async_wait_for(msg, &rc);
479 size_t bytes = IPC_GET_ARG1(answer);
480
481 /*
482 * Unlock the VFS node.
483 */
484 if (read)
485 rwlock_read_unlock(&file->node->contents_rwlock);
486 else {
487 /* Update the cached version of node's size. */
488 file->node->size = IPC_GET_ARG2(answer);
489 rwlock_write_unlock(&file->node->contents_rwlock);
490 }
491
492 /*
493 * Update the position pointer and unlock the open file.
494 */
495 file->pos += bytes;
496 futex_up(&file->lock);
497
498 /*
499 * FS server's reply is the final result of the whole operation we
500 * return to the client.
501 */
502 ipc_answer_1(rid, rc, bytes);
503}
504
505void vfs_read(ipc_callid_t rid, ipc_call_t *request)
506{
507 vfs_rdwr(rid, request, true);
508}
509
510void vfs_write(ipc_callid_t rid, ipc_call_t *request)
511{
512 vfs_rdwr(rid, request, false);
513}
514
515void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
516{
517 int fd = (int) IPC_GET_ARG1(*request);
518 off_t off = (off_t) IPC_GET_ARG2(*request);
519 int whence = (int) IPC_GET_ARG3(*request);
520
521
522 /*
523 * Lookup the file structure corresponding to the file descriptor.
524 */
525 vfs_file_t *file = vfs_file_get(fd);
526 if (!file) {
527 ipc_answer_0(rid, ENOENT);
528 return;
529 }
530
531 off_t newpos;
532 futex_down(&file->lock);
533 if (whence == SEEK_SET) {
534 file->pos = off;
535 futex_up(&file->lock);
536 ipc_answer_1(rid, EOK, off);
537 return;
538 }
539 if (whence == SEEK_CUR) {
540 if (file->pos + off < file->pos) {
541 futex_up(&file->lock);
542 ipc_answer_0(rid, EOVERFLOW);
543 return;
544 }
545 file->pos += off;
546 newpos = file->pos;
547 futex_up(&file->lock);
548 ipc_answer_1(rid, EOK, newpos);
549 return;
550 }
551 if (whence == SEEK_END) {
552 rwlock_read_lock(&file->node->contents_rwlock);
553 size_t size = file->node->size;
554 rwlock_read_unlock(&file->node->contents_rwlock);
555 if (size + off < size) {
556 futex_up(&file->lock);
557 ipc_answer_0(rid, EOVERFLOW);
558 return;
559 }
560 newpos = size + off;
561 futex_up(&file->lock);
562 ipc_answer_1(rid, EOK, newpos);
563 return;
564 }
565 futex_up(&file->lock);
566 ipc_answer_0(rid, EINVAL);
567}
568
569void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
570{
571 int fd = IPC_GET_ARG1(*request);
572 size_t size = IPC_GET_ARG2(*request);
573 ipcarg_t rc;
574
575 vfs_file_t *file = vfs_file_get(fd);
576 if (!file) {
577 ipc_answer_0(rid, ENOENT);
578 return;
579 }
580 futex_down(&file->lock);
581
582 rwlock_write_lock(&file->node->contents_rwlock);
583 int fs_phone = vfs_grab_phone(file->node->fs_handle);
584 rc = async_req_3_0(fs_phone, VFS_TRUNCATE, (ipcarg_t)file->node->dev_handle,
585 (ipcarg_t)file->node->index, (ipcarg_t)size);
586 vfs_release_phone(fs_phone);
587 if (rc == EOK)
588 file->node->size = size;
589 rwlock_write_unlock(&file->node->contents_rwlock);
590
591 futex_up(&file->lock);
592 ipc_answer_0(rid, rc);
593}
594
595/**
596 * @}
597 */
Note: See TracBrowser for help on using the repository browser.