Subversion Repositories HelenOS

Rev

Rev 2730 | Rev 2752 | 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_lookup.c
  35.  * @brief
  36.  */
  37.  
  38. #include <ipc/ipc.h>
  39. #include <async.h>
  40. #include <errno.h>
  41. #include <string.h>
  42. #include <stdlib.h>
  43. #include <bool.h>
  44. #include <futex.h>
  45. #include <libadt/list.h>
  46. #include <atomic.h>
  47. #include "vfs.h"
  48.  
  49. #define min(a, b)   ((a) < (b) ? (a) : (b))
  50.  
  51. /* Forward static declarations. */
  52. static char *canonify(char *path);
  53.  
  54. atomic_t plb_futex = FUTEX_INITIALIZER;
  55. link_t plb_head;    /**< PLB entry ring buffer. */
  56. uint8_t *plb = NULL;
  57.  
  58. /** Perform a path lookup.
  59.  *
  60.  * @param path      Path to be resolved; it needn't be an ASCIIZ string.
  61.  * @param len       Number of path characters pointed by path.
  62.  * @param lflag     Flags to be used during lookup.
  63.  * @param result    Empty structure where the lookup result will be stored.
  64.  *          Can be NULL.
  65.  * @param altroot   If non-empty, will be used instead of rootfs as the root
  66.  *          of the whole VFS tree.
  67.  *
  68.  * @return      EOK on success or an error code from errno.h.
  69.  */
  70. int vfs_lookup_internal(char *path, size_t len, int lflag,
  71.     vfs_lookup_res_t *result, vfs_pair_t *altroot)
  72. {
  73.     vfs_pair_t *root;
  74.  
  75.     if (!len)
  76.         return EINVAL;
  77.  
  78.     if (altroot)
  79.         root = altroot;
  80.     else
  81.         root = (vfs_pair_t *) &rootfs;
  82.  
  83.     if (!root->fs_handle)
  84.         return ENOENT;
  85.    
  86.     futex_down(&plb_futex);
  87.  
  88.     plb_entry_t entry;
  89.     link_initialize(&entry.plb_link);
  90.     entry.len = len;
  91.  
  92.     off_t first;    /* the first free index */
  93.     off_t last; /* the last free index */
  94.  
  95.     if (list_empty(&plb_head)) {
  96.         first = 0;
  97.         last = PLB_SIZE - 1;
  98.     } else {
  99.         plb_entry_t *oldest = list_get_instance(plb_head.next,
  100.             plb_entry_t, plb_link);
  101.         plb_entry_t *newest = list_get_instance(plb_head.prev,
  102.             plb_entry_t, plb_link);
  103.  
  104.         first = (newest->index + newest->len) % PLB_SIZE;
  105.         last = (oldest->index - 1) % PLB_SIZE;
  106.     }
  107.  
  108.     if (first <= last) {
  109.         if ((last - first) + 1 < len) {
  110.             /*
  111.              * The buffer cannot absorb the path.
  112.              */
  113.             futex_up(&plb_futex);
  114.             return ELIMIT;
  115.         }
  116.     } else {
  117.         if (PLB_SIZE - ((first - last) + 1) < len) {
  118.             /*
  119.              * The buffer cannot absorb the path.
  120.              */
  121.             futex_up(&plb_futex);
  122.             return ELIMIT;
  123.         }
  124.     }
  125.  
  126.     /*
  127.      * We know the first free index in PLB and we also know that there is
  128.      * enough space in the buffer to hold our path.
  129.      */
  130.  
  131.     entry.index = first;
  132.     entry.len = len;
  133.  
  134.     /*
  135.      * Claim PLB space by inserting the entry into the PLB entry ring
  136.      * buffer.
  137.      */
  138.     list_append(&entry.plb_link, &plb_head);
  139.    
  140.     futex_up(&plb_futex);
  141.  
  142.     /*
  143.      * Copy the path into PLB.
  144.      */
  145.     size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
  146.     size_t cnt2 = len - cnt1;
  147.    
  148.     memcpy(&plb[first], path, cnt1);
  149.     memcpy(plb, &path[cnt1], cnt2);
  150.  
  151.     ipc_call_t answer;
  152.     int phone = vfs_grab_phone(root->fs_handle);
  153.     aid_t req = async_send_4(phone, VFS_LOOKUP, (ipcarg_t) first,
  154.         (ipcarg_t) (first + len - 1) % PLB_SIZE,
  155.         (ipcarg_t) root->dev_handle, (ipcarg_t) lflag, &answer);
  156.     vfs_release_phone(phone);
  157.  
  158.     ipcarg_t rc;
  159.     async_wait_for(req, &rc);
  160.  
  161.     futex_down(&plb_futex);
  162.     list_remove(&entry.plb_link);
  163.     /*
  164.      * Erasing the path from PLB will come handy for debugging purposes.
  165.      */
  166.     memset(&plb[first], 0, cnt1);
  167.     memset(plb, 0, cnt2);
  168.     futex_up(&plb_futex);
  169.  
  170.     if ((rc == EOK) && result) {
  171.         result->triplet.fs_handle = (int) IPC_GET_ARG1(answer);
  172.         result->triplet.dev_handle = (int) IPC_GET_ARG2(answer);
  173.         result->triplet.index = (int) IPC_GET_ARG3(answer);
  174.         result->size = (size_t) IPC_GET_ARG4(answer);
  175.         result->lnkcnt = (unsigned) IPC_GET_ARG5(answer);
  176.     }
  177.  
  178.     return rc;
  179. }
  180.  
  181. /** Token types used for tokenization of path. */
  182. typedef enum {
  183.     TK_INVALID,
  184.     TK_SLASH,
  185.     TK_DOT,
  186.     TK_DOTDOT,
  187.     TK_COMP,
  188.     TK_NUL
  189. } tokval_t;
  190.  
  191. typedef struct {
  192.     tokval_t kind;
  193.     char *start;
  194.     char *stop;
  195. } token_t;
  196.  
  197. /** Fake up the TK_SLASH token. */
  198. static token_t slash_token(char *start)
  199. {
  200.     token_t ret;
  201.     ret.kind = TK_SLASH;
  202.     ret.start = start;
  203.     ret.stop = start;
  204.     return ret;
  205. }
  206.  
  207. /** Given a token, return the next token. */
  208. static token_t next_token(token_t *cur)
  209. {
  210.     token_t ret;
  211.  
  212.     if (cur->stop[1] == '\0') {
  213.         ret.kind = TK_NUL;
  214.         ret.start = cur->stop + 1;
  215.         ret.stop = ret.start;
  216.         return ret;
  217.     }
  218.     if (cur->stop[1] == '/') {
  219.         ret.kind = TK_SLASH;
  220.         ret.start = cur->stop + 1;
  221.         ret.stop = ret.start;
  222.         return ret;
  223.     }
  224.     if (cur->stop[1] == '.' && (!cur->stop[2] || cur->stop[2] == '/')) {
  225.         ret.kind = TK_DOT;
  226.         ret.start = cur->stop + 1;
  227.         ret.stop = ret.start;
  228.         return ret;
  229.     }
  230.     if (cur->stop[1] == '.' && cur->stop[2] == '.' &&
  231.         (!cur->stop[3] || cur->stop[3] == '/')) {
  232.         ret.kind = TK_DOTDOT;
  233.         ret.start = cur->stop + 1;
  234.         ret.stop = cur->stop + 2;
  235.         return ret;
  236.     }
  237.     unsigned i;
  238.     for (i = 1; cur->stop[i] && cur->stop[i] != '/'; i++)
  239.         ;
  240.     ret.kind = TK_COMP;
  241.     ret.start = &cur->stop[1];
  242.     ret.stop = &cur->stop[i - 1];
  243.     return ret;
  244. }
  245.  
  246. /** States used by canonify(). */
  247. typedef enum {
  248.     S_INI,
  249.     S_A,
  250.     S_B,
  251.     S_C,
  252.     S_ACCEPT,
  253.     S_RESTART,
  254.     S_REJECT
  255. } state_t;
  256.  
  257. typedef struct {
  258.     state_t s;
  259.     void (* f)(token_t *, token_t *, token_t *);
  260. } change_state_t;
  261.  
  262. /*
  263.  * Actions that can be performed when transitioning from one
  264.  * state of canonify() to another.
  265.  */
  266. static void set_first_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
  267. {
  268.     *tfsl = *t;
  269. }
  270. static void save_component(token_t *t, token_t *tfsl, token_t *tlcomp)
  271. {
  272.     *tlcomp = *t;
  273. }
  274. static void terminate_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
  275. {
  276.     tfsl->stop[1] = '\0';
  277. }
  278. static void remove_trailing_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
  279. {
  280.     t->start[-1] = '\0';
  281. }
  282. /** Eat the extra '/'..
  283.  *
  284.  * @param t     The current TK_SLASH token.
  285.  */
  286. static void shift_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
  287. {
  288.     char *p = t->start;
  289.     char *q = t->stop + 1;
  290.     while ((*p++ = *q++))
  291.         ;
  292. }
  293. /** Eat the extra '.'.
  294.  *
  295.  * @param t     The current TK_DOT token.
  296.  */
  297. static void shift_dot(token_t *t, token_t *tfsl, token_t *tlcomp)
  298. {
  299.     char *p = t->start;
  300.     char *q = t->stop + 1;
  301.     while ((*p++ = *q++))
  302.         ;
  303. }
  304. /** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
  305.  *
  306.  * @param t     The current TK_DOTDOT token.
  307.  * @param tlcomp    The last TK_COMP token.
  308.  */
  309. static void shift_dotdot(token_t *t, token_t *tfsl, token_t *tlcomp)
  310. {
  311.     char *p = tlcomp->start;
  312.     char *q = t->stop + 1;
  313.     while ((*p++ = *q++))
  314.         ;
  315. }
  316.  
  317. /** Transition function for canonify(). */
  318. static change_state_t trans[4][6] = {
  319.     [S_INI] = {
  320.         [TK_SLASH] = {
  321.             .s = S_A,
  322.             .f = set_first_slash,
  323.         },
  324.         [TK_DOT] = {
  325.             .s = S_REJECT,
  326.             .f = NULL,
  327.         },
  328.         [TK_DOTDOT] = {
  329.             .s = S_REJECT,
  330.             .f = NULL,
  331.         },
  332.         [TK_COMP] = {
  333.             .s = S_REJECT,
  334.             .f = NULL,
  335.         },
  336.         [TK_NUL] = {
  337.             .s = S_REJECT,
  338.             .f = NULL,
  339.         },
  340.         [TK_INVALID] = {
  341.             .s = S_REJECT,
  342.             .f = NULL,
  343.         },
  344.     },
  345.     [S_A] = {
  346.         [TK_SLASH] = {
  347.             .s = S_A,
  348.             .f = set_first_slash,
  349.         },
  350.         [TK_DOT] = {
  351.             .s = S_A,
  352.             .f = NULL,
  353.         },
  354.         [TK_DOTDOT] = {
  355.             .s = S_A,
  356.             .f = NULL,
  357.         },
  358.         [TK_COMP] = {
  359.             .s = S_B,
  360.             .f = save_component,
  361.         },
  362.         [TK_NUL] = {
  363.             .s = S_ACCEPT,
  364.             .f = terminate_slash,
  365.         },
  366.         [TK_INVALID] = {
  367.             .s = S_REJECT,
  368.             .f = NULL,
  369.         },
  370.     },
  371.     [S_B] = {
  372.         [TK_SLASH] = {
  373.             .s = S_C,
  374.             .f = NULL,
  375.         },
  376.         [TK_DOT] = {
  377.             .s = S_REJECT,
  378.             .f = NULL,
  379.         },
  380.         [TK_DOTDOT] = {
  381.             .s = S_REJECT,
  382.             .f = NULL,
  383.         },
  384.         [TK_COMP] = {
  385.             .s = S_REJECT,
  386.             .f = NULL,
  387.         },
  388.         [TK_NUL] = {
  389.             .s = S_ACCEPT,
  390.             .f = NULL,
  391.         },
  392.         [TK_INVALID] = {
  393.             .s = S_REJECT,
  394.             .f = NULL,
  395.         },
  396.     },
  397.     [S_C] = {
  398.         [TK_SLASH] = {
  399.             .s = S_RESTART,
  400.             .f = shift_slash,
  401.         },
  402.         [TK_DOT] = {
  403.             .s = S_RESTART,
  404.             .f = shift_dot,
  405.         },
  406.         [TK_DOTDOT] = {
  407.             .s = S_RESTART,
  408.             .f = shift_dotdot,
  409.         },
  410.         [TK_COMP] = {
  411.             .s = S_B,
  412.             .f = save_component,
  413.         },
  414.         [TK_NUL] = {
  415.             .s = S_ACCEPT,
  416.             .f = remove_trailing_slash,
  417.         },
  418.         [TK_INVALID] = {
  419.             .s = S_REJECT,
  420.             .f = NULL,
  421.         },
  422.     }
  423. };
  424.  
  425. /** Canonify a file system path.
  426.  *
  427.  * A file system path is canonical, if the following holds:
  428.  * 1) the path is absolute (i.e. a/b/c is not canonical)
  429.  * 2) there is no trailing slash in the path (i.e. /a/b/c is not canonical)
  430.  * 3) there is no extra slash in the path (i.e. /a//b/c is not canonical)
  431.  * 4) there is no '.' component in the path (i.e. /a/./b/c is not canonical)
  432.  * 5) there is no '..' component in the path (i.e. /a/b/../c is not canonical)
  433.  *
  434.  * This function makes a potentially non-canonical file system path canonical.
  435.  * It works in-place and requires a NULL-terminated input string.
  436.  *
  437.  * @param       Path to be canonified.
  438.  *
  439.  * @return      Canonified path or NULL on failure.
  440.  */
  441. char *canonify(char *path)
  442. {
  443.     state_t state;
  444.     token_t t;
  445.     token_t tfsl;       /* first slash */
  446.     token_t tlcomp;     /* last component */
  447.     if (*path != '/')
  448.         return NULL;
  449.     tfsl = slash_token(path);
  450. restart:
  451.     state = S_INI;
  452.     t = tfsl;
  453.     while (state != S_ACCEPT && state != S_RESTART && state != S_REJECT) {
  454.         if (trans[state][t.kind].f)
  455.             trans[state][t.kind].f(&t, &tfsl, &tlcomp);
  456.         state = trans[state][t.kind].s;
  457.         t = next_token(&t);
  458.     }
  459.    
  460.     switch (state) {
  461.     case S_RESTART:
  462.         goto restart;
  463.     case S_REJECT:
  464.         return NULL;
  465.     case S_ACCEPT:
  466.         return tfsl.start;
  467.     default:
  468.         abort();
  469.     }
  470. }
  471.  
  472. /**
  473.  * @}
  474.  */
  475.