Subversion Repositories HelenOS

Rev

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