Subversion Repositories HelenOS

Rev

Rev 3519 | Rev 3526 | 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 "fat_dentry.h"
  40. #include "fat_fat.h"
  41. #include "../../vfs/vfs.h"
  42. #include <libfs.h>
  43. #include <libblock.h>
  44. #include <ipc/ipc.h>
  45. #include <ipc/services.h>
  46. #include <ipc/devmap.h>
  47. #include <async.h>
  48. #include <errno.h>
  49. #include <string.h>
  50. #include <byteorder.h>
  51. #include <libadt/hash_table.h>
  52. #include <libadt/list.h>
  53. #include <assert.h>
  54. #include <futex.h>
  55. #include <sys/mman.h>
  56. #include <align.h>
  57.  
  58. /** Futex protecting the list of cached free FAT nodes. */
  59. static futex_t ffn_futex = FUTEX_INITIALIZER;
  60.  
  61. /** List of cached free FAT nodes. */
  62. static LIST_INITIALIZE(ffn_head);
  63.  
  64. static void fat_node_initialize(fat_node_t *node)
  65. {
  66.     futex_initialize(&node->lock, 1);
  67.     node->idx = NULL;
  68.     node->type = 0;
  69.     link_initialize(&node->ffn_link);
  70.     node->size = 0;
  71.     node->lnkcnt = 0;
  72.     node->refcnt = 0;
  73.     node->dirty = false;
  74. }
  75.  
  76. static void fat_node_sync(fat_node_t *node)
  77. {
  78.     block_t *bb, *b;
  79.     fat_dentry_t *d;
  80.     uint16_t bps;
  81.     unsigned dps;
  82.    
  83.     assert(node->dirty);
  84.  
  85.     bb = block_get(node->idx->dev_handle, BS_BLOCK, BS_SIZE);
  86.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  87.     dps = bps / sizeof(fat_dentry_t);
  88.    
  89.     /* Read the block that contains the dentry of interest. */
  90.     b = _fat_block_get(bb->data, node->idx->dev_handle, node->idx->pfc,
  91.         (node->idx->pdi * sizeof(fat_dentry_t)) / bps);
  92.  
  93.     d = ((fat_dentry_t *)b->data) + (node->idx->pdi % dps);
  94.  
  95.     d->firstc = host2uint16_t_le(node->firstc);
  96.     if (node->type == FAT_FILE)
  97.         d->size = host2uint32_t_le(node->size);
  98.     /* TODO: update other fields? (e.g time fields, attr field) */
  99.    
  100.     b->dirty = true;        /* need to sync block */
  101.     block_put(b);
  102.     block_put(bb);
  103. }
  104.  
  105. /** Internal version of fat_node_get().
  106.  *
  107.  * @param idxp      Locked index structure.
  108.  */
  109. static void *fat_node_get_core(fat_idx_t *idxp)
  110. {
  111.     block_t *bb, *b;
  112.     fat_dentry_t *d;
  113.     fat_node_t *nodep = NULL;
  114.     unsigned bps;
  115.     unsigned dps;
  116.  
  117.     if (idxp->nodep) {
  118.         /*
  119.          * We are lucky.
  120.          * The node is already instantiated in memory.
  121.          */
  122.         futex_down(&idxp->nodep->lock);
  123.         if (!idxp->nodep->refcnt++)
  124.             list_remove(&idxp->nodep->ffn_link);
  125.         futex_up(&idxp->nodep->lock);
  126.         return idxp->nodep;
  127.     }
  128.  
  129.     /*
  130.      * We must instantiate the node from the file system.
  131.      */
  132.    
  133.     assert(idxp->pfc);
  134.  
  135.     futex_down(&ffn_futex);
  136.     if (!list_empty(&ffn_head)) {
  137.         /* Try to use a cached free node structure. */
  138.         fat_idx_t *idxp_tmp;
  139.         nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
  140.         if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
  141.             goto skip_cache;
  142.         idxp_tmp = nodep->idx;
  143.         if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
  144.             futex_up(&nodep->lock);
  145.             goto skip_cache;
  146.         }
  147.         list_remove(&nodep->ffn_link);
  148.         futex_up(&ffn_futex);
  149.         if (nodep->dirty)
  150.             fat_node_sync(nodep);
  151.         idxp_tmp->nodep = NULL;
  152.         futex_up(&nodep->lock);
  153.         futex_up(&idxp_tmp->lock);
  154.     } else {
  155. skip_cache:
  156.         /* Try to allocate a new node structure. */
  157.         futex_up(&ffn_futex);
  158.         nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
  159.         if (!nodep)
  160.             return NULL;
  161.     }
  162.     fat_node_initialize(nodep);
  163.  
  164.     bb = block_get(idxp->dev_handle, BS_BLOCK, BS_SIZE);
  165.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  166.     dps = bps / sizeof(fat_dentry_t);
  167.  
  168.     /* Read the block that contains the dentry of interest. */
  169.     b = _fat_block_get(bb->data, idxp->dev_handle, idxp->pfc,
  170.         (idxp->pdi * sizeof(fat_dentry_t)) / bps);
  171.     assert(b);
  172.  
  173.     d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
  174.     if (d->attr & FAT_ATTR_SUBDIR) {
  175.         /*
  176.          * The only directory which does not have this bit set is the
  177.          * root directory itself. The root directory node is handled
  178.          * and initialized elsewhere.
  179.          */
  180.         nodep->type = FAT_DIRECTORY;
  181.         /*
  182.          * Unfortunately, the 'size' field of the FAT dentry is not
  183.          * defined for the directory entry type. We must determine the
  184.          * size of the directory by walking the FAT.
  185.          */
  186.         nodep->size = bps * _fat_blcks_get(bb->data, idxp->dev_handle,
  187.             uint16_t_le2host(d->firstc), NULL);
  188.     } else {
  189.         nodep->type = FAT_FILE;
  190.         nodep->size = uint32_t_le2host(d->size);
  191.     }
  192.     nodep->firstc = uint16_t_le2host(d->firstc);
  193.     nodep->lnkcnt = 1;
  194.     nodep->refcnt = 1;
  195.  
  196.     block_put(b);
  197.     block_put(bb);
  198.  
  199.     /* Link the idx structure with the node structure. */
  200.     nodep->idx = idxp;
  201.     idxp->nodep = nodep;
  202.  
  203.     return nodep;
  204. }
  205.  
  206. /** Instantiate a FAT in-core node. */
  207. static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
  208. {
  209.     void *node;
  210.     fat_idx_t *idxp;
  211.  
  212.     idxp = fat_idx_get_by_index(dev_handle, index);
  213.     if (!idxp)
  214.         return NULL;
  215.     /* idxp->lock held */
  216.     node = fat_node_get_core(idxp);
  217.     futex_up(&idxp->lock);
  218.     return node;
  219. }
  220.  
  221. static void fat_node_put(void *node)
  222. {
  223.     fat_node_t *nodep = (fat_node_t *)node;
  224.  
  225.     futex_down(&nodep->lock);
  226.     if (!--nodep->refcnt) {
  227.         futex_down(&ffn_futex);
  228.         list_append(&nodep->ffn_link, &ffn_head);
  229.         futex_up(&ffn_futex);
  230.     }
  231.     futex_up(&nodep->lock);
  232. }
  233.  
  234. static void *fat_create(int flags)
  235. {
  236.     return NULL;    /* not supported at the moment */
  237. }
  238.  
  239. static int fat_destroy(void *node)
  240. {
  241.     return ENOTSUP; /* not supported at the moment */
  242. }
  243.  
  244. static bool fat_link(void *prnt, void *chld, const char *name)
  245. {
  246.     return false;   /* not supported at the moment */
  247. }
  248.  
  249. static int fat_unlink(void *prnt, void *chld)
  250. {
  251.     return ENOTSUP; /* not supported at the moment */
  252. }
  253.  
  254. static void *fat_match(void *prnt, const char *component)
  255. {
  256.     fat_node_t *parentp = (fat_node_t *)prnt;
  257.     char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
  258.     unsigned i, j;
  259.     unsigned bps;       /* bytes per sector */
  260.     unsigned dps;       /* dentries per sector */
  261.     unsigned blocks;
  262.     fat_dentry_t *d;
  263.     block_t *bb, *b;
  264.  
  265.     futex_down(&parentp->idx->lock);
  266.     bb = block_get(parentp->idx->dev_handle, BS_BLOCK, BS_SIZE);
  267.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  268.     dps = bps / sizeof(fat_dentry_t);
  269.     blocks = parentp->size / bps + (parentp->size % bps != 0);
  270.     for (i = 0; i < blocks; i++) {
  271.         unsigned dentries;
  272.        
  273.         b = fat_block_get(bb->data, parentp, i);
  274.         dentries = (i == blocks - 1) ?
  275.             parentp->size % sizeof(fat_dentry_t) :
  276.             dps;
  277.         for (j = 0; j < dentries; j++) {
  278.             d = ((fat_dentry_t *)b->data) + j;
  279.             switch (fat_classify_dentry(d)) {
  280.             case FAT_DENTRY_SKIP:
  281.                 continue;
  282.             case FAT_DENTRY_LAST:
  283.                 block_put(b);
  284.                 block_put(bb);
  285.                 futex_up(&parentp->idx->lock);
  286.                 return NULL;
  287.             default:
  288.             case FAT_DENTRY_VALID:
  289.                 dentry_name_canonify(d, name);
  290.                 break;
  291.             }
  292.             if (stricmp(name, component) == 0) {
  293.                 /* hit */
  294.                 void *node;
  295.                 /*
  296.                  * Assume tree hierarchy for locking.  We
  297.                  * already have the parent and now we are going
  298.                  * to lock the child.  Never lock in the oposite
  299.                  * order.
  300.                  */
  301.                 fat_idx_t *idx = fat_idx_get_by_pos(
  302.                     parentp->idx->dev_handle, parentp->firstc,
  303.                     i * dps + j);
  304.                 futex_up(&parentp->idx->lock);
  305.                 if (!idx) {
  306.                     /*
  307.                      * Can happen if memory is low or if we
  308.                      * run out of 32-bit indices.
  309.                      */
  310.                     block_put(b);
  311.                     block_put(bb);
  312.                     return NULL;
  313.                 }
  314.                 node = fat_node_get_core(idx);
  315.                 futex_up(&idx->lock);
  316.                 block_put(b);
  317.                 block_put(bb);
  318.                 return node;
  319.             }
  320.         }
  321.         block_put(b);
  322.     }
  323.     block_put(bb);
  324.  
  325.     futex_up(&parentp->idx->lock);
  326.     return NULL;
  327. }
  328.  
  329. static fs_index_t fat_index_get(void *node)
  330. {
  331.     fat_node_t *fnodep = (fat_node_t *)node;
  332.     if (!fnodep)
  333.         return 0;
  334.     return fnodep->idx->index;
  335. }
  336.  
  337. static size_t fat_size_get(void *node)
  338. {
  339.     return ((fat_node_t *)node)->size;
  340. }
  341.  
  342. static unsigned fat_lnkcnt_get(void *node)
  343. {
  344.     return ((fat_node_t *)node)->lnkcnt;
  345. }
  346.  
  347. static bool fat_has_children(void *node)
  348. {
  349.     fat_node_t *nodep = (fat_node_t *)node;
  350.     unsigned bps;
  351.     unsigned dps;
  352.     unsigned blocks;
  353.     block_t *bb, *b;
  354.     unsigned i, j;
  355.  
  356.     if (nodep->type != FAT_DIRECTORY)
  357.         return false;
  358.  
  359.     futex_down(&nodep->idx->lock);
  360.     bb = block_get(nodep->idx->dev_handle, BS_BLOCK, BS_SIZE);
  361.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  362.     dps = bps / sizeof(fat_dentry_t);
  363.  
  364.     blocks = nodep->size / bps + (nodep->size % bps != 0);
  365.  
  366.     for (i = 0; i < blocks; i++) {
  367.         unsigned dentries;
  368.         fat_dentry_t *d;
  369.    
  370.         b = fat_block_get(bb->data, nodep, i);
  371.         dentries = (i == blocks - 1) ?
  372.             nodep->size % sizeof(fat_dentry_t) :
  373.             dps;
  374.         for (j = 0; j < dentries; j++) {
  375.             d = ((fat_dentry_t *)b->data) + j;
  376.             switch (fat_classify_dentry(d)) {
  377.             case FAT_DENTRY_SKIP:
  378.                 continue;
  379.             case FAT_DENTRY_LAST:
  380.                 block_put(b);
  381.                 block_put(bb);
  382.                 futex_up(&nodep->idx->lock);
  383.                 return false;
  384.             default:
  385.             case FAT_DENTRY_VALID:
  386.                 block_put(b);
  387.                 block_put(bb);
  388.                 futex_up(&nodep->idx->lock);
  389.                 return true;
  390.             }
  391.             block_put(b);
  392.             block_put(bb);
  393.             futex_up(&nodep->idx->lock);
  394.             return true;
  395.         }
  396.         block_put(b);
  397.     }
  398.     block_put(bb);
  399.  
  400.     futex_up(&nodep->idx->lock);
  401.     return false;
  402. }
  403.  
  404. static void *fat_root_get(dev_handle_t dev_handle)
  405. {
  406.     return fat_node_get(dev_handle, 0);
  407. }
  408.  
  409. static char fat_plb_get_char(unsigned pos)
  410. {
  411.     return fat_reg.plb_ro[pos % PLB_SIZE];
  412. }
  413.  
  414. static bool fat_is_directory(void *node)
  415. {
  416.     return ((fat_node_t *)node)->type == FAT_DIRECTORY;
  417. }
  418.  
  419. static bool fat_is_file(void *node)
  420. {
  421.     return ((fat_node_t *)node)->type == FAT_FILE;
  422. }
  423.  
  424. /** libfs operations */
  425. libfs_ops_t fat_libfs_ops = {
  426.     .match = fat_match,
  427.     .node_get = fat_node_get,
  428.     .node_put = fat_node_put,
  429.     .create = fat_create,
  430.     .destroy = fat_destroy,
  431.     .link = fat_link,
  432.     .unlink = fat_unlink,
  433.     .index_get = fat_index_get,
  434.     .size_get = fat_size_get,
  435.     .lnkcnt_get = fat_lnkcnt_get,
  436.     .has_children = fat_has_children,
  437.     .root_get = fat_root_get,
  438.     .plb_get_char = fat_plb_get_char,
  439.     .is_directory = fat_is_directory,
  440.     .is_file = fat_is_file
  441. };
  442.  
  443. void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
  444. {
  445.     dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
  446.     block_t *bb;
  447.     uint16_t bps;
  448.     uint16_t rde;
  449.     int rc;
  450.  
  451.     /*
  452.      * For now, we don't bother to remember dev_handle, dev_phone or
  453.      * dev_buffer in some data structure. We use global variables because we
  454.      * know there will be at most one mount on this file system.
  455.      * Of course, this is a huge TODO item.
  456.      */
  457.     dev_buffer = mmap(NULL, BS_SIZE, PROTO_READ | PROTO_WRITE,
  458.         MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  459.    
  460.     if (!dev_buffer) {
  461.         ipc_answer_0(rid, ENOMEM);
  462.         return;
  463.     }
  464.  
  465.     dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
  466.         DEVMAP_CONNECT_TO_DEVICE, dev_handle);
  467.  
  468.     if (dev_phone < 0) {
  469.         munmap(dev_buffer, BS_SIZE);
  470.         ipc_answer_0(rid, dev_phone);
  471.         return;
  472.     }
  473.  
  474.     rc = ipc_share_out_start(dev_phone, dev_buffer,
  475.         AS_AREA_READ | AS_AREA_WRITE);
  476.     if (rc != EOK) {
  477.             munmap(dev_buffer, BS_SIZE);
  478.         ipc_answer_0(rid, rc);
  479.         return;
  480.     }
  481.  
  482.     /* Read the number of root directory entries. */
  483.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  484.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  485.     rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
  486.     block_put(bb);
  487.  
  488.     if (bps != BS_SIZE) {
  489.         munmap(dev_buffer, BS_SIZE);
  490.         ipc_answer_0(rid, ENOTSUP);
  491.         return;
  492.     }
  493.  
  494.     rc = fat_idx_init_by_dev_handle(dev_handle);
  495.     if (rc != EOK) {
  496.             munmap(dev_buffer, BS_SIZE);
  497.         ipc_answer_0(rid, rc);
  498.         return;
  499.     }
  500.  
  501.     /* Initialize the root node. */
  502.     fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
  503.     if (!rootp) {
  504.             munmap(dev_buffer, BS_SIZE);
  505.         fat_idx_fini_by_dev_handle(dev_handle);
  506.         ipc_answer_0(rid, ENOMEM);
  507.         return;
  508.     }
  509.     fat_node_initialize(rootp);
  510.  
  511.     fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
  512.     if (!ridxp) {
  513.             munmap(dev_buffer, BS_SIZE);
  514.         free(rootp);
  515.         fat_idx_fini_by_dev_handle(dev_handle);
  516.         ipc_answer_0(rid, ENOMEM);
  517.         return;
  518.     }
  519.     assert(ridxp->index == 0);
  520.     /* ridxp->lock held */
  521.  
  522.     rootp->type = FAT_DIRECTORY;
  523.     rootp->firstc = FAT_CLST_ROOT;
  524.     rootp->refcnt = 1;
  525.     rootp->lnkcnt = 0;  /* FS root is not linked */
  526.     rootp->size = rde * sizeof(fat_dentry_t);
  527.     rootp->idx = ridxp;
  528.     ridxp->nodep = rootp;
  529.    
  530.     futex_up(&ridxp->lock);
  531.  
  532.     ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
  533. }
  534.  
  535. void fat_mount(ipc_callid_t rid, ipc_call_t *request)
  536. {
  537.     ipc_answer_0(rid, ENOTSUP);
  538. }
  539.  
  540. void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
  541. {
  542.     libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
  543. }
  544.  
  545. void fat_read(ipc_callid_t rid, ipc_call_t *request)
  546. {
  547.     dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
  548.     fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
  549.     off_t pos = (off_t)IPC_GET_ARG3(*request);
  550.     fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
  551.     uint16_t bps;
  552.     size_t bytes;
  553.     block_t *bb, *b;
  554.  
  555.     if (!nodep) {
  556.         ipc_answer_0(rid, ENOENT);
  557.         return;
  558.     }
  559.  
  560.     ipc_callid_t callid;
  561.     size_t len;
  562.     if (!ipc_data_read_receive(&callid, &len)) {
  563.         fat_node_put(nodep);
  564.         ipc_answer_0(callid, EINVAL);
  565.         ipc_answer_0(rid, EINVAL);
  566.         return;
  567.     }
  568.  
  569.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  570.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  571.  
  572.     if (nodep->type == FAT_FILE) {
  573.         /*
  574.          * Our strategy for regular file reads is to read one block at
  575.          * most and make use of the possibility to return less data than
  576.          * requested. This keeps the code very simple.
  577.          */
  578.         bytes = min(len, bps - pos % bps);
  579.         b = fat_block_get(bb->data, nodep, pos / bps);
  580.         (void) ipc_data_read_finalize(callid, b->data + pos % bps,
  581.             bytes);
  582.         block_put(b);
  583.     } else {
  584.         unsigned bnum;
  585.         off_t spos = pos;
  586.         char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
  587.         fat_dentry_t *d;
  588.  
  589.         assert(nodep->type == FAT_DIRECTORY);
  590.         assert(nodep->size % bps == 0);
  591.         assert(bps % sizeof(fat_dentry_t) == 0);
  592.  
  593.         /*
  594.          * Our strategy for readdir() is to use the position pointer as
  595.          * an index into the array of all dentries. On entry, it points
  596.          * to the first unread dentry. If we skip any dentries, we bump
  597.          * the position pointer accordingly.
  598.          */
  599.         bnum = (pos * sizeof(fat_dentry_t)) / bps;
  600.         while (bnum < nodep->size / bps) {
  601.             off_t o;
  602.  
  603.             b = fat_block_get(bb->data, nodep, bnum);
  604.             for (o = pos % (bps / sizeof(fat_dentry_t));
  605.                 o < bps / sizeof(fat_dentry_t);
  606.                 o++, pos++) {
  607.                 d = ((fat_dentry_t *)b->data) + o;
  608.                 switch (fat_classify_dentry(d)) {
  609.                 case FAT_DENTRY_SKIP:
  610.                     continue;
  611.                 case FAT_DENTRY_LAST:
  612.                     block_put(b);
  613.                     goto miss;
  614.                 default:
  615.                 case FAT_DENTRY_VALID:
  616.                     dentry_name_canonify(d, name);
  617.                     block_put(b);
  618.                     goto hit;
  619.                 }
  620.             }
  621.             block_put(b);
  622.             bnum++;
  623.         }
  624. miss:
  625.         fat_node_put(nodep);
  626.         block_put(bb);
  627.         ipc_answer_0(callid, ENOENT);
  628.         ipc_answer_1(rid, ENOENT, 0);
  629.         return;
  630. hit:
  631.         (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
  632.         bytes = (pos - spos) + 1;
  633.     }
  634.  
  635.     fat_node_put(nodep);
  636.     block_put(bb);
  637.     ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
  638. }
  639.  
  640. void fat_write(ipc_callid_t rid, ipc_call_t *request)
  641. {
  642.     dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
  643.     fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
  644.     off_t pos = (off_t)IPC_GET_ARG3(*request);
  645.     fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
  646.     size_t bytes;
  647.     block_t *b, *bb;
  648.     uint16_t bps;
  649.     unsigned spc;
  650.     off_t boundary;
  651.    
  652.     if (!nodep) {
  653.         ipc_answer_0(rid, ENOENT);
  654.         return;
  655.     }
  656.    
  657.     /* XXX remove me when you are ready */
  658.     {
  659.         ipc_answer_0(rid, ENOTSUP);
  660.         fat_node_put(nodep);
  661.         return;
  662.     }
  663.  
  664.     ipc_callid_t callid;
  665.     size_t len;
  666.     if (!ipc_data_write_receive(&callid, &len)) {
  667.         fat_node_put(nodep);
  668.         ipc_answer_0(callid, EINVAL);
  669.         ipc_answer_0(rid, EINVAL);
  670.         return;
  671.     }
  672.  
  673.     /*
  674.      * In all scenarios, we will attempt to write out only one block worth
  675.      * of data at maximum. There might be some more efficient approaches,
  676.      * but this one greatly simplifies fat_write(). Note that we can afford
  677.      * to do this because the client must be ready to handle the return
  678.      * value signalizing a smaller number of bytes written.
  679.      */
  680.     bytes = min(len, bps - pos % bps);
  681.  
  682.     bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
  683.     bps = uint16_t_le2host(FAT_BS(bb)->bps);
  684.     spc = FAT_BS(bb)->spc;
  685.    
  686.     boundary = ROUND_UP(nodep->size, bps * spc);
  687.     if (pos < boundary) {
  688.         /*
  689.          * This is the easier case - we are either overwriting already
  690.          * existing contents or writing behind the EOF, but still within
  691.          * the limits of the last cluster. The node size may grow to the
  692.          * next block size boundary.
  693.          */
  694.         fat_fill_gap(bb->data, nodep, FAT_CLST_RES0, pos);
  695.         b = fat_block_get(bb->data, nodep, pos / bps);
  696.         (void) ipc_data_write_finalize(callid, b->data + pos % bps,
  697.             bytes);
  698.         b->dirty = true;        /* need to sync block */
  699.         block_put(b);
  700.         if (pos + bytes > nodep->size) {
  701.             nodep->size = pos + bytes;
  702.             nodep->dirty = true;    /* need to sync node */
  703.         }
  704.         fat_node_put(nodep);
  705.         block_put(bb);
  706.         ipc_answer_1(rid, EOK, bytes); 
  707.         return;
  708.     } else {
  709.         /*
  710.          * This is the more difficult case. We must allocate new
  711.          * clusters for the node and zero them out.
  712.          */
  713.         int status;
  714.         unsigned nclsts;
  715.         fat_cluster_t mcl, lcl;
  716.    
  717.         nclsts = (ROUND_UP(pos + bytes, bps * spc) - boundary) /
  718.             bps * spc;
  719.         /* create an independent chain of nclsts clusters in all FATs */
  720.         status = fat_alloc_clusters(bb->data, dev_handle, nclsts, &mcl,
  721.             &lcl);
  722.         if (status != EOK) {
  723.             /* could not allocate a chain of nclsts clusters */
  724.             fat_node_put(nodep);
  725.             block_put(bb);
  726.             ipc_answer_0(callid, status);
  727.             ipc_answer_0(rid, status);
  728.             return;
  729.         }
  730.         /* zero fill any gaps */
  731.         fat_fill_gap(bb->data, nodep, mcl, pos);
  732.         b = _fat_block_get(bb->data, dev_handle, lcl,
  733.             (pos / bps) % spc);
  734.         (void) ipc_data_write_finalize(callid, b->data + pos % bps,
  735.             bytes);
  736.         b->dirty = true;        /* need to sync block */
  737.         block_put(b);
  738.         /*
  739.          * Append the cluster chain starting in mcl to the end of the
  740.          * node's cluster chain.
  741.          */
  742.         fat_append_clusters(bb->data, nodep, mcl);
  743.         nodep->size = pos + bytes;
  744.         nodep->dirty = true;        /* need to sync node */
  745.         fat_node_put(nodep);
  746.         block_put(bb);
  747.         ipc_answer_1(rid, EOK, bytes);
  748.         return;
  749.     }
  750. }
  751.  
  752. /**
  753.  * @}
  754.  */
  755.