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

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

More complete prototype of fat_write().

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