Subversion Repositories HelenOS

Rev

Rev 2690 | Rev 2693 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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