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

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

Support for readdir() on FAT.

  • Property mode set to 100644
File size: 19.8 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 "../../vfs/vfs.h"
40#include <libfs.h>
41#include <ipc/ipc.h>
42#include <ipc/services.h>
43#include <ipc/devmap.h>
44#include <async.h>
45#include <errno.h>
46#include <string.h>
47#include <byteorder.h>
48#include <libadt/hash_table.h>
49#include <libadt/list.h>
50#include <assert.h>
51#include <futex.h>
52#include <sys/mman.h>
53
54#define BS_BLOCK 0
55#define BS_SIZE 512
56
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);
62
63#define FAT_NAME_LEN 8
64#define FAT_EXT_LEN 3
65
66#define FAT_PAD ' '
67
68#define FAT_DENTRY_UNUSED 0x00
69#define FAT_DENTRY_E5_ESC 0x05
70#define FAT_DENTRY_DOT 0x2e
71#define FAT_DENTRY_ERASED 0xe5
72
73#define min(a, b) ((a) < (b) ? (a) : (b))
74
75static void dentry_name_canonify(fat_dentry_t *d, char *buf)
76{
77 int i;
78
79 for (i = 0; i < FAT_NAME_LEN; i++) {
80 if (d->name[i] == FAT_PAD)
81 break;
82 if (d->name[i] == FAT_DENTRY_E5_ESC)
83 *buf++ = 0xe5;
84 else
85 *buf++ = d->name[i];
86 }
87 if (d->ext[0] != FAT_PAD)
88 *buf++ = '.';
89 for (i = 0; i < FAT_EXT_LEN; i++) {
90 if (d->ext[i] == FAT_PAD) {
91 *buf = '\0';
92 return;
93 }
94 if (d->ext[i] == FAT_DENTRY_E5_ESC)
95 *buf++ = 0xe5;
96 else
97 *buf++ = d->ext[i];
98 }
99 *buf = '\0';
100}
101
102static int dev_phone = -1; /* FIXME */
103static void *dev_buffer = NULL; /* FIXME */
104
105/* TODO move somewhere else */
106typedef struct {
107 void *data;
108 size_t size;
109} block_t;
110
111static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
112{
113 /* FIXME */
114 block_t *b;
115 off_t bufpos = 0;
116 size_t buflen = 0;
117 off_t pos = offset * bs;
118
119 assert(dev_phone != -1);
120 assert(dev_buffer);
121
122 b = malloc(sizeof(block_t));
123 if (!b)
124 return NULL;
125
126 b->data = malloc(bs);
127 if (!b->data) {
128 free(b);
129 return NULL;
130 }
131 b->size = bs;
132
133 if (!libfs_blockread(dev_phone, dev_buffer, &bufpos, &buflen, &pos,
134 b->data, bs, bs)) {
135 free(b->data);
136 free(b);
137 return NULL;
138 }
139
140 return b;
141}
142
143static void block_put(block_t *block)
144{
145 /* FIXME */
146 free(block->data);
147 free(block);
148}
149
150#define FAT_BS(b) ((fat_bs_t *)((b)->data))
151
152#define FAT_CLST_RES0 0x0000
153#define FAT_CLST_RES1 0x0001
154#define FAT_CLST_FIRST 0x0002
155#define FAT_CLST_BAD 0xfff7
156#define FAT_CLST_LAST1 0xfff8
157#define FAT_CLST_LAST8 0xffff
158
159/* internally used to mark root directory's parent */
160#define FAT_CLST_ROOTPAR FAT_CLST_RES0
161/* internally used to mark root directory */
162#define FAT_CLST_ROOT FAT_CLST_RES1
163
164#define fat_block_get(np, off) \
165 _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
166
167static block_t *
168_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
169{
170 block_t *bb;
171 block_t *b;
172 unsigned bps;
173 unsigned spc;
174 unsigned rscnt; /* block address of the first FAT */
175 unsigned fatcnt;
176 unsigned rde;
177 unsigned rds; /* root directory size */
178 unsigned sf;
179 unsigned ssa; /* size of the system area */
180 unsigned clusters;
181 fat_cluster_t clst = firstc;
182 unsigned i;
183
184 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
185 bps = uint16_t_le2host(FAT_BS(bb)->bps);
186 spc = FAT_BS(bb)->spc;
187 rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
188 fatcnt = FAT_BS(bb)->fatcnt;
189 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
190 sf = uint16_t_le2host(FAT_BS(bb)->sec_per_fat);
191 block_put(bb);
192
193 rds = (sizeof(fat_dentry_t) * rde) / bps;
194 rds += ((sizeof(fat_dentry_t) * rde) % bps != 0);
195 ssa = rscnt + fatcnt * sf + rds;
196
197 if (firstc == FAT_CLST_ROOT) {
198 /* root directory special case */
199 assert(offset < rds);
200 b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
201 return b;
202 }
203
204 clusters = offset / spc;
205 for (i = 0; i < clusters; i++) {
206 unsigned fsec; /* sector offset relative to FAT1 */
207 unsigned fidx; /* FAT1 entry index */
208
209 assert(clst >= FAT_CLST_FIRST && clst < FAT_CLST_BAD);
210 fsec = (clst * sizeof(fat_cluster_t)) / bps;
211 fidx = clst % (bps / sizeof(fat_cluster_t));
212 /* read FAT1 */
213 b = block_get(dev_handle, rscnt + fsec, bps);
214 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
215 assert(clst != FAT_CLST_BAD);
216 assert(clst < FAT_CLST_LAST1);
217 block_put(b);
218 }
219
220 b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
221 offset % spc, bps);
222
223 return b;
224}
225
226/** Return number of blocks allocated to a file.
227 *
228 * @param dev_handle Device handle of the device with the file.
229 * @param firstc First cluster of the file.
230 *
231 * @return Number of blocks allocated to the file.
232 */
233static uint16_t
234_fat_blcks_get(dev_handle_t dev_handle, fat_cluster_t firstc)
235{
236 block_t *bb;
237 block_t *b;
238 unsigned bps;
239 unsigned spc;
240 unsigned rscnt; /* block address of the first FAT */
241 unsigned clusters = 0;
242 fat_cluster_t clst = firstc;
243
244 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
245 bps = uint16_t_le2host(FAT_BS(bb)->bps);
246 spc = FAT_BS(bb)->spc;
247 rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
248 block_put(bb);
249
250 if (firstc == FAT_CLST_RES0) {
251 /* No space allocated to the file. */
252 return 0;
253 }
254
255 while (clst < FAT_CLST_LAST1) {
256 unsigned fsec; /* sector offset relative to FAT1 */
257 unsigned fidx; /* FAT1 entry index */
258
259 assert(clst >= FAT_CLST_FIRST);
260 fsec = (clst * sizeof(fat_cluster_t)) / bps;
261 fidx = clst % (bps / sizeof(fat_cluster_t));
262 /* read FAT1 */
263 b = block_get(dev_handle, rscnt + fsec, bps);
264 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
265 assert(clst != FAT_CLST_BAD);
266 block_put(b);
267 clusters++;
268 }
269
270 return clusters * spc;
271}
272
273static void fat_node_initialize(fat_node_t *node)
274{
275 futex_initialize(&node->lock, 1);
276 node->idx = NULL;
277 node->type = 0;
278 link_initialize(&node->ffn_link);
279 node->size = 0;
280 node->lnkcnt = 0;
281 node->refcnt = 0;
282 node->dirty = false;
283}
284
285static uint16_t fat_bps_get(dev_handle_t dev_handle)
286{
287 block_t *bb;
288 uint16_t bps;
289
290 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
291 assert(bb != NULL);
292 bps = uint16_t_le2host(FAT_BS(bb)->bps);
293 block_put(bb);
294
295 return bps;
296}
297
298typedef enum {
299 FAT_DENTRY_SKIP,
300 FAT_DENTRY_LAST,
301 FAT_DENTRY_VALID
302} fat_dentry_clsf_t;
303
304static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
305{
306 if (d->attr & FAT_ATTR_VOLLABEL) {
307 /* volume label entry */
308 return FAT_DENTRY_SKIP;
309 }
310 if (d->name[0] == FAT_DENTRY_ERASED) {
311 /* not-currently-used entry */
312 return FAT_DENTRY_SKIP;
313 }
314 if (d->name[0] == FAT_DENTRY_UNUSED) {
315 /* never used entry */
316 return FAT_DENTRY_LAST;
317 }
318 if (d->name[0] == FAT_DENTRY_DOT) {
319 /*
320 * Most likely '.' or '..'.
321 * It cannot occur in a regular file name.
322 */
323 return FAT_DENTRY_SKIP;
324 }
325 return FAT_DENTRY_VALID;
326}
327
328static void fat_node_sync(fat_node_t *node)
329{
330 /* TODO */
331}
332
333/** Internal version of fat_node_get().
334 *
335 * @param idxp Locked index structure.
336 */
337static void *fat_node_get_core(fat_idx_t *idxp)
338{
339 block_t *b;
340 fat_dentry_t *d;
341 fat_node_t *nodep = NULL;
342 unsigned bps;
343 unsigned dps;
344
345 if (idxp->nodep) {
346 /*
347 * We are lucky.
348 * The node is already instantiated in memory.
349 */
350 futex_down(&idxp->nodep->lock);
351 if (!idxp->nodep->refcnt++)
352 list_remove(&idxp->nodep->ffn_link);
353 futex_up(&idxp->nodep->lock);
354 return idxp->nodep;
355 }
356
357 /*
358 * We must instantiate the node from the file system.
359 */
360
361 assert(idxp->pfc);
362
363 futex_down(&ffn_futex);
364 if (!list_empty(&ffn_head)) {
365 /* Try to use a cached free node structure. */
366 fat_idx_t *idxp_tmp;
367 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
368 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
369 goto skip_cache;
370 idxp_tmp = nodep->idx;
371 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
372 futex_up(&nodep->lock);
373 goto skip_cache;
374 }
375 list_remove(&nodep->ffn_link);
376 futex_up(&ffn_futex);
377 if (nodep->dirty)
378 fat_node_sync(nodep);
379 idxp_tmp->nodep = NULL;
380 futex_up(&nodep->lock);
381 futex_up(&idxp_tmp->lock);
382 } else {
383skip_cache:
384 /* Try to allocate a new node structure. */
385 futex_up(&ffn_futex);
386 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
387 if (!nodep)
388 return NULL;
389 }
390 fat_node_initialize(nodep);
391
392 bps = fat_bps_get(idxp->dev_handle);
393 dps = bps / sizeof(fat_dentry_t);
394
395 /* Read the block that contains the dentry of interest. */
396 b = _fat_block_get(idxp->dev_handle, idxp->pfc,
397 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
398 assert(b);
399
400 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
401 if (d->attr & FAT_ATTR_SUBDIR) {
402 /*
403 * The only directory which does not have this bit set is the
404 * root directory itself. The root directory node is handled
405 * and initialized elsewhere.
406 */
407 nodep->type = FAT_DIRECTORY;
408 /*
409 * Unfortunately, the 'size' field of the FAT dentry is not
410 * defined for the directory entry type. We must determine the
411 * size of the directory by walking the FAT.
412 */
413 nodep->size = bps * _fat_blcks_get(idxp->dev_handle,
414 uint16_t_le2host(d->firstc));
415 } else {
416 nodep->type = FAT_FILE;
417 nodep->size = uint32_t_le2host(d->size);
418 }
419 nodep->firstc = uint16_t_le2host(d->firstc);
420 nodep->lnkcnt = 1;
421 nodep->refcnt = 1;
422
423 block_put(b);
424
425 /* Link the idx structure with the node structure. */
426 nodep->idx = idxp;
427 idxp->nodep = nodep;
428
429 return nodep;
430}
431
432/** Instantiate a FAT in-core node. */
433static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
434{
435 void *node;
436 fat_idx_t *idxp;
437
438 idxp = fat_idx_get_by_index(dev_handle, index);
439 if (!idxp)
440 return NULL;
441 /* idxp->lock held */
442 node = fat_node_get_core(idxp);
443 futex_up(&idxp->lock);
444 return node;
445}
446
447static void fat_node_put(void *node)
448{
449 fat_node_t *nodep = (fat_node_t *)node;
450
451 futex_down(&nodep->lock);
452 if (!--nodep->refcnt) {
453 futex_down(&ffn_futex);
454 list_append(&nodep->ffn_link, &ffn_head);
455 futex_up(&ffn_futex);
456 }
457 futex_up(&nodep->lock);
458}
459
460static void *fat_create(int flags)
461{
462 return NULL; /* not supported at the moment */
463}
464
465static int fat_destroy(void *node)
466{
467 return ENOTSUP; /* not supported at the moment */
468}
469
470static bool fat_link(void *prnt, void *chld, const char *name)
471{
472 return false; /* not supported at the moment */
473}
474
475static int fat_unlink(void *prnt, void *chld)
476{
477 return ENOTSUP; /* not supported at the moment */
478}
479
480static void *fat_match(void *prnt, const char *component)
481{
482 fat_node_t *parentp = (fat_node_t *)prnt;
483 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
484 unsigned i, j;
485 unsigned bps; /* bytes per sector */
486 unsigned dps; /* dentries per sector */
487 unsigned blocks;
488 fat_dentry_t *d;
489 block_t *b;
490
491 futex_down(&parentp->idx->lock);
492 bps = fat_bps_get(parentp->idx->dev_handle);
493 dps = bps / sizeof(fat_dentry_t);
494 blocks = parentp->size / bps + (parentp->size % bps != 0);
495 for (i = 0; i < blocks; i++) {
496 unsigned dentries;
497
498 b = fat_block_get(parentp, i);
499 dentries = (i == blocks - 1) ?
500 parentp->size % sizeof(fat_dentry_t) :
501 dps;
502 for (j = 0; j < dentries; j++) {
503 d = ((fat_dentry_t *)b->data) + j;
504 switch (fat_classify_dentry(d)) {
505 case FAT_DENTRY_SKIP:
506 continue;
507 case FAT_DENTRY_LAST:
508 block_put(b);
509 futex_up(&parentp->idx->lock);
510 return NULL;
511 default:
512 case FAT_DENTRY_VALID:
513 dentry_name_canonify(d, name);
514 break;
515 }
516 if (stricmp(name, component) == 0) {
517 /* hit */
518 void *node;
519 /*
520 * Assume tree hierarchy for locking. We
521 * already have the parent and now we are going
522 * to lock the child. Never lock in the oposite
523 * order.
524 */
525 fat_idx_t *idx = fat_idx_get_by_pos(
526 parentp->idx->dev_handle, parentp->firstc,
527 i * dps + j);
528 futex_up(&parentp->idx->lock);
529 if (!idx) {
530 /*
531 * Can happen if memory is low or if we
532 * run out of 32-bit indices.
533 */
534 block_put(b);
535 return NULL;
536 }
537 node = fat_node_get_core(idx);
538 futex_up(&idx->lock);
539 block_put(b);
540 return node;
541 }
542 }
543 block_put(b);
544 }
545 futex_up(&parentp->idx->lock);
546 return NULL;
547}
548
549static fs_index_t fat_index_get(void *node)
550{
551 fat_node_t *fnodep = (fat_node_t *)node;
552 if (!fnodep)
553 return 0;
554 return fnodep->idx->index;
555}
556
557static size_t fat_size_get(void *node)
558{
559 return ((fat_node_t *)node)->size;
560}
561
562static unsigned fat_lnkcnt_get(void *node)
563{
564 return ((fat_node_t *)node)->lnkcnt;
565}
566
567static bool fat_has_children(void *node)
568{
569 fat_node_t *nodep = (fat_node_t *)node;
570 unsigned bps;
571 unsigned dps;
572 unsigned blocks;
573 block_t *b;
574 unsigned i, j;
575
576 if (nodep->type != FAT_DIRECTORY)
577 return false;
578
579 futex_down(&nodep->idx->lock);
580 bps = fat_bps_get(nodep->idx->dev_handle);
581 dps = bps / sizeof(fat_dentry_t);
582
583 blocks = nodep->size / bps + (nodep->size % bps != 0);
584
585 for (i = 0; i < blocks; i++) {
586 unsigned dentries;
587 fat_dentry_t *d;
588
589 b = fat_block_get(nodep, i);
590 dentries = (i == blocks - 1) ?
591 nodep->size % sizeof(fat_dentry_t) :
592 dps;
593 for (j = 0; j < dentries; j++) {
594 d = ((fat_dentry_t *)b->data) + j;
595 switch (fat_classify_dentry(d)) {
596 case FAT_DENTRY_SKIP:
597 continue;
598 case FAT_DENTRY_LAST:
599 block_put(b);
600 futex_up(&nodep->idx->lock);
601 return false;
602 default:
603 case FAT_DENTRY_VALID:
604 block_put(b);
605 futex_up(&nodep->idx->lock);
606 return true;
607 }
608 block_put(b);
609 futex_up(&nodep->idx->lock);
610 return true;
611 }
612 block_put(b);
613 }
614
615 futex_up(&nodep->idx->lock);
616 return false;
617}
618
619static void *fat_root_get(dev_handle_t dev_handle)
620{
621 return fat_node_get(dev_handle, 0);
622}
623
624static char fat_plb_get_char(unsigned pos)
625{
626 return fat_reg.plb_ro[pos % PLB_SIZE];
627}
628
629static bool fat_is_directory(void *node)
630{
631 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
632}
633
634static bool fat_is_file(void *node)
635{
636 return ((fat_node_t *)node)->type == FAT_FILE;
637}
638
639/** libfs operations */
640libfs_ops_t fat_libfs_ops = {
641 .match = fat_match,
642 .node_get = fat_node_get,
643 .node_put = fat_node_put,
644 .create = fat_create,
645 .destroy = fat_destroy,
646 .link = fat_link,
647 .unlink = fat_unlink,
648 .index_get = fat_index_get,
649 .size_get = fat_size_get,
650 .lnkcnt_get = fat_lnkcnt_get,
651 .has_children = fat_has_children,
652 .root_get = fat_root_get,
653 .plb_get_char = fat_plb_get_char,
654 .is_directory = fat_is_directory,
655 .is_file = fat_is_file
656};
657
658void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
659{
660 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
661 block_t *bb;
662 uint16_t bps;
663 uint16_t rde;
664 int rc;
665
666 /*
667 * For now, we don't bother to remember dev_handle, dev_phone or
668 * dev_buffer in some data structure. We use global variables because we
669 * know there will be at most one mount on this file system.
670 * Of course, this is a huge TODO item.
671 */
672 dev_buffer = mmap(NULL, BS_SIZE, PROTO_READ | PROTO_WRITE,
673 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
674
675 if (!dev_buffer) {
676 ipc_answer_0(rid, ENOMEM);
677 return;
678 }
679
680 dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
681 DEVMAP_CONNECT_TO_DEVICE, dev_handle);
682
683 if (dev_phone < 0) {
684 munmap(dev_buffer, BS_SIZE);
685 ipc_answer_0(rid, dev_phone);
686 return;
687 }
688
689 rc = ipc_share_out_start(dev_phone, dev_buffer,
690 AS_AREA_READ | AS_AREA_WRITE);
691 if (rc != EOK) {
692 munmap(dev_buffer, BS_SIZE);
693 ipc_answer_0(rid, rc);
694 return;
695 }
696
697 /* Read the number of root directory entries. */
698 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
699 bps = uint16_t_le2host(FAT_BS(bb)->bps);
700 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
701 block_put(bb);
702
703 if (bps != BS_SIZE) {
704 munmap(dev_buffer, BS_SIZE);
705 ipc_answer_0(rid, ENOTSUP);
706 return;
707 }
708
709 rc = fat_idx_init_by_dev_handle(dev_handle);
710 if (rc != EOK) {
711 munmap(dev_buffer, BS_SIZE);
712 ipc_answer_0(rid, rc);
713 return;
714 }
715
716 /* Initialize the root node. */
717 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
718 if (!rootp) {
719 munmap(dev_buffer, BS_SIZE);
720 fat_idx_fini_by_dev_handle(dev_handle);
721 ipc_answer_0(rid, ENOMEM);
722 return;
723 }
724 fat_node_initialize(rootp);
725
726 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
727 if (!ridxp) {
728 munmap(dev_buffer, BS_SIZE);
729 free(rootp);
730 fat_idx_fini_by_dev_handle(dev_handle);
731 ipc_answer_0(rid, ENOMEM);
732 return;
733 }
734 assert(ridxp->index == 0);
735 /* ridxp->lock held */
736
737 rootp->type = FAT_DIRECTORY;
738 rootp->firstc = FAT_CLST_ROOT;
739 rootp->refcnt = 1;
740 rootp->size = rde * sizeof(fat_dentry_t);
741 rootp->idx = ridxp;
742 ridxp->nodep = rootp;
743
744 futex_up(&ridxp->lock);
745
746 ipc_answer_0(rid, EOK);
747}
748
749void fat_mount(ipc_callid_t rid, ipc_call_t *request)
750{
751 ipc_answer_0(rid, ENOTSUP);
752}
753
754void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
755{
756 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
757}
758
759void fat_read(ipc_callid_t rid, ipc_call_t *request)
760{
761 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
762 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
763 off_t pos = (off_t)IPC_GET_ARG3(*request);
764 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
765 uint16_t bps = fat_bps_get(dev_handle);
766 size_t bytes;
767 block_t *b;
768
769 if (!nodep) {
770 ipc_answer_0(rid, ENOENT);
771 return;
772 }
773
774 ipc_callid_t callid;
775 size_t len;
776 if (!ipc_data_read_receive(&callid, &len)) {
777 fat_node_put(nodep);
778 ipc_answer_0(callid, EINVAL);
779 ipc_answer_0(rid, EINVAL);
780 return;
781 }
782
783 if (nodep->type == FAT_FILE) {
784 /*
785 * Our strategy for regular file reads is to read one block at
786 * most and make use of the possibility to return less data than
787 * requested. This keeps the code very simple.
788 */
789 bytes = min(len, bps - pos % bps);
790 b = fat_block_get(nodep, pos / bps);
791 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
792 bytes);
793 block_put(b);
794 } else {
795 unsigned bnum;
796 off_t spos = pos;
797 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
798 fat_dentry_t *d;
799
800 assert(nodep->type == FAT_DIRECTORY);
801 assert(nodep->size % bps == 0);
802 assert(bps % sizeof(fat_dentry_t) == 0);
803
804 /*
805 * Our strategy for readdir() is to use the position pointer as
806 * an index into the array of all dentries. On entry, it points
807 * to the first unread dentry. If we skip any dentries, we bump
808 * the position pointer accordingly.
809 */
810 bnum = (pos * sizeof(fat_dentry_t)) / bps;
811 while (bnum < nodep->size / bps) {
812 off_t o;
813
814 b = fat_block_get(nodep, bnum);
815 for (o = pos % (bps / sizeof(fat_dentry_t));
816 o < bps / sizeof(fat_dentry_t);
817 o++, pos++) {
818 d = ((fat_dentry_t *)b->data) + o;
819 switch (fat_classify_dentry(d)) {
820 case FAT_DENTRY_SKIP:
821 continue;
822 case FAT_DENTRY_LAST:
823 block_put(b);
824 goto miss;
825 default:
826 case FAT_DENTRY_VALID:
827 dentry_name_canonify(d, name);
828 block_put(b);
829 goto hit;
830 }
831 }
832 block_put(b);
833 bnum++;
834 }
835miss:
836 fat_node_put(nodep);
837 ipc_answer_0(callid, ENOENT);
838 ipc_answer_1(rid, ENOENT, 0);
839 return;
840hit:
841 (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
842 bytes = (pos - spos) + 1;
843 }
844
845 fat_node_put(nodep);
846 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
847}
848
849/**
850 * @}
851 */
Note: See TracBrowser for help on using the repository browser.