Subversion Repositories HelenOS

Rev

Rev 2698 | Rev 2708 | 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("/"), L_DIRECTORY, result,
  75.         &altroot);
  76. }
  77.  
  78. void 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.  
  294. void 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.  
  399. static 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.  
  505. void vfs_read(ipc_callid_t rid, ipc_call_t *request)
  506. {
  507.     vfs_rdwr(rid, request, true);
  508. }
  509.  
  510. void vfs_write(ipc_callid_t rid, ipc_call_t *request)
  511. {
  512.     vfs_rdwr(rid, request, false);
  513. }
  514.  
  515. void 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.  
  569. void 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.  */
  598.