Subversion Repositories HelenOS

Rev

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