source: mainline/uspace/srv/fs/fat/fat_ops.c@ e17d986

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e17d986 was e17d986, checked in by Jakub Jermar <jakub@…>, 17 years ago

Implementation of fat_append_clusters().

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