Subversion Repositories HelenOS

Rev

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