Subversion Repositories HelenOS

Rev

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