Subversion Repositories HelenOS

Rev

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