source: mainline/uspace/srv/vfs/vfs_ops.c@ 861e7d1

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

Merge all VFS operations into one file.

  • Property mode set to 100644
File size: 26.0 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 <ipc/services.h>
40#include <async.h>
41#include <fibril.h>
42#include <errno.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <bool.h>
47#include <futex.h>
48#include <rwlock.h>
49#include <libadt/list.h>
50#include <unistd.h>
51#include <ctype.h>
52#include <as.h>
53#include <assert.h>
54#include <atomic.h>
55#include "vfs.h"
56
57#define min(a, b) ((a) < (b) ? (a) : (b))
58
59/**
60 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
61 * concurrent VFS operation which modifies the file system namespace.
62 */
63RWLOCK_INITIALIZE(namespace_rwlock);
64
65atomic_t plb_futex = FUTEX_INITIALIZER;
66link_t plb_head; /**< PLB entry ring buffer. */
67uint8_t *plb = NULL;
68
69/** Perform a path lookup.
70 *
71 * @param path Path to be resolved; it needn't be an ASCIIZ string.
72 * @param len Number of path characters pointed by path.
73 * @param result Empty node structure where the result will be stored.
74 * @param size Storage where the size of the node will be stored. Can
75 * be NULL.
76 * @param altroot If non-empty, will be used instead of rootfs as the root
77 * of the whole VFS tree.
78 *
79 * @return EOK on success or an error code from errno.h.
80 */
81int vfs_lookup_internal(char *path, size_t len, vfs_triplet_t *result,
82 size_t *size, vfs_pair_t *altroot)
83{
84 vfs_pair_t *root;
85
86 if (!len)
87 return EINVAL;
88
89 if (altroot)
90 root = altroot;
91 else
92 root = (vfs_pair_t *) &rootfs;
93
94 if (!root->fs_handle)
95 return ENOENT;
96
97 futex_down(&plb_futex);
98
99 plb_entry_t entry;
100 link_initialize(&entry.plb_link);
101 entry.len = len;
102
103 off_t first; /* the first free index */
104 off_t last; /* the last free index */
105
106 if (list_empty(&plb_head)) {
107 first = 0;
108 last = PLB_SIZE - 1;
109 } else {
110 plb_entry_t *oldest = list_get_instance(plb_head.next,
111 plb_entry_t, plb_link);
112 plb_entry_t *newest = list_get_instance(plb_head.prev,
113 plb_entry_t, plb_link);
114
115 first = (newest->index + newest->len) % PLB_SIZE;
116 last = (oldest->index - 1) % PLB_SIZE;
117 }
118
119 if (first <= last) {
120 if ((last - first) + 1 < len) {
121 /*
122 * The buffer cannot absorb the path.
123 */
124 futex_up(&plb_futex);
125 return ELIMIT;
126 }
127 } else {
128 if (PLB_SIZE - ((first - last) + 1) < len) {
129 /*
130 * The buffer cannot absorb the path.
131 */
132 futex_up(&plb_futex);
133 return ELIMIT;
134 }
135 }
136
137 /*
138 * We know the first free index in PLB and we also know that there is
139 * enough space in the buffer to hold our path.
140 */
141
142 entry.index = first;
143 entry.len = len;
144
145 /*
146 * Claim PLB space by inserting the entry into the PLB entry ring
147 * buffer.
148 */
149 list_append(&entry.plb_link, &plb_head);
150
151 futex_up(&plb_futex);
152
153 /*
154 * Copy the path into PLB.
155 */
156 size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
157 size_t cnt2 = len - cnt1;
158
159 memcpy(&plb[first], path, cnt1);
160 memcpy(plb, &path[cnt1], cnt2);
161
162 ipc_call_t answer;
163 int phone = vfs_grab_phone(root->fs_handle);
164 aid_t req = async_send_3(phone, VFS_LOOKUP, (ipcarg_t) first,
165 (ipcarg_t) (first + len - 1) % PLB_SIZE,
166 (ipcarg_t) root->dev_handle, &answer);
167 vfs_release_phone(phone);
168
169 ipcarg_t rc;
170 async_wait_for(req, &rc);
171
172 futex_down(&plb_futex);
173 list_remove(&entry.plb_link);
174 /*
175 * Erasing the path from PLB will come handy for debugging purposes.
176 */
177 memset(&plb[first], 0, cnt1);
178 memset(plb, 0, cnt2);
179 futex_up(&plb_futex);
180
181 if (rc == EOK) {
182 result->fs_handle = (int) IPC_GET_ARG1(answer);
183 result->dev_handle = (int) IPC_GET_ARG2(answer);
184 result->index = (int) IPC_GET_ARG3(answer);
185 if (size)
186 *size = (size_t) IPC_GET_ARG4(answer);
187 }
188
189 return rc;
190}
191
192atomic_t rootfs_futex = FUTEX_INITIALIZER;
193vfs_triplet_t rootfs = {
194 .fs_handle = 0,
195 .dev_handle = 0,
196 .index = 0,
197};
198
199static int lookup_root(int fs_handle, int dev_handle, vfs_triplet_t *root,
200 size_t *size)
201{
202 vfs_pair_t altroot = {
203 .fs_handle = fs_handle,
204 .dev_handle = dev_handle,
205 };
206
207 return vfs_lookup_internal("/", strlen("/"), root, size, &altroot);
208}
209
210void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
211{
212 int dev_handle;
213 vfs_node_t *mp_node = NULL;
214
215 /*
216 * We expect the library to do the device-name to device-handle
217 * translation for us, thus the device handle will arrive as ARG1
218 * in the request.
219 */
220 dev_handle = IPC_GET_ARG1(*request);
221
222 /*
223 * For now, don't make use of ARG2 and ARG3, but they can be used to
224 * carry mount options in the future.
225 */
226
227 ipc_callid_t callid;
228 size_t size;
229
230 /*
231 * Now, we expect the client to send us data with the name of the file
232 * system.
233 */
234 if (!ipc_data_write_receive(&callid, &size)) {
235 ipc_answer_0(callid, EINVAL);
236 ipc_answer_0(rid, EINVAL);
237 return;
238 }
239
240 /*
241 * Don't receive more than is necessary for storing a full file system
242 * name.
243 */
244 if (size < 1 || size > FS_NAME_MAXLEN) {
245 ipc_answer_0(callid, EINVAL);
246 ipc_answer_0(rid, EINVAL);
247 return;
248 }
249
250 /*
251 * Deliver the file system name.
252 */
253 char fs_name[FS_NAME_MAXLEN + 1];
254 (void) ipc_data_write_finalize(callid, fs_name, size);
255 fs_name[size] = '\0';
256
257 /*
258 * Check if we know a file system with the same name as is in fs_name.
259 * This will also give us its file system handle.
260 */
261 int fs_handle = fs_name_to_handle(fs_name, true);
262 if (!fs_handle) {
263 ipc_answer_0(rid, ENOENT);
264 return;
265 }
266
267 /*
268 * Now, we want the client to send us the mount point.
269 */
270 if (!ipc_data_write_receive(&callid, &size)) {
271 ipc_answer_0(callid, EINVAL);
272 ipc_answer_0(rid, EINVAL);
273 return;
274 }
275
276 /*
277 * Check whether size is reasonable wrt. the mount point.
278 */
279 if (size < 1 || size > MAX_PATH_LEN) {
280 ipc_answer_0(callid, EINVAL);
281 ipc_answer_0(rid, EINVAL);
282 return;
283 }
284 /*
285 * Allocate buffer for the mount point data being received.
286 */
287 uint8_t *buf;
288 buf = malloc(size);
289 if (!buf) {
290 ipc_answer_0(callid, ENOMEM);
291 ipc_answer_0(rid, ENOMEM);
292 return;
293 }
294
295 /*
296 * Deliver the mount point.
297 */
298 (void) ipc_data_write_finalize(callid, buf, size);
299
300 /*
301 * Lookup the root node of the filesystem being mounted.
302 * In this case, we don't need to take the namespace_futex as the root
303 * node cannot be removed. However, we do take a reference to it so
304 * that we can track how many times it has been mounted.
305 */
306 int rc;
307 vfs_triplet_t mounted_root;
308 size_t mrsz;
309 rc = lookup_root(fs_handle, dev_handle, &mounted_root, &mrsz);
310 if (rc != EOK) {
311 free(buf);
312 ipc_answer_0(rid, rc);
313 return;
314 }
315 vfs_node_t *mr_node = vfs_node_get(&mounted_root, mrsz);
316 if (!mr_node) {
317 free(buf);
318 ipc_answer_0(rid, ENOMEM);
319 return;
320 }
321
322 /*
323 * Finally, we need to resolve the path to the mountpoint.
324 */
325 vfs_triplet_t mp;
326 size_t mpsz;
327 futex_down(&rootfs_futex);
328 if (rootfs.fs_handle) {
329 /*
330 * We already have the root FS.
331 */
332 rwlock_write_lock(&namespace_rwlock);
333 rc = vfs_lookup_internal(buf, size, &mp, &mpsz, NULL);
334 if (rc != EOK) {
335 /*
336 * The lookup failed for some reason.
337 */
338 rwlock_write_unlock(&namespace_rwlock);
339 futex_up(&rootfs_futex);
340 vfs_node_put(mr_node); /* failed -> drop reference */
341 free(buf);
342 ipc_answer_0(rid, rc);
343 return;
344 }
345 mp_node = vfs_node_get(&mp, mpsz);
346 if (!mp_node) {
347 rwlock_write_unlock(&namespace_rwlock);
348 futex_up(&rootfs_futex);
349 vfs_node_put(mr_node); /* failed -> drop reference */
350 free(buf);
351 ipc_answer_0(rid, ENOMEM);
352 return;
353 }
354 /*
355 * Now we hold a reference to mp_node.
356 * It will be dropped upon the corresponding VFS_UNMOUNT.
357 * This prevents the mount point from being deleted.
358 */
359 rwlock_write_unlock(&namespace_rwlock);
360 } else {
361 /*
362 * We still don't have the root file system mounted.
363 */
364 if ((size == 1) && (buf[0] == '/')) {
365 /*
366 * For this simple, but important case, we are done.
367 */
368 rootfs = mounted_root;
369 futex_up(&rootfs_futex);
370 free(buf);
371 ipc_answer_0(rid, EOK);
372 return;
373 } else {
374 /*
375 * We can't resolve this without the root filesystem
376 * being mounted first.
377 */
378 futex_up(&rootfs_futex);
379 free(buf);
380 vfs_node_put(mr_node); /* failed -> drop reference */
381 ipc_answer_0(rid, ENOENT);
382 return;
383 }
384 }
385 futex_up(&rootfs_futex);
386
387 free(buf); /* The buffer is not needed anymore. */
388
389 /*
390 * At this point, we have all necessary pieces: file system and device
391 * handles, and we know the mount point VFS node and also the root node
392 * of the file system being mounted.
393 */
394
395 int phone = vfs_grab_phone(mp.fs_handle);
396 /* Later we can use ARG3 to pass mode/flags. */
397 aid_t req1 = async_send_3(phone, VFS_MOUNT, (ipcarg_t) mp.dev_handle,
398 (ipcarg_t) mp.index, 0, NULL);
399 /* The second call uses the same method. */
400 aid_t req2 = async_send_3(phone, VFS_MOUNT,
401 (ipcarg_t) mounted_root.fs_handle,
402 (ipcarg_t) mounted_root.dev_handle, (ipcarg_t) mounted_root.index,
403 NULL);
404 vfs_release_phone(phone);
405
406 ipcarg_t rc1;
407 ipcarg_t rc2;
408 async_wait_for(req1, &rc1);
409 async_wait_for(req2, &rc2);
410
411 if ((rc1 != EOK) || (rc2 != EOK)) {
412 /* Mount failed, drop references to mr_node and mp_node. */
413 vfs_node_put(mr_node);
414 if (mp_node)
415 vfs_node_put(mp_node);
416 }
417
418 if (rc2 == EOK)
419 ipc_answer_0(rid, rc1);
420 else if (rc1 == EOK)
421 ipc_answer_0(rid, rc2);
422 else
423 ipc_answer_0(rid, rc1);
424}
425
426void vfs_open(ipc_callid_t rid, ipc_call_t *request)
427{
428 if (!vfs_files_init()) {
429 ipc_answer_0(rid, ENOMEM);
430 return;
431 }
432
433 /*
434 * The POSIX interface is open(path, flags, mode).
435 * We can receive flags and mode along with the VFS_OPEN call; the path
436 * will need to arrive in another call.
437 */
438 int flags = IPC_GET_ARG1(*request);
439 int mode = IPC_GET_ARG2(*request);
440 size_t len;
441
442 ipc_callid_t callid;
443
444 if (!ipc_data_write_receive(&callid, &len)) {
445 ipc_answer_0(callid, EINVAL);
446 ipc_answer_0(rid, EINVAL);
447 return;
448 }
449
450 /*
451 * Now we are on the verge of accepting the path.
452 *
453 * There is one optimization we could do in the future: copy the path
454 * directly into the PLB using some kind of a callback.
455 */
456 char *path = malloc(len);
457
458 if (!path) {
459 ipc_answer_0(callid, ENOMEM);
460 ipc_answer_0(rid, ENOMEM);
461 return;
462 }
463
464 int rc;
465 if ((rc = ipc_data_write_finalize(callid, path, len))) {
466 ipc_answer_0(rid, rc);
467 free(path);
468 return;
469 }
470
471 /*
472 * Avoid the race condition in which the file can be deleted before we
473 * find/create-and-lock the VFS node corresponding to the looked-up
474 * triplet.
475 */
476 rwlock_read_lock(&namespace_rwlock);
477
478 /*
479 * The path is now populated and we can call vfs_lookup_internal().
480 */
481 vfs_triplet_t triplet;
482 size_t size;
483 rc = vfs_lookup_internal(path, len, &triplet, &size, NULL);
484 if (rc) {
485 rwlock_read_unlock(&namespace_rwlock);
486 ipc_answer_0(rid, rc);
487 free(path);
488 return;
489 }
490
491 /*
492 * Path is no longer needed.
493 */
494 free(path);
495
496 vfs_node_t *node = vfs_node_get(&triplet, size);
497 rwlock_read_unlock(&namespace_rwlock);
498
499 /*
500 * Get ourselves a file descriptor and the corresponding vfs_file_t
501 * structure.
502 */
503 int fd = vfs_fd_alloc();
504 if (fd < 0) {
505 vfs_node_put(node);
506 ipc_answer_0(rid, fd);
507 return;
508 }
509 vfs_file_t *file = vfs_file_get(fd);
510 file->node = node;
511
512 /*
513 * The following increase in reference count is for the fact that the
514 * file is being opened and that a file structure is pointing to it.
515 * It is necessary so that the file will not disappear when
516 * vfs_node_put() is called. The reference will be dropped by the
517 * respective VFS_CLOSE.
518 */
519 vfs_node_addref(node);
520 vfs_node_put(node);
521
522 /*
523 * Success! Return the new file descriptor to the client.
524 */
525 ipc_answer_1(rid, EOK, fd);
526}
527
528static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
529{
530
531 /*
532 * The following code strongly depends on the fact that the files data
533 * structure can be only accessed by a single fibril and all file
534 * operations are serialized (i.e. the reads and writes cannot
535 * interleave and a file cannot be closed while it is being read).
536 *
537 * Additional synchronization needs to be added once the table of
538 * open files supports parallel access!
539 */
540
541 int fd = IPC_GET_ARG1(*request);
542
543 /*
544 * Lookup the file structure corresponding to the file descriptor.
545 */
546 vfs_file_t *file = vfs_file_get(fd);
547 if (!file) {
548 ipc_answer_0(rid, ENOENT);
549 return;
550 }
551
552 /*
553 * Now we need to receive a call with client's
554 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
555 */
556 ipc_callid_t callid;
557 int res;
558 if (read)
559 res = ipc_data_read_receive(&callid, NULL);
560 else
561 res = ipc_data_write_receive(&callid, NULL);
562 if (!res) {
563 ipc_answer_0(callid, EINVAL);
564 ipc_answer_0(rid, EINVAL);
565 return;
566 }
567
568 /*
569 * Lock the open file structure so that no other thread can manipulate
570 * the same open file at a time.
571 */
572 futex_down(&file->lock);
573
574 /*
575 * Lock the file's node so that no other client can read/write to it at
576 * the same time.
577 */
578 if (read)
579 rwlock_read_lock(&file->node->contents_rwlock);
580 else
581 rwlock_write_lock(&file->node->contents_rwlock);
582
583 int fs_phone = vfs_grab_phone(file->node->fs_handle);
584
585 /*
586 * Make a VFS_READ/VFS_WRITE request at the destination FS server.
587 */
588 aid_t msg;
589 ipc_call_t answer;
590 msg = async_send_3(fs_phone, IPC_GET_METHOD(*request),
591 file->node->dev_handle, file->node->index, file->pos, &answer);
592
593 /*
594 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
595 * destination FS server. The call will be routed as if sent by
596 * ourselves. Note that call arguments are immutable in this case so we
597 * don't have to bother.
598 */
599 ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
600
601 vfs_release_phone(fs_phone);
602
603 /*
604 * Wait for reply from the FS server.
605 */
606 ipcarg_t rc;
607 async_wait_for(msg, &rc);
608 size_t bytes = IPC_GET_ARG1(answer);
609
610 /*
611 * Unlock the VFS node.
612 */
613 if (read)
614 rwlock_read_unlock(&file->node->contents_rwlock);
615 else {
616 /* Update the cached version of node's size. */
617 file->node->size = IPC_GET_ARG2(answer);
618 rwlock_write_unlock(&file->node->contents_rwlock);
619 }
620
621 /*
622 * Update the position pointer and unlock the open file.
623 */
624 file->pos += bytes;
625 futex_up(&file->lock);
626
627 /*
628 * FS server's reply is the final result of the whole operation we
629 * return to the client.
630 */
631 ipc_answer_1(rid, rc, bytes);
632}
633
634void vfs_read(ipc_callid_t rid, ipc_call_t *request)
635{
636 vfs_rdwr(rid, request, true);
637}
638
639void vfs_write(ipc_callid_t rid, ipc_call_t *request)
640{
641 vfs_rdwr(rid, request, false);
642}
643
644void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
645{
646 int fd = (int) IPC_GET_ARG1(*request);
647 off_t off = (off_t) IPC_GET_ARG2(*request);
648 int whence = (int) IPC_GET_ARG3(*request);
649
650
651 /*
652 * Lookup the file structure corresponding to the file descriptor.
653 */
654 vfs_file_t *file = vfs_file_get(fd);
655 if (!file) {
656 ipc_answer_0(rid, ENOENT);
657 return;
658 }
659
660 off_t newpos;
661 futex_down(&file->lock);
662 if (whence == SEEK_SET) {
663 file->pos = off;
664 futex_up(&file->lock);
665 ipc_answer_1(rid, EOK, off);
666 return;
667 }
668 if (whence == SEEK_CUR) {
669 if (file->pos + off < file->pos) {
670 futex_up(&file->lock);
671 ipc_answer_0(rid, EOVERFLOW);
672 return;
673 }
674 file->pos += off;
675 newpos = file->pos;
676 futex_up(&file->lock);
677 ipc_answer_1(rid, EOK, newpos);
678 return;
679 }
680 if (whence == SEEK_END) {
681 rwlock_read_lock(&file->node->contents_rwlock);
682 size_t size = file->node->size;
683 rwlock_read_unlock(&file->node->contents_rwlock);
684 if (size + off < size) {
685 futex_up(&file->lock);
686 ipc_answer_0(rid, EOVERFLOW);
687 return;
688 }
689 newpos = size + off;
690 futex_up(&file->lock);
691 ipc_answer_1(rid, EOK, newpos);
692 return;
693 }
694 futex_up(&file->lock);
695 ipc_answer_0(rid, EINVAL);
696}
697
698atomic_t fs_head_futex = FUTEX_INITIALIZER;
699link_t fs_head;
700
701atomic_t fs_handle_next = {
702 .count = 1
703};
704
705/** Verify the VFS info structure.
706 *
707 * @param info Info structure to be verified.
708 *
709 * @return Non-zero if the info structure is sane, zero otherwise.
710 */
711static bool vfs_info_sane(vfs_info_t *info)
712{
713 int i;
714
715 /*
716 * Check if the name is non-empty and is composed solely of ASCII
717 * characters [a-z]+[a-z0-9_-]*.
718 */
719 if (!islower(info->name[0])) {
720 dprintf("The name doesn't start with a lowercase character.\n");
721 return false;
722 }
723 for (i = 1; i < FS_NAME_MAXLEN; i++) {
724 if (!(islower(info->name[i]) || isdigit(info->name[i])) &&
725 (info->name[i] != '-') && (info->name[i] != '_')) {
726 if (info->name[i] == '\0') {
727 break;
728 } else {
729 dprintf("The name contains illegal "
730 "characters.\n");
731 return false;
732 }
733 }
734 }
735 /*
736 * This check is not redundant. It ensures that the name is
737 * NULL-terminated, even if FS_NAME_MAXLEN characters are used.
738 */
739 if (info->name[i] != '\0') {
740 dprintf("The name is not properly NULL-terminated.\n");
741 return false;
742 }
743
744
745 /*
746 * Check if the FS implements mandatory VFS operations.
747 */
748 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_REGISTER)] != VFS_OP_DEFINED) {
749 dprintf("Operation VFS_REGISTER not defined by the client.\n");
750 return false;
751 }
752 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_MOUNT)] != VFS_OP_DEFINED) {
753 dprintf("Operation VFS_MOUNT not defined by the client.\n");
754 return false;
755 }
756 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_UNMOUNT)] != VFS_OP_DEFINED) {
757 dprintf("Operation VFS_UNMOUNT not defined by the client.\n");
758 return false;
759 }
760 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_LOOKUP)] != VFS_OP_DEFINED) {
761 dprintf("Operation VFS_LOOKUP not defined by the client.\n");
762 return false;
763 }
764 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_OPEN)] != VFS_OP_DEFINED) {
765 dprintf("Operation VFS_OPEN not defined by the client.\n");
766 return false;
767 }
768 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_CLOSE)] != VFS_OP_DEFINED) {
769 dprintf("Operation VFS_CLOSE not defined by the client.\n");
770 return false;
771 }
772 if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_READ)] != VFS_OP_DEFINED) {
773 dprintf("Operation VFS_READ not defined by the client.\n");
774 return false;
775 }
776
777 /*
778 * Check if each operation is either not defined, defined or default.
779 */
780 for (i = VFS_FIRST; i < VFS_LAST; i++) {
781 if ((info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_NULL) &&
782 (info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFAULT) &&
783 (info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFINED)) {
784 dprintf("Operation info not understood.\n");
785 return false;
786 }
787 }
788 return true;
789}
790
791/** VFS_REGISTER protocol function.
792 *
793 * @param rid Hash of the call with the request.
794 * @param request Call structure with the request.
795 */
796void vfs_register(ipc_callid_t rid, ipc_call_t *request)
797{
798 ipc_callid_t callid;
799 ipc_call_t call;
800 int rc;
801 size_t size;
802
803 dprintf("Processing VFS_REGISTER request received from %p.\n",
804 request->in_phone_hash);
805
806 /*
807 * The first call has to be IPC_M_DATA_SEND in which we receive the
808 * VFS info structure from the client FS.
809 */
810 if (!ipc_data_write_receive(&callid, &size)) {
811 /*
812 * The client doesn't obey the same protocol as we do.
813 */
814 dprintf("Receiving of VFS info failed.\n");
815 ipc_answer_0(callid, EINVAL);
816 ipc_answer_0(rid, EINVAL);
817 return;
818 }
819
820 dprintf("VFS info received, size = %d\n", size);
821
822 /*
823 * We know the size of the VFS info structure. See if the client
824 * understands this easy concept too.
825 */
826 if (size != sizeof(vfs_info_t)) {
827 /*
828 * The client is sending us something, which cannot be
829 * the info structure.
830 */
831 dprintf("Received VFS info has bad size.\n");
832 ipc_answer_0(callid, EINVAL);
833 ipc_answer_0(rid, EINVAL);
834 return;
835 }
836
837 /*
838 * Allocate and initialize a buffer for the fs_info structure.
839 */
840 fs_info_t *fs_info;
841 fs_info = (fs_info_t *) malloc(sizeof(fs_info_t));
842 if (!fs_info) {
843 dprintf("Could not allocate memory for FS info.\n");
844 ipc_answer_0(callid, ENOMEM);
845 ipc_answer_0(rid, ENOMEM);
846 return;
847 }
848 link_initialize(&fs_info->fs_link);
849 futex_initialize(&fs_info->phone_futex, 1);
850
851 rc = ipc_data_write_finalize(callid, &fs_info->vfs_info, size);
852 if (rc != EOK) {
853 dprintf("Failed to deliver the VFS info into our AS, rc=%d.\n",
854 rc);
855 free(fs_info);
856 ipc_answer_0(callid, rc);
857 ipc_answer_0(rid, rc);
858 return;
859 }
860
861 dprintf("VFS info delivered.\n");
862
863 if (!vfs_info_sane(&fs_info->vfs_info)) {
864 free(fs_info);
865 ipc_answer_0(callid, EINVAL);
866 ipc_answer_0(rid, EINVAL);
867 return;
868 }
869
870 futex_down(&fs_head_futex);
871
872 /*
873 * Check for duplicit registrations.
874 */
875 if (fs_name_to_handle(fs_info->vfs_info.name, false)) {
876 /*
877 * We already register a fs like this.
878 */
879 dprintf("FS is already registered.\n");
880 futex_up(&fs_head_futex);
881 free(fs_info);
882 ipc_answer_0(callid, EEXISTS);
883 ipc_answer_0(rid, EEXISTS);
884 return;
885 }
886
887 /*
888 * Add fs_info to the list of registered FS's.
889 */
890 dprintf("Inserting FS into the list of registered file systems.\n");
891 list_append(&fs_info->fs_link, &fs_head);
892
893 /*
894 * Now we want the client to send us the IPC_M_CONNECT_TO_ME call so
895 * that a callback connection is created and we have a phone through
896 * which to forward VFS requests to it.
897 */
898 callid = async_get_call(&call);
899 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
900 dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
901 list_remove(&fs_info->fs_link);
902 futex_up(&fs_head_futex);
903 free(fs_info);
904 ipc_answer_0(callid, EINVAL);
905 ipc_answer_0(rid, EINVAL);
906 return;
907 }
908 fs_info->phone = IPC_GET_ARG5(call);
909 ipc_answer_0(callid, EOK);
910
911 dprintf("Callback connection to FS created.\n");
912
913 /*
914 * The client will want us to send him the address space area with PLB.
915 */
916
917 if (!ipc_share_in_receive(&callid, &size)) {
918 dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
919 list_remove(&fs_info->fs_link);
920 futex_up(&fs_head_futex);
921 ipc_hangup(fs_info->phone);
922 free(fs_info);
923 ipc_answer_0(callid, EINVAL);
924 ipc_answer_0(rid, EINVAL);
925 return;
926 }
927
928 /*
929 * We can only send the client address space area PLB_SIZE bytes long.
930 */
931 if (size != PLB_SIZE) {
932 dprintf("Client suggests wrong size of PFB, size = %d\n", size);
933 list_remove(&fs_info->fs_link);
934 futex_up(&fs_head_futex);
935 ipc_hangup(fs_info->phone);
936 free(fs_info);
937 ipc_answer_0(callid, EINVAL);
938 ipc_answer_0(rid, EINVAL);
939 return;
940 }
941
942 /*
943 * Commit to read-only sharing the PLB with the client.
944 */
945 (void) ipc_share_in_finalize(callid, plb,
946 AS_AREA_READ | AS_AREA_CACHEABLE);
947
948 dprintf("Sharing PLB.\n");
949
950 /*
951 * That was it. The FS has been registered.
952 * In reply to the VFS_REGISTER request, we assign the client file
953 * system a global file system handle.
954 */
955 fs_info->fs_handle = (int) atomic_postinc(&fs_handle_next);
956 ipc_answer_1(rid, EOK, (ipcarg_t) fs_info->fs_handle);
957
958 futex_up(&fs_head_futex);
959
960 dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n",
961 FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle);
962}
963
964/** For a given file system handle, implement policy for allocating a phone.
965 *
966 * @param handle File system handle.
967 *
968 * @return Phone over which a multi-call request can be safely
969 * sent. Return 0 if no phone was found.
970 */
971int vfs_grab_phone(int handle)
972{
973 /*
974 * For now, we don't try to be very clever and very fast.
975 * We simply lookup the phone in the fs_head list. We currently don't
976 * open any additional phones (even though that itself would be pretty
977 * straightforward; housekeeping multiple open phones to a FS task would
978 * be more demanding). Instead, we simply take the respective
979 * phone_futex and keep it until vfs_release_phone().
980 */
981 futex_down(&fs_head_futex);
982 link_t *cur;
983 fs_info_t *fs;
984 for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
985 fs = list_get_instance(cur, fs_info_t, fs_link);
986 if (fs->fs_handle == handle) {
987 futex_up(&fs_head_futex);
988 /*
989 * For now, take the futex unconditionally.
990 * Oh yeah, serialization rocks.
991 * It will be up'ed in vfs_release_phone().
992 */
993 futex_down(&fs->phone_futex);
994 /*
995 * Avoid deadlock with other fibrils in the same thread
996 * by disabling fibril preemption.
997 */
998 fibril_inc_sercount();
999 return fs->phone;
1000 }
1001 }
1002 futex_up(&fs_head_futex);
1003 return 0;
1004}
1005
1006/** Tell VFS that the phone is in use for any request.
1007 *
1008 * @param phone Phone to FS task.
1009 */
1010void vfs_release_phone(int phone)
1011{
1012 bool found = false;
1013
1014 /*
1015 * Undo the fibril_inc_sercount() done in vfs_grab_phone().
1016 */
1017 fibril_dec_sercount();
1018
1019 futex_down(&fs_head_futex);
1020 link_t *cur;
1021 for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
1022 fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
1023 if (fs->phone == phone) {
1024 found = true;
1025 futex_up(&fs_head_futex);
1026 futex_up(&fs->phone_futex);
1027 return;
1028 }
1029 }
1030 futex_up(&fs_head_futex);
1031
1032 /*
1033 * Not good to get here.
1034 */
1035 assert(found == true);
1036}
1037
1038/** Convert file system name to its handle.
1039 *
1040 * @param name File system name.
1041 * @param lock If true, the function will down and up the
1042 * fs_head_futex.
1043 *
1044 * @return File system handle or zero if file system not found.
1045 */
1046int fs_name_to_handle(char *name, bool lock)
1047{
1048 int handle = 0;
1049
1050 if (lock)
1051 futex_down(&fs_head_futex);
1052 link_t *cur;
1053 for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
1054 fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
1055 if (strncmp(fs->vfs_info.name, name,
1056 sizeof(fs->vfs_info.name)) == 0) {
1057 handle = fs->fs_handle;
1058 break;
1059 }
1060 }
1061 if (lock)
1062 futex_up(&fs_head_futex);
1063 return handle;
1064}
1065
1066/**
1067 * @}
1068 */
Note: See TracBrowser for help on using the repository browser.