Subversion Repositories HelenOS

Rev

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