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

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

Support mount options.
Let tmpfs decide whether to start empty or restored a TMPFS dump based on the mount option.

  • Property mode set to 100644
File size: 28.0 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 fat_bs_t *bs;
292 fat_cluster_t mcl, lcl;
293 uint16_t bps;
294 int rc;
295
296 bs = block_bb_get(dev_handle);
297 bps = uint16_t_le2host(bs->bps);
298 if (flags & L_DIRECTORY) {
299 /* allocate a cluster */
300 rc = fat_alloc_clusters(bs, dev_handle, 1, &mcl, &lcl);
301 if (rc != EOK)
302 return NULL;
303 }
304
305 nodep = fat_node_get_new();
306 if (!nodep) {
307 fat_free_clusters(bs, dev_handle, mcl);
308 return NULL;
309 }
310 idxp = fat_idx_get_new(dev_handle);
311 if (!idxp) {
312 fat_free_clusters(bs, dev_handle, mcl);
313 fat_node_put(nodep);
314 return NULL;
315 }
316 /* idxp->lock held */
317 if (flags & L_DIRECTORY) {
318 int i;
319 block_t *b;
320
321 /*
322 * Populate the new cluster with unused dentries.
323 */
324 for (i = 0; i < bs->spc; i++) {
325 b = _fat_block_get(bs, dev_handle, mcl, i,
326 BLOCK_FLAGS_NOREAD);
327 /* mark all dentries as never-used */
328 memset(b->data, 0, bps);
329 b->dirty = false;
330 block_put(b);
331 }
332 nodep->type = FAT_DIRECTORY;
333 nodep->firstc = mcl;
334 nodep->size = bps * bs->spc;
335 } else {
336 nodep->type = FAT_FILE;
337 nodep->firstc = FAT_CLST_RES0;
338 nodep->size = 0;
339 }
340 nodep->lnkcnt = 0; /* not linked anywhere */
341 nodep->refcnt = 1;
342 nodep->dirty = true;
343
344 nodep->idx = idxp;
345 idxp->nodep = nodep;
346
347 futex_up(&idxp->lock);
348 return nodep;
349}
350
351int fat_destroy_node(void *node)
352{
353 fat_node_t *nodep = (fat_node_t *)node;
354 fat_bs_t *bs;
355
356 /*
357 * The node is not reachable from the file system. This means that the
358 * link count should be zero and that the index structure cannot be
359 * found in the position hash. Obviously, we don't need to lock the node
360 * nor its index structure.
361 */
362 assert(nodep->lnkcnt == 0);
363
364 /*
365 * The node may not have any children.
366 */
367 assert(fat_has_children(node) == false);
368
369 bs = block_bb_get(nodep->idx->dev_handle);
370 if (nodep->firstc != FAT_CLST_RES0) {
371 assert(nodep->size);
372 /* Free all clusters allocated to the node. */
373 fat_free_clusters(bs, nodep->idx->dev_handle, nodep->firstc);
374 }
375
376 fat_idx_destroy(nodep->idx);
377 free(nodep);
378 return EOK;
379}
380
381int fat_link(void *prnt, void *chld, const char *name)
382{
383 fat_node_t *parentp = (fat_node_t *)prnt;
384 fat_node_t *childp = (fat_node_t *)chld;
385 fat_dentry_t *d;
386 fat_bs_t *bs;
387 block_t *b;
388 int i, j;
389 uint16_t bps;
390 unsigned dps;
391 unsigned blocks;
392 fat_cluster_t mcl, lcl;
393 int rc;
394
395 futex_down(&childp->lock);
396 if (childp->lnkcnt == 1) {
397 /*
398 * On FAT, we don't support multiple hard links.
399 */
400 futex_up(&childp->lock);
401 return EMLINK;
402 }
403 assert(childp->lnkcnt == 0);
404 futex_up(&childp->lock);
405
406 if (!fat_dentry_name_verify(name)) {
407 /*
408 * Attempt to create unsupported name.
409 */
410 return ENOTSUP;
411 }
412
413 /*
414 * Get us an unused parent node's dentry or grow the parent and allocate
415 * a new one.
416 */
417
418 futex_down(&parentp->idx->lock);
419 bs = block_bb_get(parentp->idx->dev_handle);
420 bps = uint16_t_le2host(bs->bps);
421 dps = bps / sizeof(fat_dentry_t);
422
423 blocks = parentp->size / bps;
424
425 for (i = 0; i < blocks; i++) {
426 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
427 for (j = 0; j < dps; j++) {
428 d = ((fat_dentry_t *)b->data) + j;
429 switch (fat_classify_dentry(d)) {
430 case FAT_DENTRY_SKIP:
431 case FAT_DENTRY_VALID:
432 /* skipping used and meta entries */
433 continue;
434 case FAT_DENTRY_FREE:
435 case FAT_DENTRY_LAST:
436 /* found an empty slot */
437 goto hit;
438 }
439 }
440 block_put(b);
441 }
442 j = 0;
443
444 /*
445 * We need to grow the parent in order to create a new unused dentry.
446 */
447 if (parentp->idx->pfc == FAT_CLST_ROOT) {
448 /* Can't grow the root directory. */
449 futex_up(&parentp->idx->lock);
450 return ENOSPC;
451 }
452 rc = fat_alloc_clusters(bs, parentp->idx->dev_handle, 1, &mcl, &lcl);
453 if (rc != EOK) {
454 futex_up(&parentp->idx->lock);
455 return rc;
456 }
457 fat_append_clusters(bs, parentp, mcl);
458 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NOREAD);
459 d = (fat_dentry_t *)b->data;
460 /*
461 * Clear all dentries in the block except for the first one (the first
462 * dentry will be cleared in the next step).
463 */
464 memset(d + 1, 0, bps - sizeof(fat_dentry_t));
465
466hit:
467 /*
468 * At this point we only establish the link between the parent and the
469 * child. The dentry, except of the name and the extension, will remain
470 * uninitialized until the corresponding node is synced. Thus the valid
471 * dentry data is kept in the child node structure.
472 */
473 memset(d, 0, sizeof(fat_dentry_t));
474 fat_dentry_name_set(d, name);
475 b->dirty = true; /* need to sync block */
476 block_put(b);
477 futex_up(&parentp->idx->lock);
478
479 futex_down(&childp->idx->lock);
480
481 /*
482 * If possible, create the Sub-directory Identifier Entry and the
483 * Sub-directory Parent Pointer Entry (i.e. "." and ".."). These entries
484 * are not mandatory according to Standard ECMA-107 and HelenOS VFS does
485 * not use them anyway, so this is rather a sign of our good will.
486 */
487 b = fat_block_get(bs, childp, 0, BLOCK_FLAGS_NONE);
488 d = (fat_dentry_t *)b->data;
489 if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
490 str_cmp(d->name, FAT_NAME_DOT) == 0) {
491 memset(d, 0, sizeof(fat_dentry_t));
492 str_cpy(d->name, 8, FAT_NAME_DOT);
493 str_cpy(d->ext, 3, FAT_EXT_PAD);
494 d->attr = FAT_ATTR_SUBDIR;
495 d->firstc = host2uint16_t_le(childp->firstc);
496 /* TODO: initialize also the date/time members. */
497 }
498 d++;
499 if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
500 str_cmp(d->name, FAT_NAME_DOT_DOT) == 0) {
501 memset(d, 0, sizeof(fat_dentry_t));
502 str_cpy(d->name, 8, FAT_NAME_DOT_DOT);
503 str_cpy(d->ext, 3, FAT_EXT_PAD);
504 d->attr = FAT_ATTR_SUBDIR;
505 d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
506 host2uint16_t_le(FAT_CLST_RES0) :
507 host2uint16_t_le(parentp->firstc);
508 /* TODO: initialize also the date/time members. */
509 }
510 b->dirty = true; /* need to sync block */
511 block_put(b);
512
513 childp->idx->pfc = parentp->firstc;
514 childp->idx->pdi = i * dps + j;
515 futex_up(&childp->idx->lock);
516
517 futex_down(&childp->lock);
518 childp->lnkcnt = 1;
519 childp->dirty = true; /* need to sync node */
520 futex_up(&childp->lock);
521
522 /*
523 * Hash in the index structure into the position hash.
524 */
525 fat_idx_hashin(childp->idx);
526
527 return EOK;
528}
529
530int fat_unlink(void *prnt, void *chld)
531{
532 fat_node_t *parentp = (fat_node_t *)prnt;
533 fat_node_t *childp = (fat_node_t *)chld;
534 fat_bs_t *bs;
535 fat_dentry_t *d;
536 uint16_t bps;
537 block_t *b;
538
539 futex_down(&parentp->lock);
540 futex_down(&childp->lock);
541 assert(childp->lnkcnt == 1);
542 futex_down(&childp->idx->lock);
543 bs = block_bb_get(childp->idx->dev_handle);
544 bps = uint16_t_le2host(bs->bps);
545
546 b = _fat_block_get(bs, childp->idx->dev_handle, childp->idx->pfc,
547 (childp->idx->pdi * sizeof(fat_dentry_t)) / bps,
548 BLOCK_FLAGS_NONE);
549 d = (fat_dentry_t *)b->data +
550 (childp->idx->pdi % (bps / sizeof(fat_dentry_t)));
551 /* mark the dentry as not-currently-used */
552 d->name[0] = FAT_DENTRY_ERASED;
553 b->dirty = true; /* need to sync block */
554 block_put(b);
555
556 /* remove the index structure from the position hash */
557 fat_idx_hashout(childp->idx);
558 /* clear position information */
559 childp->idx->pfc = FAT_CLST_RES0;
560 childp->idx->pdi = 0;
561 futex_up(&childp->idx->lock);
562 childp->lnkcnt = 0;
563 childp->dirty = true;
564 futex_up(&childp->lock);
565 futex_up(&parentp->lock);
566
567 return EOK;
568}
569
570void *fat_match(void *prnt, const char *component)
571{
572 fat_bs_t *bs;
573 fat_node_t *parentp = (fat_node_t *)prnt;
574 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
575 unsigned i, j;
576 unsigned bps; /* bytes per sector */
577 unsigned dps; /* dentries per sector */
578 unsigned blocks;
579 fat_dentry_t *d;
580 block_t *b;
581
582 futex_down(&parentp->idx->lock);
583 bs = block_bb_get(parentp->idx->dev_handle);
584 bps = uint16_t_le2host(bs->bps);
585 dps = bps / sizeof(fat_dentry_t);
586 blocks = parentp->size / bps;
587 for (i = 0; i < blocks; i++) {
588 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
589 for (j = 0; j < dps; j++) {
590 d = ((fat_dentry_t *)b->data) + j;
591 switch (fat_classify_dentry(d)) {
592 case FAT_DENTRY_SKIP:
593 case FAT_DENTRY_FREE:
594 continue;
595 case FAT_DENTRY_LAST:
596 block_put(b);
597 futex_up(&parentp->idx->lock);
598 return NULL;
599 default:
600 case FAT_DENTRY_VALID:
601 fat_dentry_name_get(d, name);
602 break;
603 }
604 if (fat_dentry_namecmp(name, component) == 0) {
605 /* hit */
606 void *node;
607 /*
608 * Assume tree hierarchy for locking. We
609 * already have the parent and now we are going
610 * to lock the child. Never lock in the oposite
611 * order.
612 */
613 fat_idx_t *idx = fat_idx_get_by_pos(
614 parentp->idx->dev_handle, parentp->firstc,
615 i * dps + j);
616 futex_up(&parentp->idx->lock);
617 if (!idx) {
618 /*
619 * Can happen if memory is low or if we
620 * run out of 32-bit indices.
621 */
622 block_put(b);
623 return NULL;
624 }
625 node = fat_node_get_core(idx);
626 futex_up(&idx->lock);
627 block_put(b);
628 return node;
629 }
630 }
631 block_put(b);
632 }
633
634 futex_up(&parentp->idx->lock);
635 return NULL;
636}
637
638fs_index_t fat_index_get(void *node)
639{
640 fat_node_t *fnodep = (fat_node_t *)node;
641 if (!fnodep)
642 return 0;
643 return fnodep->idx->index;
644}
645
646size_t fat_size_get(void *node)
647{
648 return ((fat_node_t *)node)->size;
649}
650
651unsigned fat_lnkcnt_get(void *node)
652{
653 return ((fat_node_t *)node)->lnkcnt;
654}
655
656bool fat_has_children(void *node)
657{
658 fat_bs_t *bs;
659 fat_node_t *nodep = (fat_node_t *)node;
660 unsigned bps;
661 unsigned dps;
662 unsigned blocks;
663 block_t *b;
664 unsigned i, j;
665
666 if (nodep->type != FAT_DIRECTORY)
667 return false;
668
669 futex_down(&nodep->idx->lock);
670 bs = block_bb_get(nodep->idx->dev_handle);
671 bps = uint16_t_le2host(bs->bps);
672 dps = bps / sizeof(fat_dentry_t);
673
674 blocks = nodep->size / bps;
675
676 for (i = 0; i < blocks; i++) {
677 fat_dentry_t *d;
678
679 b = fat_block_get(bs, nodep, i, BLOCK_FLAGS_NONE);
680 for (j = 0; j < dps; j++) {
681 d = ((fat_dentry_t *)b->data) + j;
682 switch (fat_classify_dentry(d)) {
683 case FAT_DENTRY_SKIP:
684 case FAT_DENTRY_FREE:
685 continue;
686 case FAT_DENTRY_LAST:
687 block_put(b);
688 futex_up(&nodep->idx->lock);
689 return false;
690 default:
691 case FAT_DENTRY_VALID:
692 block_put(b);
693 futex_up(&nodep->idx->lock);
694 return true;
695 }
696 block_put(b);
697 futex_up(&nodep->idx->lock);
698 return true;
699 }
700 block_put(b);
701 }
702
703 futex_up(&nodep->idx->lock);
704 return false;
705}
706
707void *fat_root_get(dev_handle_t dev_handle)
708{
709 return fat_node_get(dev_handle, 0);
710}
711
712char fat_plb_get_char(unsigned pos)
713{
714 return fat_reg.plb_ro[pos % PLB_SIZE];
715}
716
717bool fat_is_directory(void *node)
718{
719 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
720}
721
722bool fat_is_file(void *node)
723{
724 return ((fat_node_t *)node)->type == FAT_FILE;
725}
726
727/** libfs operations */
728libfs_ops_t fat_libfs_ops = {
729 .match = fat_match,
730 .node_get = fat_node_get,
731 .node_put = fat_node_put,
732 .create = fat_create_node,
733 .destroy = fat_destroy_node,
734 .link = fat_link,
735 .unlink = fat_unlink,
736 .index_get = fat_index_get,
737 .size_get = fat_size_get,
738 .lnkcnt_get = fat_lnkcnt_get,
739 .has_children = fat_has_children,
740 .root_get = fat_root_get,
741 .plb_get_char = fat_plb_get_char,
742 .is_directory = fat_is_directory,
743 .is_file = fat_is_file
744};
745
746/*
747 * VFS operations.
748 */
749
750void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
751{
752 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
753 fat_bs_t *bs;
754 uint16_t bps;
755 uint16_t rde;
756 int rc;
757
758 /* accept the mount options */
759 ipc_callid_t callid;
760 size_t size;
761 if (!ipc_data_write_receive(&callid, &size)) {
762 ipc_answer_0(callid, EINVAL);
763 ipc_answer_0(rid, EINVAL);
764 return;
765 }
766 char *opts = malloc(size + 1);
767 if (!opts) {
768 ipc_answer_0(callid, ENOMEM);
769 ipc_answer_0(rid, ENOMEM);
770 return;
771 }
772 ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
773 if (retval != EOK) {
774 ipc_answer_0(rid, retval);
775 free(opts);
776 return;
777 }
778 opts[size] = '\0';
779
780 /* initialize libblock */
781 rc = block_init(dev_handle, BS_SIZE);
782 if (rc != EOK) {
783 ipc_answer_0(rid, rc);
784 return;
785 }
786
787 /* prepare the boot block */
788 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
789 if (rc != EOK) {
790 block_fini(dev_handle);
791 ipc_answer_0(rid, rc);
792 return;
793 }
794
795 /* get the buffer with the boot sector */
796 bs = block_bb_get(dev_handle);
797
798 /* Read the number of root directory entries. */
799 bps = uint16_t_le2host(bs->bps);
800 rde = uint16_t_le2host(bs->root_ent_max);
801
802 if (bps != BS_SIZE) {
803 block_fini(dev_handle);
804 ipc_answer_0(rid, ENOTSUP);
805 return;
806 }
807
808 /* Initialize the block cache */
809 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
810 if (rc != EOK) {
811 block_fini(dev_handle);
812 ipc_answer_0(rid, rc);
813 return;
814 }
815
816 rc = fat_idx_init_by_dev_handle(dev_handle);
817 if (rc != EOK) {
818 block_fini(dev_handle);
819 ipc_answer_0(rid, rc);
820 return;
821 }
822
823 /* Initialize the root node. */
824 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
825 if (!rootp) {
826 block_fini(dev_handle);
827 fat_idx_fini_by_dev_handle(dev_handle);
828 ipc_answer_0(rid, ENOMEM);
829 return;
830 }
831 fat_node_initialize(rootp);
832
833 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
834 if (!ridxp) {
835 block_fini(dev_handle);
836 free(rootp);
837 fat_idx_fini_by_dev_handle(dev_handle);
838 ipc_answer_0(rid, ENOMEM);
839 return;
840 }
841 assert(ridxp->index == 0);
842 /* ridxp->lock held */
843
844 rootp->type = FAT_DIRECTORY;
845 rootp->firstc = FAT_CLST_ROOT;
846 rootp->refcnt = 1;
847 rootp->lnkcnt = 0; /* FS root is not linked */
848 rootp->size = rde * sizeof(fat_dentry_t);
849 rootp->idx = ridxp;
850 ridxp->nodep = rootp;
851
852 futex_up(&ridxp->lock);
853
854 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
855}
856
857void fat_mount(ipc_callid_t rid, ipc_call_t *request)
858{
859 ipc_answer_0(rid, ENOTSUP);
860}
861
862void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
863{
864 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
865}
866
867void fat_read(ipc_callid_t rid, ipc_call_t *request)
868{
869 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
870 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
871 off_t pos = (off_t)IPC_GET_ARG3(*request);
872 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
873 fat_bs_t *bs;
874 uint16_t bps;
875 size_t bytes;
876 block_t *b;
877
878 if (!nodep) {
879 ipc_answer_0(rid, ENOENT);
880 return;
881 }
882
883 ipc_callid_t callid;
884 size_t len;
885 if (!ipc_data_read_receive(&callid, &len)) {
886 fat_node_put(nodep);
887 ipc_answer_0(callid, EINVAL);
888 ipc_answer_0(rid, EINVAL);
889 return;
890 }
891
892 bs = block_bb_get(dev_handle);
893 bps = uint16_t_le2host(bs->bps);
894
895 if (nodep->type == FAT_FILE) {
896 /*
897 * Our strategy for regular file reads is to read one block at
898 * most and make use of the possibility to return less data than
899 * requested. This keeps the code very simple.
900 */
901 if (pos >= nodep->size) {
902 /* reading beyond the EOF */
903 bytes = 0;
904 (void) ipc_data_read_finalize(callid, NULL, 0);
905 } else {
906 bytes = min(len, bps - pos % bps);
907 bytes = min(bytes, nodep->size - pos);
908 b = fat_block_get(bs, nodep, pos / bps,
909 BLOCK_FLAGS_NONE);
910 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
911 bytes);
912 block_put(b);
913 }
914 } else {
915 unsigned bnum;
916 off_t spos = pos;
917 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
918 fat_dentry_t *d;
919
920 assert(nodep->type == FAT_DIRECTORY);
921 assert(nodep->size % bps == 0);
922 assert(bps % sizeof(fat_dentry_t) == 0);
923
924 /*
925 * Our strategy for readdir() is to use the position pointer as
926 * an index into the array of all dentries. On entry, it points
927 * to the first unread dentry. If we skip any dentries, we bump
928 * the position pointer accordingly.
929 */
930 bnum = (pos * sizeof(fat_dentry_t)) / bps;
931 while (bnum < nodep->size / bps) {
932 off_t o;
933
934 b = fat_block_get(bs, nodep, bnum, BLOCK_FLAGS_NONE);
935 for (o = pos % (bps / sizeof(fat_dentry_t));
936 o < bps / sizeof(fat_dentry_t);
937 o++, pos++) {
938 d = ((fat_dentry_t *)b->data) + o;
939 switch (fat_classify_dentry(d)) {
940 case FAT_DENTRY_SKIP:
941 case FAT_DENTRY_FREE:
942 continue;
943 case FAT_DENTRY_LAST:
944 block_put(b);
945 goto miss;
946 default:
947 case FAT_DENTRY_VALID:
948 fat_dentry_name_get(d, name);
949 block_put(b);
950 goto hit;
951 }
952 }
953 block_put(b);
954 bnum++;
955 }
956miss:
957 fat_node_put(nodep);
958 ipc_answer_0(callid, ENOENT);
959 ipc_answer_1(rid, ENOENT, 0);
960 return;
961hit:
962 (void) ipc_data_read_finalize(callid, name, str_size(name) + 1);
963 bytes = (pos - spos) + 1;
964 }
965
966 fat_node_put(nodep);
967 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
968}
969
970void fat_write(ipc_callid_t rid, ipc_call_t *request)
971{
972 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
973 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
974 off_t pos = (off_t)IPC_GET_ARG3(*request);
975 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
976 fat_bs_t *bs;
977 size_t bytes;
978 block_t *b;
979 uint16_t bps;
980 unsigned spc;
981 unsigned bpc; /* bytes per cluster */
982 off_t boundary;
983 int flags = BLOCK_FLAGS_NONE;
984
985 if (!nodep) {
986 ipc_answer_0(rid, ENOENT);
987 return;
988 }
989
990 ipc_callid_t callid;
991 size_t len;
992 if (!ipc_data_write_receive(&callid, &len)) {
993 fat_node_put(nodep);
994 ipc_answer_0(callid, EINVAL);
995 ipc_answer_0(rid, EINVAL);
996 return;
997 }
998
999 bs = block_bb_get(dev_handle);
1000 bps = uint16_t_le2host(bs->bps);
1001 spc = bs->spc;
1002 bpc = bps * spc;
1003
1004 /*
1005 * In all scenarios, we will attempt to write out only one block worth
1006 * of data at maximum. There might be some more efficient approaches,
1007 * but this one greatly simplifies fat_write(). Note that we can afford
1008 * to do this because the client must be ready to handle the return
1009 * value signalizing a smaller number of bytes written.
1010 */
1011 bytes = min(len, bps - pos % bps);
1012 if (bytes == bps)
1013 flags |= BLOCK_FLAGS_NOREAD;
1014
1015 boundary = ROUND_UP(nodep->size, bpc);
1016 if (pos < boundary) {
1017 /*
1018 * This is the easier case - we are either overwriting already
1019 * existing contents or writing behind the EOF, but still within
1020 * the limits of the last cluster. The node size may grow to the
1021 * next block size boundary.
1022 */
1023 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
1024 b = fat_block_get(bs, nodep, pos / bps, flags);
1025 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1026 bytes);
1027 b->dirty = true; /* need to sync block */
1028 block_put(b);
1029 if (pos + bytes > nodep->size) {
1030 nodep->size = pos + bytes;
1031 nodep->dirty = true; /* need to sync node */
1032 }
1033 ipc_answer_2(rid, EOK, bytes, nodep->size);
1034 fat_node_put(nodep);
1035 return;
1036 } else {
1037 /*
1038 * This is the more difficult case. We must allocate new
1039 * clusters for the node and zero them out.
1040 */
1041 int status;
1042 unsigned nclsts;
1043 fat_cluster_t mcl, lcl;
1044
1045 nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
1046 /* create an independent chain of nclsts clusters in all FATs */
1047 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
1048 if (status != EOK) {
1049 /* could not allocate a chain of nclsts clusters */
1050 fat_node_put(nodep);
1051 ipc_answer_0(callid, status);
1052 ipc_answer_0(rid, status);
1053 return;
1054 }
1055 /* zero fill any gaps */
1056 fat_fill_gap(bs, nodep, mcl, pos);
1057 b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
1058 flags);
1059 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1060 bytes);
1061 b->dirty = true; /* need to sync block */
1062 block_put(b);
1063 /*
1064 * Append the cluster chain starting in mcl to the end of the
1065 * node's cluster chain.
1066 */
1067 fat_append_clusters(bs, nodep, mcl);
1068 nodep->size = pos + bytes;
1069 nodep->dirty = true; /* need to sync node */
1070 ipc_answer_2(rid, EOK, bytes, nodep->size);
1071 fat_node_put(nodep);
1072 return;
1073 }
1074}
1075
1076void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
1077{
1078 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1079 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1080 size_t size = (off_t)IPC_GET_ARG3(*request);
1081 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
1082 fat_bs_t *bs;
1083 uint16_t bps;
1084 uint8_t spc;
1085 unsigned bpc; /* bytes per cluster */
1086 int rc;
1087
1088 if (!nodep) {
1089 ipc_answer_0(rid, ENOENT);
1090 return;
1091 }
1092
1093 bs = block_bb_get(dev_handle);
1094 bps = uint16_t_le2host(bs->bps);
1095 spc = bs->spc;
1096 bpc = bps * spc;
1097
1098 if (nodep->size == size) {
1099 rc = EOK;
1100 } else if (nodep->size < size) {
1101 /*
1102 * The standard says we have the freedom to grow the node.
1103 * For now, we simply return an error.
1104 */
1105 rc = EINVAL;
1106 } else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
1107 /*
1108 * The node will be shrunk, but no clusters will be deallocated.
1109 */
1110 nodep->size = size;
1111 nodep->dirty = true; /* need to sync node */
1112 rc = EOK;
1113 } else {
1114 /*
1115 * The node will be shrunk, clusters will be deallocated.
1116 */
1117 if (size == 0) {
1118 fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
1119 } else {
1120 fat_cluster_t lastc;
1121 (void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
1122 &lastc, (size - 1) / bpc);
1123 fat_chop_clusters(bs, nodep, lastc);
1124 }
1125 nodep->size = size;
1126 nodep->dirty = true; /* need to sync node */
1127 rc = EOK;
1128 }
1129 fat_node_put(nodep);
1130 ipc_answer_0(rid, rc);
1131 return;
1132}
1133
1134void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
1135{
1136 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1137 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1138 int rc;
1139
1140 fat_node_t *nodep = fat_node_get(dev_handle, index);
1141 if (!nodep) {
1142 ipc_answer_0(rid, ENOENT);
1143 return;
1144 }
1145
1146 rc = fat_destroy_node(nodep);
1147 ipc_answer_0(rid, rc);
1148}
1149
1150/**
1151 * @}
1152 */
Note: See TracBrowser for help on using the repository browser.