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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7afb4a5 was 92fd52d7, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

Nuke strcpy() and strcmp().

  • Property mode set to 100644
File size: 27.6 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 strcpy(d->name, FAT_NAME_DOT);
493 strcpy(d->ext, 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 strcpy(d->name, FAT_NAME_DOT_DOT);
503 strcpy(d->ext, 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 /* initialize libblock */
759 rc = block_init(dev_handle, BS_SIZE);
760 if (rc != EOK) {
761 ipc_answer_0(rid, rc);
762 return;
763 }
764
765 /* prepare the boot block */
766 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
767 if (rc != EOK) {
768 block_fini(dev_handle);
769 ipc_answer_0(rid, rc);
770 return;
771 }
772
773 /* get the buffer with the boot sector */
774 bs = block_bb_get(dev_handle);
775
776 /* Read the number of root directory entries. */
777 bps = uint16_t_le2host(bs->bps);
778 rde = uint16_t_le2host(bs->root_ent_max);
779
780 if (bps != BS_SIZE) {
781 block_fini(dev_handle);
782 ipc_answer_0(rid, ENOTSUP);
783 return;
784 }
785
786 /* Initialize the block cache */
787 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
788 if (rc != EOK) {
789 block_fini(dev_handle);
790 ipc_answer_0(rid, rc);
791 return;
792 }
793
794 rc = fat_idx_init_by_dev_handle(dev_handle);
795 if (rc != EOK) {
796 block_fini(dev_handle);
797 ipc_answer_0(rid, rc);
798 return;
799 }
800
801 /* Initialize the root node. */
802 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
803 if (!rootp) {
804 block_fini(dev_handle);
805 fat_idx_fini_by_dev_handle(dev_handle);
806 ipc_answer_0(rid, ENOMEM);
807 return;
808 }
809 fat_node_initialize(rootp);
810
811 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
812 if (!ridxp) {
813 block_fini(dev_handle);
814 free(rootp);
815 fat_idx_fini_by_dev_handle(dev_handle);
816 ipc_answer_0(rid, ENOMEM);
817 return;
818 }
819 assert(ridxp->index == 0);
820 /* ridxp->lock held */
821
822 rootp->type = FAT_DIRECTORY;
823 rootp->firstc = FAT_CLST_ROOT;
824 rootp->refcnt = 1;
825 rootp->lnkcnt = 0; /* FS root is not linked */
826 rootp->size = rde * sizeof(fat_dentry_t);
827 rootp->idx = ridxp;
828 ridxp->nodep = rootp;
829
830 futex_up(&ridxp->lock);
831
832 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
833}
834
835void fat_mount(ipc_callid_t rid, ipc_call_t *request)
836{
837 ipc_answer_0(rid, ENOTSUP);
838}
839
840void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
841{
842 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
843}
844
845void fat_read(ipc_callid_t rid, ipc_call_t *request)
846{
847 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
848 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
849 off_t pos = (off_t)IPC_GET_ARG3(*request);
850 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
851 fat_bs_t *bs;
852 uint16_t bps;
853 size_t bytes;
854 block_t *b;
855
856 if (!nodep) {
857 ipc_answer_0(rid, ENOENT);
858 return;
859 }
860
861 ipc_callid_t callid;
862 size_t len;
863 if (!ipc_data_read_receive(&callid, &len)) {
864 fat_node_put(nodep);
865 ipc_answer_0(callid, EINVAL);
866 ipc_answer_0(rid, EINVAL);
867 return;
868 }
869
870 bs = block_bb_get(dev_handle);
871 bps = uint16_t_le2host(bs->bps);
872
873 if (nodep->type == FAT_FILE) {
874 /*
875 * Our strategy for regular file reads is to read one block at
876 * most and make use of the possibility to return less data than
877 * requested. This keeps the code very simple.
878 */
879 if (pos >= nodep->size) {
880 /* reading beyond the EOF */
881 bytes = 0;
882 (void) ipc_data_read_finalize(callid, NULL, 0);
883 } else {
884 bytes = min(len, bps - pos % bps);
885 bytes = min(bytes, nodep->size - pos);
886 b = fat_block_get(bs, nodep, pos / bps,
887 BLOCK_FLAGS_NONE);
888 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
889 bytes);
890 block_put(b);
891 }
892 } else {
893 unsigned bnum;
894 off_t spos = pos;
895 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
896 fat_dentry_t *d;
897
898 assert(nodep->type == FAT_DIRECTORY);
899 assert(nodep->size % bps == 0);
900 assert(bps % sizeof(fat_dentry_t) == 0);
901
902 /*
903 * Our strategy for readdir() is to use the position pointer as
904 * an index into the array of all dentries. On entry, it points
905 * to the first unread dentry. If we skip any dentries, we bump
906 * the position pointer accordingly.
907 */
908 bnum = (pos * sizeof(fat_dentry_t)) / bps;
909 while (bnum < nodep->size / bps) {
910 off_t o;
911
912 b = fat_block_get(bs, nodep, bnum, BLOCK_FLAGS_NONE);
913 for (o = pos % (bps / sizeof(fat_dentry_t));
914 o < bps / sizeof(fat_dentry_t);
915 o++, pos++) {
916 d = ((fat_dentry_t *)b->data) + o;
917 switch (fat_classify_dentry(d)) {
918 case FAT_DENTRY_SKIP:
919 case FAT_DENTRY_FREE:
920 continue;
921 case FAT_DENTRY_LAST:
922 block_put(b);
923 goto miss;
924 default:
925 case FAT_DENTRY_VALID:
926 fat_dentry_name_get(d, name);
927 block_put(b);
928 goto hit;
929 }
930 }
931 block_put(b);
932 bnum++;
933 }
934miss:
935 fat_node_put(nodep);
936 ipc_answer_0(callid, ENOENT);
937 ipc_answer_1(rid, ENOENT, 0);
938 return;
939hit:
940 (void) ipc_data_read_finalize(callid, name, str_size(name) + 1);
941 bytes = (pos - spos) + 1;
942 }
943
944 fat_node_put(nodep);
945 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
946}
947
948void fat_write(ipc_callid_t rid, ipc_call_t *request)
949{
950 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
951 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
952 off_t pos = (off_t)IPC_GET_ARG3(*request);
953 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
954 fat_bs_t *bs;
955 size_t bytes;
956 block_t *b;
957 uint16_t bps;
958 unsigned spc;
959 unsigned bpc; /* bytes per cluster */
960 off_t boundary;
961 int flags = BLOCK_FLAGS_NONE;
962
963 if (!nodep) {
964 ipc_answer_0(rid, ENOENT);
965 return;
966 }
967
968 ipc_callid_t callid;
969 size_t len;
970 if (!ipc_data_write_receive(&callid, &len)) {
971 fat_node_put(nodep);
972 ipc_answer_0(callid, EINVAL);
973 ipc_answer_0(rid, EINVAL);
974 return;
975 }
976
977 bs = block_bb_get(dev_handle);
978 bps = uint16_t_le2host(bs->bps);
979 spc = bs->spc;
980 bpc = bps * spc;
981
982 /*
983 * In all scenarios, we will attempt to write out only one block worth
984 * of data at maximum. There might be some more efficient approaches,
985 * but this one greatly simplifies fat_write(). Note that we can afford
986 * to do this because the client must be ready to handle the return
987 * value signalizing a smaller number of bytes written.
988 */
989 bytes = min(len, bps - pos % bps);
990 if (bytes == bps)
991 flags |= BLOCK_FLAGS_NOREAD;
992
993 boundary = ROUND_UP(nodep->size, bpc);
994 if (pos < boundary) {
995 /*
996 * This is the easier case - we are either overwriting already
997 * existing contents or writing behind the EOF, but still within
998 * the limits of the last cluster. The node size may grow to the
999 * next block size boundary.
1000 */
1001 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
1002 b = fat_block_get(bs, nodep, pos / bps, flags);
1003 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1004 bytes);
1005 b->dirty = true; /* need to sync block */
1006 block_put(b);
1007 if (pos + bytes > nodep->size) {
1008 nodep->size = pos + bytes;
1009 nodep->dirty = true; /* need to sync node */
1010 }
1011 ipc_answer_2(rid, EOK, bytes, nodep->size);
1012 fat_node_put(nodep);
1013 return;
1014 } else {
1015 /*
1016 * This is the more difficult case. We must allocate new
1017 * clusters for the node and zero them out.
1018 */
1019 int status;
1020 unsigned nclsts;
1021 fat_cluster_t mcl, lcl;
1022
1023 nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
1024 /* create an independent chain of nclsts clusters in all FATs */
1025 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
1026 if (status != EOK) {
1027 /* could not allocate a chain of nclsts clusters */
1028 fat_node_put(nodep);
1029 ipc_answer_0(callid, status);
1030 ipc_answer_0(rid, status);
1031 return;
1032 }
1033 /* zero fill any gaps */
1034 fat_fill_gap(bs, nodep, mcl, pos);
1035 b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
1036 flags);
1037 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1038 bytes);
1039 b->dirty = true; /* need to sync block */
1040 block_put(b);
1041 /*
1042 * Append the cluster chain starting in mcl to the end of the
1043 * node's cluster chain.
1044 */
1045 fat_append_clusters(bs, nodep, mcl);
1046 nodep->size = pos + bytes;
1047 nodep->dirty = true; /* need to sync node */
1048 ipc_answer_2(rid, EOK, bytes, nodep->size);
1049 fat_node_put(nodep);
1050 return;
1051 }
1052}
1053
1054void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
1055{
1056 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1057 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1058 size_t size = (off_t)IPC_GET_ARG3(*request);
1059 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
1060 fat_bs_t *bs;
1061 uint16_t bps;
1062 uint8_t spc;
1063 unsigned bpc; /* bytes per cluster */
1064 int rc;
1065
1066 if (!nodep) {
1067 ipc_answer_0(rid, ENOENT);
1068 return;
1069 }
1070
1071 bs = block_bb_get(dev_handle);
1072 bps = uint16_t_le2host(bs->bps);
1073 spc = bs->spc;
1074 bpc = bps * spc;
1075
1076 if (nodep->size == size) {
1077 rc = EOK;
1078 } else if (nodep->size < size) {
1079 /*
1080 * The standard says we have the freedom to grow the node.
1081 * For now, we simply return an error.
1082 */
1083 rc = EINVAL;
1084 } else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
1085 /*
1086 * The node will be shrunk, but no clusters will be deallocated.
1087 */
1088 nodep->size = size;
1089 nodep->dirty = true; /* need to sync node */
1090 rc = EOK;
1091 } else {
1092 /*
1093 * The node will be shrunk, clusters will be deallocated.
1094 */
1095 if (size == 0) {
1096 fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
1097 } else {
1098 fat_cluster_t lastc;
1099 (void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
1100 &lastc, (size - 1) / bpc);
1101 fat_chop_clusters(bs, nodep, lastc);
1102 }
1103 nodep->size = size;
1104 nodep->dirty = true; /* need to sync node */
1105 rc = EOK;
1106 }
1107 fat_node_put(nodep);
1108 ipc_answer_0(rid, rc);
1109 return;
1110}
1111
1112void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
1113{
1114 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1115 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1116 int rc;
1117
1118 fat_node_t *nodep = fat_node_get(dev_handle, index);
1119 if (!nodep) {
1120 ipc_answer_0(rid, ENOENT);
1121 return;
1122 }
1123
1124 rc = fat_destroy_node(nodep);
1125 ipc_answer_0(rid, rc);
1126}
1127
1128/**
1129 * @}
1130 */
Note: See TracBrowser for help on using the repository browser.