Subversion Repositories HelenOS

Rev

Rev 2857 | Rev 2859 | 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    fat_ops.c
  35.  * @brief   Implementation of VFS operations for the FAT file system server.
  36.  */
  37.  
  38. #include "fat.h"
  39. #include "../../vfs/vfs.h"
  40. #include <libfs.h>
  41. #include <ipc/ipc.h>
  42. #include <async.h>
  43. #include <errno.h>
  44. #include <string.h>
  45. #include <byteorder.h>
  46. #include <libadt/hash_table.h>
  47. #include <libadt/list.h>
  48. #include <assert.h>
  49. #include <futex.h>
  50.  
  51. #define BS_BLOCK        0
  52.  
  53. #define FIN_KEY_DEV_HANDLE  0
  54. #define FIN_KEY_INDEX       1
  55.  
  56. /** Futex protecting both fin_hash and ffn_head. */
  57. futex_t fin_futex = FUTEX_INITIALIZER;
  58.  
  59. /** Hash table of FAT in-core nodes. */
  60. hash_table_t fin_hash;
  61.  
  62. /** List of free FAT in-core nodes. */
  63. link_t ffn_head;
  64.  
  65. #define FAT_NAME_LEN        8
  66. #define FAT_EXT_LEN     3
  67.  
  68. #define FAT_PAD         ' '
  69.  
  70. #define FAT_DENTRY_UNUSED   0x00
  71. #define FAT_DENTRY_E5_ESC   0x05
  72. #define FAT_DENTRY_DOT      0x2e
  73. #define FAT_DENTRY_ERASED   0xe5
  74.  
  75. static void dentry_name_canonify(fat_dentry_t *d, char *buf)
  76. {
  77.     int i;
  78.  
  79.     for (i = 0; i < FAT_NAME_LEN; i++) {
  80.         if (d->name[i] == FAT_PAD) {
  81.             buf++;
  82.             break;
  83.         }
  84.         if (d->name[i] == FAT_DENTRY_E5_ESC)
  85.             *buf++ = 0xe5;
  86.         else
  87.             *buf++ = d->name[i];
  88.     }
  89.     if (d->ext[0] != FAT_PAD)
  90.         *buf++ = '.';
  91.     for (i = 0; i < FAT_EXT_LEN; i++) {
  92.         if (d->ext[i] == FAT_PAD) {
  93.             *buf = '\0';
  94.             return;
  95.         }
  96.         if (d->ext[i] == FAT_DENTRY_E5_ESC)
  97.             *buf++ = 0xe5;
  98.         else
  99.             *buf++ = d->ext[i];
  100.     }
  101. }
  102.  
  103. /* TODO and also move somewhere else */
  104. typedef struct {
  105.     void *data;
  106. } block_t;
  107.  
  108. static block_t *block_get(dev_handle_t dev_handle, off_t offset)
  109. {
  110.     return NULL;    /* TODO */
  111. }
  112.  
  113. static block_t *fat_block_get(dev_handle_t dev_handle, fs_index_t index,
  114.     off_t offset) {
  115.     return NULL;    /* TODO */
  116. }
  117.  
  118. static void block_put(block_t *block)
  119. {
  120.     /* TODO */
  121. }
  122.  
  123. static void fat_node_initialize(fat_node_t *node)
  124. {
  125.     futex_initialize(&node->lock, 1);
  126.     node->type = 0;
  127.     node->index = 0;
  128.     node->pindex = 0;
  129.     node->dev_handle = 0;
  130.     link_initialize(&node->fin_link);
  131.     link_initialize(&node->ffn_link);
  132.     node->size = 0;
  133.     node->lnkcnt = 0;
  134.     node->refcnt = 0;
  135.     node->dirty = false;
  136. }
  137.  
  138. static uint16_t fat_bps_get(dev_handle_t dev_handle)
  139. {
  140.     block_t *bb;
  141.     uint16_t bps;
  142.    
  143.     bb = block_get(dev_handle, BS_BLOCK);
  144.     assert(bb != NULL);
  145.     bps = uint16_t_le2host(((fat_bs_t *)bb->data)->bps);
  146.     block_put(bb);
  147.  
  148.     return bps;
  149. }
  150.  
  151. typedef enum {
  152.     FAT_DENTRY_SKIP,
  153.     FAT_DENTRY_LAST,
  154.     FAT_DENTRY_VALID
  155. } fat_dentry_clsf_t;
  156.  
  157. static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
  158. {
  159.     if (d->attr & FAT_ATTR_VOLLABEL) {
  160.         /* volume label entry */
  161.         return FAT_DENTRY_SKIP;
  162.     }
  163.     if (d->name[0] == FAT_DENTRY_ERASED) {
  164.         /* not-currently-used entry */
  165.         return FAT_DENTRY_SKIP;
  166.     }
  167.     if (d->name[0] == FAT_DENTRY_UNUSED) {
  168.         /* never used entry */
  169.         return FAT_DENTRY_LAST;
  170.     }
  171.     if (d->name[0] == FAT_DENTRY_DOT) {
  172.         /*
  173.          * Most likely '.' or '..'.
  174.          * It cannot occur in a regular file name.
  175.          */
  176.         return FAT_DENTRY_SKIP;
  177.     }
  178.     return FAT_DENTRY_VALID;
  179. }
  180.  
  181. static void fat_sync_node(fat_node_t *node)
  182. {
  183.     /* TODO */
  184. }
  185.  
  186. /** Instantiate a FAT in-core node.
  187.  *
  188.  * FAT stores the info necessary for instantiation of a node in the parent of
  189.  * that node.  This design necessitated the addition of the parent node index
  190.  * parameter to this otherwise generic libfs API.
  191.  */
  192. static void *
  193. fat_node_get(dev_handle_t dev_handle, fs_index_t index, fs_index_t pindex)
  194. {
  195.     link_t *lnk;
  196.     fat_node_t *node = NULL;
  197.     block_t *b;
  198.     unsigned bps;
  199.     unsigned dps;
  200.     fat_dentry_t *d;
  201.     unsigned i, j;
  202.  
  203.     unsigned long key[] = {
  204.         [FIN_KEY_DEV_HANDLE] = dev_handle,
  205.         [FIN_KEY_INDEX] = index
  206.     };
  207.  
  208.     futex_down(&fin_futex);
  209.     lnk = hash_table_find(&fin_hash, key);
  210.     if (lnk) {
  211.         /*
  212.          * The in-core node was found in the hash table.
  213.          */
  214.         node = hash_table_get_instance(lnk, fat_node_t, fin_link);
  215.         if (!node->refcnt++)
  216.             list_remove(&node->ffn_link);
  217.         futex_up(&fin_futex);
  218.        
  219.         /* Make sure that the node is fully instantiated. */
  220.         futex_down(&node->lock);
  221.         futex_up(&node->lock);
  222.        
  223.         return (void *) node;  
  224.     }
  225.  
  226.     bps = fat_bps_get(dev_handle);
  227.     dps = bps / sizeof(fat_dentry_t);
  228.    
  229.     if (!list_empty(&ffn_head)) {
  230.         /*
  231.          * We are going to reuse a node from the free list.
  232.          */
  233.         lnk = ffn_head.next;
  234.         list_remove(lnk);
  235.         node = list_get_instance(lnk, fat_node_t, ffn_link);
  236.         assert(!node->refcnt);
  237.         if (node->dirty)
  238.             fat_sync_node(node);
  239.         key[FIN_KEY_DEV_HANDLE] = node->dev_handle;
  240.         key[FIN_KEY_INDEX] = node->index;
  241.         hash_table_remove(&fin_hash, key, sizeof(key)/sizeof(*key));
  242.     } else {
  243.         /*
  244.          * We need to allocate a new node.
  245.          */
  246.         node = malloc(sizeof(fat_node_t));
  247.         if (!node)
  248.             return NULL;
  249.     }
  250.     fat_node_initialize(node);
  251.     node->refcnt++;
  252.     node->lnkcnt++;
  253.     node->dev_handle = dev_handle;
  254.     node->index = index;
  255.     node->pindex = pindex;
  256.     key[FIN_KEY_DEV_HANDLE] = node->dev_handle;
  257.     key[FIN_KEY_INDEX] = node->index;
  258.     hash_table_insert(&fin_hash, key, &node->fin_link);
  259.  
  260.     /*
  261.      * We have already put the node back to fin_hash.
  262.      * The node is not yet fully instantiated so we lock it prior to
  263.      * unlocking fin_hash.
  264.      */
  265.     futex_down(&node->lock);
  266.     futex_up(&fin_futex);
  267.  
  268.     /*
  269.      * Because of the design of the FAT file system, we have no clue about
  270.      * how big (i.e. how many directory entries it contains) is the parent
  271.      * of the node we are trying to instantiate.  However, we know that it
  272.      * must contain a directory entry for our node of interest.  We simply
  273.      * scan the parent until we find it.
  274.      */
  275.     for (i = 0; ; i++) {
  276.         b = fat_block_get(node->dev_handle, node->pindex, i);
  277.         for (j = 0; j < dps; j++) {
  278.             d = ((fat_dentry_t *)b->data) + j;
  279.             if (d->firstc == node->index)
  280.                 goto found;
  281.         }
  282.         block_put(b);
  283.     }
  284.    
  285. found:
  286.     if (!(d->attr & (FAT_ATTR_SUBDIR | FAT_ATTR_VOLLABEL)))
  287.         node->type = FAT_FILE;
  288.     if ((d->attr & FAT_ATTR_SUBDIR) || !index)
  289.         node->type = FAT_DIRECTORY;
  290.     assert((node->type == FAT_FILE) || (node->type == FAT_DIRECTORY));
  291.    
  292.     node->size = uint32_t_le2host(d->size);
  293.     block_put(b);
  294.  
  295.     futex_up(&node->lock);
  296.     return node;
  297. }
  298.  
  299. static void fat_node_put(void *node)
  300. {
  301.     fat_node_t *nodep = (fat_node_t *)node;
  302.  
  303.     futex_down(&fin_futex);
  304.     if (!--nodep->refcnt)
  305.         list_append(&nodep->ffn_link, &ffn_head);
  306.     futex_up(&fin_futex);
  307. }
  308.  
  309. static void *fat_create(int flags)
  310. {
  311.     return NULL;    /* not supported at the moment */
  312. }
  313.  
  314. static int fat_destroy(void *node)
  315. {
  316.     return ENOTSUP; /* not supported at the moment */
  317. }
  318.  
  319. static bool fat_link(void *prnt, void *chld, const char *name)
  320. {
  321.     return false;   /* not supported at the moment */
  322. }
  323.  
  324. static int fat_unlink(void *prnt, void *chld)
  325. {
  326.     return ENOTSUP; /* not supported at the moment */
  327. }
  328.  
  329. static void *fat_match(void *prnt, const char *component)
  330. {
  331.     fat_node_t *parentp = (fat_node_t *)prnt;
  332.     char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
  333.     unsigned i, j;
  334.     unsigned bps;       /* bytes per sector */
  335.     unsigned dps;       /* dentries per sector */
  336.     unsigned blocks;
  337.     fat_dentry_t *d;
  338.     block_t *b;
  339.  
  340.     bps = fat_bps_get(parentp->dev_handle);
  341.     dps = bps / sizeof(fat_dentry_t);
  342.     blocks = parentp->size / bps + (parentp->size % bps != 0);
  343.     for (i = 0; i < blocks; i++) {
  344.         unsigned dentries;
  345.        
  346.         b = fat_block_get(parentp->dev_handle, parentp->index, i);
  347.         dentries = (i == blocks - 1) ?
  348.             parentp->size % sizeof(fat_dentry_t) :
  349.             dps;
  350.         for (j = 0; j < dentries; j++) {
  351.             d = ((fat_dentry_t *)b->data) + j;
  352.             switch (fat_classify_dentry(d)) {
  353.             case FAT_DENTRY_SKIP:
  354.                 continue;
  355.             case FAT_DENTRY_LAST:
  356.                 block_put(b);
  357.                 return NULL;
  358.             default:
  359.             case FAT_DENTRY_VALID:
  360.                 dentry_name_canonify(d, name);
  361.                 break;
  362.             }
  363.             if (strcmp(name, component) == 0) {
  364.                 /* hit */
  365.                 void *node = fat_node_get(parentp->dev_handle,
  366.                     (fs_index_t)uint16_t_le2host(d->firstc),
  367.                     parentp->index);
  368.                 block_put(b);
  369.                 return node;
  370.             }
  371.         }
  372.         block_put(b);
  373.     }
  374.  
  375.     return NULL;
  376. }
  377.  
  378. static fs_index_t fat_index_get(void *node)
  379. {
  380.     fat_node_t *fnodep = (fat_node_t *)node;
  381.     if (!fnodep)
  382.         return 0;
  383.     return fnodep->index;
  384. }
  385.  
  386. static size_t fat_size_get(void *node)
  387. {
  388.     return ((fat_node_t *)node)->size;
  389. }
  390.  
  391. static unsigned fat_lnkcnt_get(void *node)
  392. {
  393.     return ((fat_node_t *)node)->lnkcnt;
  394. }
  395.  
  396. static bool fat_has_children(void *node)
  397. {
  398.     fat_node_t *nodep = (fat_node_t *)node;
  399.     unsigned bps;
  400.     unsigned dps;
  401.     unsigned blocks;
  402.     block_t *b;
  403.     unsigned i, j;
  404.  
  405.     if (nodep->type != FAT_DIRECTORY)
  406.         return false;
  407.  
  408.     bps = fat_bps_get(nodep->dev_handle);
  409.     dps = bps / sizeof(fat_dentry_t);
  410.  
  411.     blocks = nodep->size / bps + (nodep->size % bps != 0);
  412.  
  413.     for (i = 0; i < blocks; i++) {
  414.         unsigned dentries;
  415.         fat_dentry_t *d;
  416.    
  417.         b = fat_block_get(nodep->dev_handle, nodep->index, i);
  418.         dentries = (i == blocks - 1) ?
  419.             nodep->size % sizeof(fat_dentry_t) :
  420.             dps;
  421.         for (j = 0; j < dentries; j++) {
  422.             d = ((fat_dentry_t *)b->data) + j;
  423.             switch (fat_classify_dentry(d)) {
  424.             case FAT_DENTRY_SKIP:
  425.                 continue;
  426.             case FAT_DENTRY_LAST:
  427.                 block_put(b);
  428.                 return false;
  429.             default:
  430.             case FAT_DENTRY_VALID:
  431.                 block_put(b);
  432.                 return true;
  433.             }
  434.             block_put(b);
  435.             return true;
  436.         }
  437.         block_put(b);
  438.     }
  439.  
  440.     return false;
  441. }
  442.  
  443. static void *fat_root_get(dev_handle_t dev_handle)
  444. {
  445.     return fat_node_get(dev_handle, 0, 0); 
  446. }
  447.  
  448. static char fat_plb_get_char(unsigned pos)
  449. {
  450.     return fat_reg.plb_ro[pos % PLB_SIZE];
  451. }
  452.  
  453. static bool fat_is_directory(void *node)
  454. {
  455.     return ((fat_node_t *)node)->type == FAT_DIRECTORY;
  456. }
  457.  
  458. static bool fat_is_file(void *node)
  459. {
  460.     return ((fat_node_t *)node)->type == FAT_FILE;
  461. }
  462.  
  463. /** libfs operations */
  464. libfs_ops_t fat_libfs_ops = {
  465.     .match = fat_match,
  466.     .node_get = fat_node_get,
  467.     .node_put = fat_node_put,
  468.     .create = fat_create,
  469.     .destroy = fat_destroy,
  470.     .link = fat_link,
  471.     .unlink = fat_unlink,
  472.     .index_get = fat_index_get,
  473.     .size_get = fat_size_get,
  474.     .lnkcnt_get = fat_lnkcnt_get,
  475.     .has_children = fat_has_children,
  476.     .root_get = fat_root_get,
  477.     .plb_get_char = fat_plb_get_char,
  478.     .is_directory = fat_is_directory,
  479.     .is_file = fat_is_file
  480. };
  481.  
  482. void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
  483. {
  484.     libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
  485. }
  486.  
  487. /**
  488.  * @}
  489.  */
  490.