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

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

When syncing a FAT node, do not skip attributes.

  • Property mode set to 100644
File size: 24.1 KB
Line 
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. */
59static futex_t ffn_futex = FUTEX_INITIALIZER;
60
61/** List of cached free FAT nodes. */
62static LIST_INITIALIZE(ffn_head);
63
64static 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
76static 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, BLOCK_FLAGS_NONE);
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 } else if (node->type == FAT_DIRECTORY) {
100 d->attr = FAT_ATTR_SUBDIR;
101 }
102
103 /* TODO: update other fields? (e.g time fields) */
104
105 b->dirty = true; /* need to sync block */
106 block_put(b);
107}
108
109static fat_node_t *fat_node_get_new(void)
110{
111 fat_node_t *nodep;
112
113 futex_down(&ffn_futex);
114 if (!list_empty(&ffn_head)) {
115 /* Try to use a cached free node structure. */
116 fat_idx_t *idxp_tmp;
117 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
118 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
119 goto skip_cache;
120 idxp_tmp = nodep->idx;
121 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
122 futex_up(&nodep->lock);
123 goto skip_cache;
124 }
125 list_remove(&nodep->ffn_link);
126 futex_up(&ffn_futex);
127 if (nodep->dirty)
128 fat_node_sync(nodep);
129 idxp_tmp->nodep = NULL;
130 futex_up(&nodep->lock);
131 futex_up(&idxp_tmp->lock);
132 } else {
133skip_cache:
134 /* Try to allocate a new node structure. */
135 futex_up(&ffn_futex);
136 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
137 if (!nodep)
138 return NULL;
139 }
140 fat_node_initialize(nodep);
141
142 return nodep;
143}
144
145/** Internal version of fat_node_get().
146 *
147 * @param idxp Locked index structure.
148 */
149static void *fat_node_get_core(fat_idx_t *idxp)
150{
151 block_t *b;
152 fat_bs_t *bs;
153 fat_dentry_t *d;
154 fat_node_t *nodep = NULL;
155 unsigned bps;
156 unsigned spc;
157 unsigned dps;
158
159 if (idxp->nodep) {
160 /*
161 * We are lucky.
162 * The node is already instantiated in memory.
163 */
164 futex_down(&idxp->nodep->lock);
165 if (!idxp->nodep->refcnt++)
166 list_remove(&idxp->nodep->ffn_link);
167 futex_up(&idxp->nodep->lock);
168 return idxp->nodep;
169 }
170
171 /*
172 * We must instantiate the node from the file system.
173 */
174
175 assert(idxp->pfc);
176
177 nodep = fat_node_get_new();
178 if (!nodep)
179 return NULL;
180
181 bs = block_bb_get(idxp->dev_handle);
182 bps = uint16_t_le2host(bs->bps);
183 spc = bs->spc;
184 dps = bps / sizeof(fat_dentry_t);
185
186 /* Read the block that contains the dentry of interest. */
187 b = _fat_block_get(bs, idxp->dev_handle, idxp->pfc,
188 (idxp->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
189 assert(b);
190
191 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
192 if (d->attr & FAT_ATTR_SUBDIR) {
193 /*
194 * The only directory which does not have this bit set is the
195 * root directory itself. The root directory node is handled
196 * and initialized elsewhere.
197 */
198 nodep->type = FAT_DIRECTORY;
199 /*
200 * Unfortunately, the 'size' field of the FAT dentry is not
201 * defined for the directory entry type. We must determine the
202 * size of the directory by walking the FAT.
203 */
204 nodep->size = bps * spc * fat_clusters_get(bs, idxp->dev_handle,
205 uint16_t_le2host(d->firstc));
206 } else {
207 nodep->type = FAT_FILE;
208 nodep->size = uint32_t_le2host(d->size);
209 }
210 nodep->firstc = uint16_t_le2host(d->firstc);
211 nodep->lnkcnt = 1;
212 nodep->refcnt = 1;
213
214 block_put(b);
215
216 /* Link the idx structure with the node structure. */
217 nodep->idx = idxp;
218 idxp->nodep = nodep;
219
220 return nodep;
221}
222
223/*
224 * Forward declarations of FAT libfs operations.
225 */
226static void *fat_node_get(dev_handle_t, fs_index_t);
227static void fat_node_put(void *);
228static void *fat_create_node(dev_handle_t, int);
229static int fat_destroy_node(void *);
230static int fat_link(void *, void *, const char *);
231static int fat_unlink(void *, void *);
232static void *fat_match(void *, const char *);
233static fs_index_t fat_index_get(void *);
234static size_t fat_size_get(void *);
235static unsigned fat_lnkcnt_get(void *);
236static bool fat_has_children(void *);
237static void *fat_root_get(dev_handle_t);
238static char fat_plb_get_char(unsigned);
239static bool fat_is_directory(void *);
240static bool fat_is_file(void *node);
241
242/*
243 * FAT libfs operations.
244 */
245
246/** Instantiate a FAT in-core node. */
247void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
248{
249 void *node;
250 fat_idx_t *idxp;
251
252 idxp = fat_idx_get_by_index(dev_handle, index);
253 if (!idxp)
254 return NULL;
255 /* idxp->lock held */
256 node = fat_node_get_core(idxp);
257 futex_up(&idxp->lock);
258 return node;
259}
260
261void fat_node_put(void *node)
262{
263 fat_node_t *nodep = (fat_node_t *)node;
264 bool destroy = false;
265
266 futex_down(&nodep->lock);
267 if (!--nodep->refcnt) {
268 if (nodep->idx) {
269 futex_down(&ffn_futex);
270 list_append(&nodep->ffn_link, &ffn_head);
271 futex_up(&ffn_futex);
272 } else {
273 /*
274 * The node does not have any index structure associated
275 * with itself. This can only mean that we are releasing
276 * the node after a failed attempt to allocate the index
277 * structure for it.
278 */
279 destroy = true;
280 }
281 }
282 futex_up(&nodep->lock);
283 if (destroy)
284 free(node);
285}
286
287void *fat_create_node(dev_handle_t dev_handle, int flags)
288{
289 fat_idx_t *idxp;
290 fat_node_t *nodep;
291
292 nodep = fat_node_get_new();
293 if (!nodep)
294 return NULL;
295 idxp = fat_idx_get_new(dev_handle);
296 if (!idxp) {
297 fat_node_put(nodep);
298 return NULL;
299 }
300 /* idxp->lock held */
301 if (flags & L_DIRECTORY) {
302 nodep->type = FAT_DIRECTORY;
303 } else {
304 nodep->type = FAT_FILE;
305 }
306 nodep->size = 0;
307 nodep->firstc = FAT_CLST_RES0;
308 nodep->lnkcnt = 0; /* not linked anywhere */
309 nodep->refcnt = 1;
310
311 nodep->idx = idxp;
312 idxp->nodep = nodep;
313
314 futex_up(&idxp->lock);
315 return nodep;
316}
317
318int fat_destroy_node(void *node)
319{
320 fat_node_t *nodep = (fat_node_t *)node;
321 fat_bs_t *bs;
322
323 /*
324 * The node is not reachable from the file system. This means that the
325 * link count should be zero and that the index structure cannot be
326 * found in the position hash. Obviously, we don't need to lock the node
327 * nor its index structure.
328 */
329 assert(nodep->lnkcnt == 0);
330
331 /*
332 * The node may not have any children.
333 */
334 assert(fat_has_children(node) == false);
335
336 bs = block_bb_get(nodep->idx->dev_handle);
337 if (nodep->firstc != FAT_CLST_RES0) {
338 assert(nodep->size);
339 /* Free all clusters allocated to the node. */
340 fat_free_clusters(bs, nodep->idx->dev_handle, nodep->firstc);
341 }
342
343 fat_idx_destroy(nodep->idx);
344 free(nodep);
345 return EOK;
346}
347
348int fat_link(void *prnt, void *chld, const char *name)
349{
350 fat_node_t *parentp = (fat_node_t *)prnt;
351 fat_node_t *childp = (fat_node_t *)chld;
352 fat_dentry_t *d;
353 fat_bs_t *bs;
354 block_t *b;
355 int i, j;
356 uint16_t bps;
357 unsigned dps;
358 unsigned blocks;
359
360 futex_down(&childp->lock);
361 if (childp->lnkcnt == 1) {
362 /*
363 * On FAT, we don't support multiple hard links.
364 */
365 futex_up(&childp->lock);
366 return EMLINK;
367 }
368 assert(childp->lnkcnt == 0);
369 futex_up(&childp->lock);
370
371 if (!fat_dentry_name_verify(name)) {
372 /*
373 * Attempt to create unsupported name.
374 */
375 return ENOTSUP;
376 }
377
378 /*
379 * Get us an unused parent node's dentry or grow the parent and allocate
380 * a new one.
381 */
382
383 futex_down(&parentp->idx->lock);
384 bs = block_bb_get(parentp->idx->dev_handle);
385 bps = uint16_t_le2host(bs->bps);
386 dps = bps / sizeof(fat_dentry_t);
387
388 blocks = parentp->size / bps;
389
390 for (i = 0; i < blocks; i++) {
391 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
392 for (j = 0; j < dps; j++) {
393 d = ((fat_dentry_t *)b->data) + j;
394 switch (fat_classify_dentry(d)) {
395 case FAT_DENTRY_SKIP:
396 case FAT_DENTRY_VALID:
397 /* skipping used and meta entries */
398 continue;
399 case FAT_DENTRY_FREE:
400 case FAT_DENTRY_LAST:
401 /* found an empty slot */
402 goto hit;
403 }
404 }
405 block_put(b);
406 }
407
408 /*
409 * We need to grow the parent in order to create a new unused dentry.
410 */
411 futex_up(&parentp->idx->lock);
412 return ENOTSUP; /* XXX */
413
414hit:
415 /*
416 * At this point we only establish the link between the parent and the
417 * child. The dentry, except of the name and the extension, will remain
418 * uninitialized until the the corresponding node is synced. Thus the
419 * valid dentry data is kept in the child node structure.
420 */
421 memset(d, 0, sizeof(fat_dentry_t));
422 fat_dentry_name_set(d, name);
423 b->dirty = true; /* need to sync block */
424 block_put(b);
425 futex_up(&parentp->idx->lock);
426
427 futex_down(&childp->idx->lock);
428 childp->idx->pfc = parentp->firstc;
429 childp->idx->pdi = i * dps + j;
430 futex_up(&childp->idx->lock);
431
432 futex_down(&childp->lock);
433 childp->lnkcnt = 1;
434 childp->dirty = true; /* need to sync node */
435 futex_up(&childp->lock);
436
437 /*
438 * Hash in the index structure into the position hash.
439 */
440 fat_idx_hashin(childp->idx);
441
442 return EOK;
443}
444
445int fat_unlink(void *prnt, void *chld)
446{
447 return ENOTSUP; /* not supported at the moment */
448}
449
450void *fat_match(void *prnt, const char *component)
451{
452 fat_bs_t *bs;
453 fat_node_t *parentp = (fat_node_t *)prnt;
454 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
455 unsigned i, j;
456 unsigned bps; /* bytes per sector */
457 unsigned dps; /* dentries per sector */
458 unsigned blocks;
459 fat_dentry_t *d;
460 block_t *b;
461
462 futex_down(&parentp->idx->lock);
463 bs = block_bb_get(parentp->idx->dev_handle);
464 bps = uint16_t_le2host(bs->bps);
465 dps = bps / sizeof(fat_dentry_t);
466 blocks = parentp->size / bps;
467 for (i = 0; i < blocks; i++) {
468 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
469 for (j = 0; j < dps; j++) {
470 d = ((fat_dentry_t *)b->data) + j;
471 switch (fat_classify_dentry(d)) {
472 case FAT_DENTRY_SKIP:
473 case FAT_DENTRY_FREE:
474 continue;
475 case FAT_DENTRY_LAST:
476 block_put(b);
477 futex_up(&parentp->idx->lock);
478 return NULL;
479 default:
480 case FAT_DENTRY_VALID:
481 fat_dentry_name_get(d, name);
482 break;
483 }
484 if (stricmp(name, component) == 0) {
485 /* hit */
486 void *node;
487 /*
488 * Assume tree hierarchy for locking. We
489 * already have the parent and now we are going
490 * to lock the child. Never lock in the oposite
491 * order.
492 */
493 fat_idx_t *idx = fat_idx_get_by_pos(
494 parentp->idx->dev_handle, parentp->firstc,
495 i * dps + j);
496 futex_up(&parentp->idx->lock);
497 if (!idx) {
498 /*
499 * Can happen if memory is low or if we
500 * run out of 32-bit indices.
501 */
502 block_put(b);
503 return NULL;
504 }
505 node = fat_node_get_core(idx);
506 futex_up(&idx->lock);
507 block_put(b);
508 return node;
509 }
510 }
511 block_put(b);
512 }
513
514 futex_up(&parentp->idx->lock);
515 return NULL;
516}
517
518fs_index_t fat_index_get(void *node)
519{
520 fat_node_t *fnodep = (fat_node_t *)node;
521 if (!fnodep)
522 return 0;
523 return fnodep->idx->index;
524}
525
526size_t fat_size_get(void *node)
527{
528 return ((fat_node_t *)node)->size;
529}
530
531unsigned fat_lnkcnt_get(void *node)
532{
533 return ((fat_node_t *)node)->lnkcnt;
534}
535
536bool fat_has_children(void *node)
537{
538 fat_bs_t *bs;
539 fat_node_t *nodep = (fat_node_t *)node;
540 unsigned bps;
541 unsigned dps;
542 unsigned blocks;
543 block_t *b;
544 unsigned i, j;
545
546 if (nodep->type != FAT_DIRECTORY)
547 return false;
548
549 futex_down(&nodep->idx->lock);
550 bs = block_bb_get(nodep->idx->dev_handle);
551 bps = uint16_t_le2host(bs->bps);
552 dps = bps / sizeof(fat_dentry_t);
553
554 blocks = nodep->size / bps;
555
556 for (i = 0; i < blocks; i++) {
557 fat_dentry_t *d;
558
559 b = fat_block_get(bs, nodep, i, BLOCK_FLAGS_NONE);
560 for (j = 0; j < dps; j++) {
561 d = ((fat_dentry_t *)b->data) + j;
562 switch (fat_classify_dentry(d)) {
563 case FAT_DENTRY_SKIP:
564 case FAT_DENTRY_FREE:
565 continue;
566 case FAT_DENTRY_LAST:
567 block_put(b);
568 futex_up(&nodep->idx->lock);
569 return false;
570 default:
571 case FAT_DENTRY_VALID:
572 block_put(b);
573 futex_up(&nodep->idx->lock);
574 return true;
575 }
576 block_put(b);
577 futex_up(&nodep->idx->lock);
578 return true;
579 }
580 block_put(b);
581 }
582
583 futex_up(&nodep->idx->lock);
584 return false;
585}
586
587void *fat_root_get(dev_handle_t dev_handle)
588{
589 return fat_node_get(dev_handle, 0);
590}
591
592char fat_plb_get_char(unsigned pos)
593{
594 return fat_reg.plb_ro[pos % PLB_SIZE];
595}
596
597bool fat_is_directory(void *node)
598{
599 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
600}
601
602bool fat_is_file(void *node)
603{
604 return ((fat_node_t *)node)->type == FAT_FILE;
605}
606
607/** libfs operations */
608libfs_ops_t fat_libfs_ops = {
609 .match = fat_match,
610 .node_get = fat_node_get,
611 .node_put = fat_node_put,
612 .create = fat_create_node,
613 .destroy = fat_destroy_node,
614 .link = fat_link,
615 .unlink = fat_unlink,
616 .index_get = fat_index_get,
617 .size_get = fat_size_get,
618 .lnkcnt_get = fat_lnkcnt_get,
619 .has_children = fat_has_children,
620 .root_get = fat_root_get,
621 .plb_get_char = fat_plb_get_char,
622 .is_directory = fat_is_directory,
623 .is_file = fat_is_file
624};
625
626/*
627 * VFS operations.
628 */
629
630void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
631{
632 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
633 fat_bs_t *bs;
634 uint16_t bps;
635 uint16_t rde;
636 int rc;
637
638 /* initialize libblock */
639 rc = block_init(dev_handle, BS_SIZE);
640 if (rc != EOK) {
641 ipc_answer_0(rid, rc);
642 return;
643 }
644
645 /* prepare the boot block */
646 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
647 if (rc != EOK) {
648 block_fini(dev_handle);
649 ipc_answer_0(rid, rc);
650 return;
651 }
652
653 /* get the buffer with the boot sector */
654 bs = block_bb_get(dev_handle);
655
656 /* Read the number of root directory entries. */
657 bps = uint16_t_le2host(bs->bps);
658 rde = uint16_t_le2host(bs->root_ent_max);
659
660 if (bps != BS_SIZE) {
661 block_fini(dev_handle);
662 ipc_answer_0(rid, ENOTSUP);
663 return;
664 }
665
666 /* Initialize the block cache */
667 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
668 if (rc != EOK) {
669 block_fini(dev_handle);
670 ipc_answer_0(rid, rc);
671 return;
672 }
673
674 rc = fat_idx_init_by_dev_handle(dev_handle);
675 if (rc != EOK) {
676 block_fini(dev_handle);
677 ipc_answer_0(rid, rc);
678 return;
679 }
680
681 /* Initialize the root node. */
682 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
683 if (!rootp) {
684 block_fini(dev_handle);
685 fat_idx_fini_by_dev_handle(dev_handle);
686 ipc_answer_0(rid, ENOMEM);
687 return;
688 }
689 fat_node_initialize(rootp);
690
691 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
692 if (!ridxp) {
693 block_fini(dev_handle);
694 free(rootp);
695 fat_idx_fini_by_dev_handle(dev_handle);
696 ipc_answer_0(rid, ENOMEM);
697 return;
698 }
699 assert(ridxp->index == 0);
700 /* ridxp->lock held */
701
702 rootp->type = FAT_DIRECTORY;
703 rootp->firstc = FAT_CLST_ROOT;
704 rootp->refcnt = 1;
705 rootp->lnkcnt = 0; /* FS root is not linked */
706 rootp->size = rde * sizeof(fat_dentry_t);
707 rootp->idx = ridxp;
708 ridxp->nodep = rootp;
709
710 futex_up(&ridxp->lock);
711
712 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
713}
714
715void fat_mount(ipc_callid_t rid, ipc_call_t *request)
716{
717 ipc_answer_0(rid, ENOTSUP);
718}
719
720void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
721{
722 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
723}
724
725void fat_read(ipc_callid_t rid, ipc_call_t *request)
726{
727 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
728 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
729 off_t pos = (off_t)IPC_GET_ARG3(*request);
730 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
731 fat_bs_t *bs;
732 uint16_t bps;
733 size_t bytes;
734 block_t *b;
735
736 if (!nodep) {
737 ipc_answer_0(rid, ENOENT);
738 return;
739 }
740
741 ipc_callid_t callid;
742 size_t len;
743 if (!ipc_data_read_receive(&callid, &len)) {
744 fat_node_put(nodep);
745 ipc_answer_0(callid, EINVAL);
746 ipc_answer_0(rid, EINVAL);
747 return;
748 }
749
750 bs = block_bb_get(dev_handle);
751 bps = uint16_t_le2host(bs->bps);
752
753 if (nodep->type == FAT_FILE) {
754 /*
755 * Our strategy for regular file reads is to read one block at
756 * most and make use of the possibility to return less data than
757 * requested. This keeps the code very simple.
758 */
759 if (pos >= nodep->size) {
760 /* reading beyond the EOF */
761 bytes = 0;
762 (void) ipc_data_read_finalize(callid, NULL, 0);
763 } else {
764 bytes = min(len, bps - pos % bps);
765 bytes = min(bytes, nodep->size - pos);
766 b = fat_block_get(bs, nodep, pos / bps,
767 BLOCK_FLAGS_NONE);
768 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
769 bytes);
770 block_put(b);
771 }
772 } else {
773 unsigned bnum;
774 off_t spos = pos;
775 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
776 fat_dentry_t *d;
777
778 assert(nodep->type == FAT_DIRECTORY);
779 assert(nodep->size % bps == 0);
780 assert(bps % sizeof(fat_dentry_t) == 0);
781
782 /*
783 * Our strategy for readdir() is to use the position pointer as
784 * an index into the array of all dentries. On entry, it points
785 * to the first unread dentry. If we skip any dentries, we bump
786 * the position pointer accordingly.
787 */
788 bnum = (pos * sizeof(fat_dentry_t)) / bps;
789 while (bnum < nodep->size / bps) {
790 off_t o;
791
792 b = fat_block_get(bs, nodep, bnum, BLOCK_FLAGS_NONE);
793 for (o = pos % (bps / sizeof(fat_dentry_t));
794 o < bps / sizeof(fat_dentry_t);
795 o++, pos++) {
796 d = ((fat_dentry_t *)b->data) + o;
797 switch (fat_classify_dentry(d)) {
798 case FAT_DENTRY_SKIP:
799 case FAT_DENTRY_FREE:
800 continue;
801 case FAT_DENTRY_LAST:
802 block_put(b);
803 goto miss;
804 default:
805 case FAT_DENTRY_VALID:
806 fat_dentry_name_get(d, name);
807 block_put(b);
808 goto hit;
809 }
810 }
811 block_put(b);
812 bnum++;
813 }
814miss:
815 fat_node_put(nodep);
816 ipc_answer_0(callid, ENOENT);
817 ipc_answer_1(rid, ENOENT, 0);
818 return;
819hit:
820 (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
821 bytes = (pos - spos) + 1;
822 }
823
824 fat_node_put(nodep);
825 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
826}
827
828void fat_write(ipc_callid_t rid, ipc_call_t *request)
829{
830 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
831 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
832 off_t pos = (off_t)IPC_GET_ARG3(*request);
833 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
834 fat_bs_t *bs;
835 size_t bytes;
836 block_t *b;
837 uint16_t bps;
838 unsigned spc;
839 unsigned bpc; /* bytes per cluster */
840 off_t boundary;
841 int flags = BLOCK_FLAGS_NONE;
842
843 if (!nodep) {
844 ipc_answer_0(rid, ENOENT);
845 return;
846 }
847
848 ipc_callid_t callid;
849 size_t len;
850 if (!ipc_data_write_receive(&callid, &len)) {
851 fat_node_put(nodep);
852 ipc_answer_0(callid, EINVAL);
853 ipc_answer_0(rid, EINVAL);
854 return;
855 }
856
857 bs = block_bb_get(dev_handle);
858 bps = uint16_t_le2host(bs->bps);
859 spc = bs->spc;
860 bpc = bps * spc;
861
862 /*
863 * In all scenarios, we will attempt to write out only one block worth
864 * of data at maximum. There might be some more efficient approaches,
865 * but this one greatly simplifies fat_write(). Note that we can afford
866 * to do this because the client must be ready to handle the return
867 * value signalizing a smaller number of bytes written.
868 */
869 bytes = min(len, bps - pos % bps);
870 if (bytes == bps)
871 flags |= BLOCK_FLAGS_NOREAD;
872
873 boundary = ROUND_UP(nodep->size, bpc);
874 if (pos < boundary) {
875 /*
876 * This is the easier case - we are either overwriting already
877 * existing contents or writing behind the EOF, but still within
878 * the limits of the last cluster. The node size may grow to the
879 * next block size boundary.
880 */
881 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
882 b = fat_block_get(bs, nodep, pos / bps, flags);
883 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
884 bytes);
885 b->dirty = true; /* need to sync block */
886 block_put(b);
887 if (pos + bytes > nodep->size) {
888 nodep->size = pos + bytes;
889 nodep->dirty = true; /* need to sync node */
890 }
891 ipc_answer_2(rid, EOK, bytes, nodep->size);
892 fat_node_put(nodep);
893 return;
894 } else {
895 /*
896 * This is the more difficult case. We must allocate new
897 * clusters for the node and zero them out.
898 */
899 int status;
900 unsigned nclsts;
901 fat_cluster_t mcl, lcl;
902
903 nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
904 /* create an independent chain of nclsts clusters in all FATs */
905 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
906 if (status != EOK) {
907 /* could not allocate a chain of nclsts clusters */
908 fat_node_put(nodep);
909 ipc_answer_0(callid, status);
910 ipc_answer_0(rid, status);
911 return;
912 }
913 /* zero fill any gaps */
914 fat_fill_gap(bs, nodep, mcl, pos);
915 b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
916 flags);
917 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
918 bytes);
919 b->dirty = true; /* need to sync block */
920 block_put(b);
921 /*
922 * Append the cluster chain starting in mcl to the end of the
923 * node's cluster chain.
924 */
925 fat_append_clusters(bs, nodep, mcl);
926 nodep->size = pos + bytes;
927 nodep->dirty = true; /* need to sync node */
928 ipc_answer_2(rid, EOK, bytes, nodep->size);
929 fat_node_put(nodep);
930 return;
931 }
932}
933
934void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
935{
936 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
937 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
938 size_t size = (off_t)IPC_GET_ARG3(*request);
939 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
940 fat_bs_t *bs;
941 uint16_t bps;
942 uint8_t spc;
943 unsigned bpc; /* bytes per cluster */
944 int rc;
945
946 if (!nodep) {
947 ipc_answer_0(rid, ENOENT);
948 return;
949 }
950
951 bs = block_bb_get(dev_handle);
952 bps = uint16_t_le2host(bs->bps);
953 spc = bs->spc;
954 bpc = bps * spc;
955
956 if (nodep->size == size) {
957 rc = EOK;
958 } else if (nodep->size < size) {
959 /*
960 * The standard says we have the freedom to grow the node.
961 * For now, we simply return an error.
962 */
963 rc = EINVAL;
964 } else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
965 /*
966 * The node will be shrunk, but no clusters will be deallocated.
967 */
968 nodep->size = size;
969 nodep->dirty = true; /* need to sync node */
970 rc = EOK;
971 } else {
972 /*
973 * The node will be shrunk, clusters will be deallocated.
974 */
975 if (size == 0) {
976 fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
977 } else {
978 fat_cluster_t lastc;
979 (void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
980 &lastc, (size - 1) / bpc);
981 fat_chop_clusters(bs, nodep, lastc);
982 }
983 nodep->size = size;
984 nodep->dirty = true; /* need to sync node */
985 rc = EOK;
986 }
987 fat_node_put(nodep);
988 ipc_answer_0(rid, rc);
989 return;
990}
991
992void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
993{
994 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
995 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
996 int rc;
997
998 fat_node_t *nodep = fat_node_get(dev_handle, index);
999 if (!nodep) {
1000 ipc_answer_0(rid, ENOENT);
1001 return;
1002 }
1003
1004 rc = fat_destroy_node(nodep);
1005 ipc_answer_0(rid, rc);
1006}
1007
1008/**
1009 * @}
1010 */
Note: See TracBrowser for help on using the repository browser.