Subversion Repositories HelenOS

Rev

Rev 3119 | Rev 3257 | 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. #define BS_SIZE         512
  53.  
  54. /** Futex protecting the list of cached free FAT nodes. */
  55. static futex_t ffn_futex = FUTEX_INITIALIZER;
  56.  
  57. /** List of cached free FAT nodes. */
  58. static LIST_INITIALIZE(ffn_head);
  59.  
  60. #define FAT_NAME_LEN        8
  61. #define FAT_EXT_LEN     3
  62.  
  63. #define FAT_PAD         ' '
  64.  
  65. #define FAT_DENTRY_UNUSED   0x00
  66. #define FAT_DENTRY_E5_ESC   0x05
  67. #define FAT_DENTRY_DOT      0x2e
  68. #define FAT_DENTRY_ERASED   0xe5
  69.  
  70. static void dentry_name_canonify(fat_dentry_t *d, char *buf)
  71. {
  72.     int i;
  73.  
  74.     for (i = 0; i < FAT_NAME_LEN; i++) {
  75.         if (d->name[i] == FAT_PAD) {
  76.             buf++;
  77.             break;
  78.         }
  79.         if (d->name[i] == FAT_DENTRY_E5_ESC)
  80.             *buf++ = 0xe5;
  81.         else
  82.             *buf++ = d->name[i];
  83.     }
  84.     if (d->ext[0] != FAT_PAD)
  85.         *buf++ = '.';
  86.     for (i = 0; i < FAT_EXT_LEN; i++) {
  87.         if (d->ext[i] == FAT_PAD) {
  88.             *buf = '\0';
  89.             return;
  90.         }
  91.         if (d->ext[i] == FAT_DENTRY_E5_ESC)
  92.             *buf++ = 0xe5;
  93.         else
  94.             *buf++ = d->ext[i];
  95.     }
  96. }
  97.  
  98. /* TODO move somewhere else */
  99. typedef struct {
  100.     void *data;
  101. } block_t;
  102.  
  103. static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
  104. {
  105.     return NULL;    /* TODO */
  106. }
  107.  
  108. static void block_put(block_t *block)
  109. {
  110.     /* TODO */
  111. }
  112.  
  113. #define FAT_BS(b)       ((fat_bs_t *)((b)->data))
  114.  
  115. #define FAT_CLST_RES0   0x0000
  116. #define FAT_CLST_RES1   0x0001
  117. #define FAT_CLST_FIRST  0x0002
  118. #define FAT_CLST_BAD    0xfff7
  119. #define FAT_CLST_LAST1  0xfff8
  120. #define FAT_CLST_LAST8  0xffff
  121.  
  122. /* internally used to mark root directory's parent */
  123. #define FAT_CLST_ROOTPAR    FAT_CLST_RES0
  124. /* internally used to mark root directory */
  125. #define FAT_CLST_ROOT       FAT_CLST_RES1
  126.  
  127. #define fat_block_get(np, off) \
  128.     _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
  129.  
  130. static block_t *
  131. _fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
  132. {
  133.     block_t *bb;
  134.     block_t *b;
  135.     unsigned bps;
  136.     unsigned spc;
  137.     unsigned rscnt;     /* block address of the first FAT */
  138.     unsigned fatcnt;
  139.     unsigned rde;
  140.     unsigned rds;       /* root directory size */
  141.     unsigned sf;
  142.     unsigned ssa;       /* size of the system area */
  143.     unsigned clusters;
  144.     fat_cluster_t clst = firstc;
  145.     unsigned i;
  146.  
  147.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  148.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  149.     spc = FAT_BS(bb)->spc;
  150.     rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
  151.     fatcnt = FAT_BS(bb)->fatcnt;
  152.     rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
  153.     sf = uint16_t_le2host(FAT_BS(bb)->sec_per_fat);
  154.     block_put(bb);
  155.  
  156.     rds = (sizeof(fat_dentry_t) * rde) / bps;
  157.     rds += ((sizeof(fat_dentry_t) * rde) % bps != 0);
  158.     ssa = rscnt + fatcnt * sf + rds;
  159.  
  160.     if (firstc == FAT_CLST_ROOT) {
  161.         /* root directory special case */
  162.         assert(offset < rds);
  163.         b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
  164.         return b;
  165.     }
  166.  
  167.     clusters = offset / spc;
  168.     for (i = 0; i < clusters; i++) {
  169.         unsigned fsec;  /* sector offset relative to FAT1 */
  170.         unsigned fidx;  /* FAT1 entry index */
  171.  
  172.         assert(clst >= FAT_CLST_FIRST && clst < FAT_CLST_BAD);
  173.         fsec = (clst * sizeof(fat_cluster_t)) / bps;
  174.         fidx = clst % (bps / sizeof(fat_cluster_t));
  175.         /* read FAT1 */
  176.         b = block_get(dev_handle, rscnt + fsec, bps);
  177.         clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
  178.         assert(clst != FAT_CLST_BAD);
  179.         assert(clst < FAT_CLST_LAST1);
  180.         block_put(b);
  181.     }
  182.  
  183.     b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
  184.         offset % spc, bps);
  185.  
  186.     return b;
  187. }
  188.  
  189. static void fat_node_initialize(fat_node_t *node)
  190. {
  191.     futex_initialize(&node->lock, 1);
  192.     node->idx = NULL;
  193.     node->type = 0;
  194.     link_initialize(&node->ffn_link);
  195.     node->size = 0;
  196.     node->lnkcnt = 0;
  197.     node->refcnt = 0;
  198.     node->dirty = false;
  199. }
  200.  
  201. static uint16_t fat_bps_get(dev_handle_t dev_handle)
  202. {
  203.     block_t *bb;
  204.     uint16_t bps;
  205.    
  206.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  207.     assert(bb != NULL);
  208.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  209.     block_put(bb);
  210.  
  211.     return bps;
  212. }
  213.  
  214. typedef enum {
  215.     FAT_DENTRY_SKIP,
  216.     FAT_DENTRY_LAST,
  217.     FAT_DENTRY_VALID
  218. } fat_dentry_clsf_t;
  219.  
  220. static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
  221. {
  222.     if (d->attr & FAT_ATTR_VOLLABEL) {
  223.         /* volume label entry */
  224.         return FAT_DENTRY_SKIP;
  225.     }
  226.     if (d->name[0] == FAT_DENTRY_ERASED) {
  227.         /* not-currently-used entry */
  228.         return FAT_DENTRY_SKIP;
  229.     }
  230.     if (d->name[0] == FAT_DENTRY_UNUSED) {
  231.         /* never used entry */
  232.         return FAT_DENTRY_LAST;
  233.     }
  234.     if (d->name[0] == FAT_DENTRY_DOT) {
  235.         /*
  236.          * Most likely '.' or '..'.
  237.          * It cannot occur in a regular file name.
  238.          */
  239.         return FAT_DENTRY_SKIP;
  240.     }
  241.     return FAT_DENTRY_VALID;
  242. }
  243.  
  244. static void fat_node_sync(fat_node_t *node)
  245. {
  246.     /* TODO */
  247. }
  248.  
  249. /** Internal version of fat_node_get().
  250.  *
  251.  * @param idxp      Locked index structure.
  252.  */
  253. static void *fat_node_get_core(fat_idx_t *idxp)
  254. {
  255.     block_t *b;
  256.     fat_dentry_t *d;
  257.     fat_node_t *nodep;
  258.     unsigned bps;
  259.     unsigned dps;
  260.  
  261.     if (idxp->nodep) {
  262.         /*
  263.          * We are lucky.
  264.          * The node is already instantiated in memory.
  265.          */
  266.         futex_down(&idxp->nodep->lock);
  267.         if (!idxp->nodep->refcnt++)
  268.             list_remove(&nodep->ffn_link);
  269.         futex_up(&idxp->nodep->lock);
  270.         return idxp->nodep;
  271.     }
  272.  
  273.     /*
  274.      * We must instantiate the node from the file system.
  275.      */
  276.    
  277.     assert(idxp->pfc);
  278.  
  279.     futex_down(&ffn_futex);
  280.     if (!list_empty(&ffn_head)) {
  281.         /* Try to use a cached free node structure. */
  282.         fat_idx_t *idxp_tmp;
  283.         nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
  284.         if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
  285.             goto skip_cache;
  286.         idxp_tmp = nodep->idx;
  287.         if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
  288.             futex_up(&nodep->lock);
  289.             goto skip_cache;
  290.         }
  291.         list_remove(&nodep->ffn_link);
  292.         futex_up(&ffn_futex);
  293.         if (nodep->dirty)
  294.             fat_node_sync(nodep);
  295.         idxp_tmp->nodep = NULL;
  296.         futex_up(&nodep->lock);
  297.         futex_up(&idxp_tmp->lock);
  298.     } else {
  299. skip_cache:
  300.         /* Try to allocate a new node structure. */
  301.         futex_up(&ffn_futex);
  302.         nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
  303.         if (!nodep)
  304.             return NULL;
  305.     }
  306.     fat_node_initialize(nodep);
  307.  
  308.     bps = fat_bps_get(idxp->dev_handle);
  309.     dps = bps / sizeof(fat_dentry_t);
  310.  
  311.     /* Read the block that contains the dentry of interest. */
  312.     b = _fat_block_get(idxp->dev_handle, idxp->pfc,
  313.         (idxp->pdi * sizeof(fat_dentry_t)) / bps);
  314.     assert(b);
  315.  
  316.     d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
  317.     if (d->attr & FAT_ATTR_SUBDIR) {
  318.         /*
  319.          * The only directory which does not have this bit set is the
  320.          * root directory itself. The root directory node is handled
  321.          * and initialized elsewhere.
  322.          */
  323.         nodep->type = FAT_DIRECTORY;
  324.     } else {
  325.         nodep->type = FAT_FILE;
  326.     }
  327.     nodep->firstc = uint16_t_le2host(d->firstc);
  328.     nodep->size = uint32_t_le2host(d->size);
  329.     nodep->lnkcnt = 1;
  330.     nodep->refcnt = 1;
  331.  
  332.     block_put(b);
  333.  
  334.     /* Link the idx structure with the node structure. */
  335.     nodep->idx = idxp;
  336.     idxp->nodep = nodep;
  337.  
  338.     return nodep;
  339. }
  340.  
  341. /** Instantiate a FAT in-core node. */
  342. static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
  343. {
  344.     void *node;
  345.     fat_idx_t *idxp;
  346.  
  347.     idxp = fat_idx_get_by_index(dev_handle, index);
  348.     if (!idxp)
  349.         return NULL;
  350.     /* idxp->lock held */
  351.     node = fat_node_get_core(idxp);
  352.     futex_up(&idxp->lock);
  353.     return node;
  354. }
  355.  
  356. static void fat_node_put(void *node)
  357. {
  358.     fat_node_t *nodep = (fat_node_t *)node;
  359.  
  360.     futex_down(&nodep->lock);
  361.     if (!--nodep->refcnt) {
  362.         futex_down(&ffn_futex);
  363.         list_append(&nodep->ffn_link, &ffn_head);
  364.         futex_up(&ffn_futex);
  365.     }
  366.     futex_up(&nodep->lock);
  367. }
  368.  
  369. static void *fat_create(int flags)
  370. {
  371.     return NULL;    /* not supported at the moment */
  372. }
  373.  
  374. static int fat_destroy(void *node)
  375. {
  376.     return ENOTSUP; /* not supported at the moment */
  377. }
  378.  
  379. static bool fat_link(void *prnt, void *chld, const char *name)
  380. {
  381.     return false;   /* not supported at the moment */
  382. }
  383.  
  384. static int fat_unlink(void *prnt, void *chld)
  385. {
  386.     return ENOTSUP; /* not supported at the moment */
  387. }
  388.  
  389. static void *fat_match(void *prnt, const char *component)
  390. {
  391.     fat_node_t *parentp = (fat_node_t *)prnt;
  392.     char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
  393.     unsigned i, j;
  394.     unsigned bps;       /* bytes per sector */
  395.     unsigned dps;       /* dentries per sector */
  396.     unsigned blocks;
  397.     fat_dentry_t *d;
  398.     block_t *b;
  399.  
  400.     futex_down(&parentp->idx->lock);
  401.     bps = fat_bps_get(parentp->idx->dev_handle);
  402.     dps = bps / sizeof(fat_dentry_t);
  403.     blocks = parentp->size / bps + (parentp->size % bps != 0);
  404.     for (i = 0; i < blocks; i++) {
  405.         unsigned dentries;
  406.        
  407.         b = fat_block_get(parentp, i);
  408.         dentries = (i == blocks - 1) ?
  409.             parentp->size % sizeof(fat_dentry_t) :
  410.             dps;
  411.         for (j = 0; j < dentries; j++) {
  412.             d = ((fat_dentry_t *)b->data) + j;
  413.             switch (fat_classify_dentry(d)) {
  414.             case FAT_DENTRY_SKIP:
  415.                 continue;
  416.             case FAT_DENTRY_LAST:
  417.                 block_put(b);
  418.                 futex_up(&parentp->idx->lock);
  419.                 return NULL;
  420.             default:
  421.             case FAT_DENTRY_VALID:
  422.                 dentry_name_canonify(d, name);
  423.                 break;
  424.             }
  425.             if (strcmp(name, component) == 0) {
  426.                 /* hit */
  427.                 void *node;
  428.                 /*
  429.                  * Assume tree hierarchy for locking.  We
  430.                  * already have the parent and now we are going
  431.                  * to lock the child.  Never lock in the oposite
  432.                  * order.
  433.                  */
  434.                 fat_idx_t *idx = fat_idx_get_by_pos(
  435.                     parentp->idx->dev_handle, parentp->firstc,
  436.                     i * dps + j);
  437.                 futex_up(&parentp->idx->lock);
  438.                 if (!idx) {
  439.                     /*
  440.                      * Can happen if memory is low or if we
  441.                      * run out of 32-bit indices.
  442.                      */
  443.                     block_put(b);
  444.                     return NULL;
  445.                 }
  446.                 node = fat_node_get_core(idx);
  447.                 futex_up(&idx->lock);
  448.                 block_put(b);
  449.                 return node;
  450.             }
  451.         }
  452.         block_put(b);
  453.     }
  454.     futex_up(&parentp->idx->lock);
  455.     return NULL;
  456. }
  457.  
  458. static fs_index_t fat_index_get(void *node)
  459. {
  460.     fat_node_t *fnodep = (fat_node_t *)node;
  461.     if (!fnodep)
  462.         return 0;
  463.     return fnodep->idx->index;
  464. }
  465.  
  466. static size_t fat_size_get(void *node)
  467. {
  468.     return ((fat_node_t *)node)->size;
  469. }
  470.  
  471. static unsigned fat_lnkcnt_get(void *node)
  472. {
  473.     return ((fat_node_t *)node)->lnkcnt;
  474. }
  475.  
  476. static bool fat_has_children(void *node)
  477. {
  478.     fat_node_t *nodep = (fat_node_t *)node;
  479.     unsigned bps;
  480.     unsigned dps;
  481.     unsigned blocks;
  482.     block_t *b;
  483.     unsigned i, j;
  484.  
  485.     if (nodep->type != FAT_DIRECTORY)
  486.         return false;
  487.  
  488.     futex_down(&nodep->idx->lock);
  489.     bps = fat_bps_get(nodep->idx->dev_handle);
  490.     dps = bps / sizeof(fat_dentry_t);
  491.  
  492.     blocks = nodep->size / bps + (nodep->size % bps != 0);
  493.  
  494.     for (i = 0; i < blocks; i++) {
  495.         unsigned dentries;
  496.         fat_dentry_t *d;
  497.    
  498.         b = fat_block_get(nodep, i);
  499.         dentries = (i == blocks - 1) ?
  500.             nodep->size % sizeof(fat_dentry_t) :
  501.             dps;
  502.         for (j = 0; j < dentries; j++) {
  503.             d = ((fat_dentry_t *)b->data) + j;
  504.             switch (fat_classify_dentry(d)) {
  505.             case FAT_DENTRY_SKIP:
  506.                 continue;
  507.             case FAT_DENTRY_LAST:
  508.                 block_put(b);
  509.                 futex_up(&nodep->idx->lock);
  510.                 return false;
  511.             default:
  512.             case FAT_DENTRY_VALID:
  513.                 block_put(b);
  514.                 futex_up(&nodep->idx->lock);
  515.                 return true;
  516.             }
  517.             block_put(b);
  518.             futex_up(&nodep->idx->lock);
  519.             return true;
  520.         }
  521.         block_put(b);
  522.     }
  523.  
  524.     futex_up(&nodep->idx->lock);
  525.     return false;
  526. }
  527.  
  528. static void *fat_root_get(dev_handle_t dev_handle)
  529. {
  530.     return fat_node_get(dev_handle, 0);
  531. }
  532.  
  533. static char fat_plb_get_char(unsigned pos)
  534. {
  535.     return fat_reg.plb_ro[pos % PLB_SIZE];
  536. }
  537.  
  538. static bool fat_is_directory(void *node)
  539. {
  540.     return ((fat_node_t *)node)->type == FAT_DIRECTORY;
  541. }
  542.  
  543. static bool fat_is_file(void *node)
  544. {
  545.     return ((fat_node_t *)node)->type == FAT_FILE;
  546. }
  547.  
  548. /** libfs operations */
  549. libfs_ops_t fat_libfs_ops = {
  550.     .match = fat_match,
  551.     .node_get = fat_node_get,
  552.     .node_put = fat_node_put,
  553.     .create = fat_create,
  554.     .destroy = fat_destroy,
  555.     .link = fat_link,
  556.     .unlink = fat_unlink,
  557.     .index_get = fat_index_get,
  558.     .size_get = fat_size_get,
  559.     .lnkcnt_get = fat_lnkcnt_get,
  560.     .has_children = fat_has_children,
  561.     .root_get = fat_root_get,
  562.     .plb_get_char = fat_plb_get_char,
  563.     .is_directory = fat_is_directory,
  564.     .is_file = fat_is_file
  565. };
  566.  
  567. void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
  568. {
  569.     dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
  570.     block_t *bb;
  571.     uint16_t rde;
  572.     int rc;
  573.  
  574.     /* Read the number of root directory entries. */
  575.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  576.     rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
  577.     block_put(bb);
  578.  
  579.     rc = fat_idx_init_by_dev_handle(dev_handle);
  580.     if (rc != EOK) {
  581.         ipc_answer_0(rid, rc);
  582.         return;
  583.     }
  584.  
  585.     /* Initialize the root node. */
  586.     fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
  587.     if (!rootp) {
  588.         fat_idx_fini_by_dev_handle(dev_handle);
  589.         ipc_answer_0(rid, ENOMEM);
  590.         return;
  591.     }
  592.     fat_node_initialize(rootp);
  593.  
  594.     fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
  595.     if (!ridxp) {
  596.         free(rootp);
  597.         fat_idx_fini_by_dev_handle(dev_handle);
  598.         ipc_answer_0(rid, ENOMEM);
  599.         return;
  600.     }
  601.     assert(ridxp->index == 0);
  602.     /* ridxp->lock held */
  603.  
  604.     rootp->type = FAT_DIRECTORY;
  605.     rootp->firstc = FAT_CLST_ROOT;
  606.     rootp->refcnt = 1;
  607.     rootp->size = rde * sizeof(fat_dentry_t);
  608.     rootp->idx = ridxp;
  609.     ridxp->nodep = rootp;
  610.    
  611.     futex_up(&ridxp->lock);
  612.  
  613.     ipc_answer_0(rid, EOK);
  614. }
  615.  
  616. void fat_mount(ipc_callid_t rid, ipc_call_t *request)
  617. {
  618.     ipc_answer_0(rid, ENOTSUP);
  619. }
  620.  
  621. void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
  622. {
  623.     libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
  624. }
  625.  
  626. /**
  627.  * @}
  628.  */
  629.