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

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

First attempt at delivering the FAT16 read-only support.
Needless to say it doesn't work yet, but now it's only
the lurking bugs that need to be fixed.

  • Property mode set to 100644
File size: 16.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 "../../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
73static void dentry_name_canonify(fat_dentry_t *d, char *buf)
74{
75 int i;
76
77 for (i = 0; i < FAT_NAME_LEN; i++) {
78 if (d->name[i] == FAT_PAD) {
79 buf++;
80 break;
81 }
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}
100
101static int dev_phone = -1; /* FIXME */
102static void *dev_buffer = NULL; /* FIXME */
103
104/* TODO move somewhere else */
105typedef struct {
106 void *data;
107 size_t size;
108} block_t;
109
110static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
111{
112 /* FIXME */
113 block_t *b;
114 off_t bufpos = 0;
115 size_t buflen = 0;
116
117 assert(dev_phone != -1);
118 assert(dev_buffer);
119
120 b = malloc(sizeof(block_t));
121 if (!b)
122 return NULL;
123
124 b->data = malloc(bs);
125 if (!b->data) {
126 free(b);
127 return NULL;
128 }
129 b->size = bs;
130
131 if (!libfs_blockread(dev_phone, dev_buffer, &bufpos, &buflen, &offset,
132 b->data, bs, bs)) {
133 free(b->data);
134 free(b);
135 return NULL;
136 }
137
138 return b;
139}
140
141static void block_put(block_t *block)
142{
143 /* FIXME */
144 free(block->data);
145 free(block);
146}
147
148#define FAT_BS(b) ((fat_bs_t *)((b)->data))
149
150#define FAT_CLST_RES0 0x0000
151#define FAT_CLST_RES1 0x0001
152#define FAT_CLST_FIRST 0x0002
153#define FAT_CLST_BAD 0xfff7
154#define FAT_CLST_LAST1 0xfff8
155#define FAT_CLST_LAST8 0xffff
156
157/* internally used to mark root directory's parent */
158#define FAT_CLST_ROOTPAR FAT_CLST_RES0
159/* internally used to mark root directory */
160#define FAT_CLST_ROOT FAT_CLST_RES1
161
162#define fat_block_get(np, off) \
163 _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
164
165static block_t *
166_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
167{
168 block_t *bb;
169 block_t *b;
170 unsigned bps;
171 unsigned spc;
172 unsigned rscnt; /* block address of the first FAT */
173 unsigned fatcnt;
174 unsigned rde;
175 unsigned rds; /* root directory size */
176 unsigned sf;
177 unsigned ssa; /* size of the system area */
178 unsigned clusters;
179 fat_cluster_t clst = firstc;
180 unsigned i;
181
182 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
183 bps = uint16_t_le2host(FAT_BS(bb)->bps);
184 spc = FAT_BS(bb)->spc;
185 rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
186 fatcnt = FAT_BS(bb)->fatcnt;
187 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
188 sf = uint16_t_le2host(FAT_BS(bb)->sec_per_fat);
189 block_put(bb);
190
191 rds = (sizeof(fat_dentry_t) * rde) / bps;
192 rds += ((sizeof(fat_dentry_t) * rde) % bps != 0);
193 ssa = rscnt + fatcnt * sf + rds;
194
195 if (firstc == FAT_CLST_ROOT) {
196 /* root directory special case */
197 assert(offset < rds);
198 b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
199 return b;
200 }
201
202 clusters = offset / spc;
203 for (i = 0; i < clusters; i++) {
204 unsigned fsec; /* sector offset relative to FAT1 */
205 unsigned fidx; /* FAT1 entry index */
206
207 assert(clst >= FAT_CLST_FIRST && clst < FAT_CLST_BAD);
208 fsec = (clst * sizeof(fat_cluster_t)) / bps;
209 fidx = clst % (bps / sizeof(fat_cluster_t));
210 /* read FAT1 */
211 b = block_get(dev_handle, rscnt + fsec, bps);
212 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
213 assert(clst != FAT_CLST_BAD);
214 assert(clst < FAT_CLST_LAST1);
215 block_put(b);
216 }
217
218 b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
219 offset % spc, bps);
220
221 return b;
222}
223
224static void fat_node_initialize(fat_node_t *node)
225{
226 futex_initialize(&node->lock, 1);
227 node->idx = NULL;
228 node->type = 0;
229 link_initialize(&node->ffn_link);
230 node->size = 0;
231 node->lnkcnt = 0;
232 node->refcnt = 0;
233 node->dirty = false;
234}
235
236static uint16_t fat_bps_get(dev_handle_t dev_handle)
237{
238 block_t *bb;
239 uint16_t bps;
240
241 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
242 assert(bb != NULL);
243 bps = uint16_t_le2host(FAT_BS(bb)->bps);
244 block_put(bb);
245
246 return bps;
247}
248
249typedef enum {
250 FAT_DENTRY_SKIP,
251 FAT_DENTRY_LAST,
252 FAT_DENTRY_VALID
253} fat_dentry_clsf_t;
254
255static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
256{
257 if (d->attr & FAT_ATTR_VOLLABEL) {
258 /* volume label entry */
259 return FAT_DENTRY_SKIP;
260 }
261 if (d->name[0] == FAT_DENTRY_ERASED) {
262 /* not-currently-used entry */
263 return FAT_DENTRY_SKIP;
264 }
265 if (d->name[0] == FAT_DENTRY_UNUSED) {
266 /* never used entry */
267 return FAT_DENTRY_LAST;
268 }
269 if (d->name[0] == FAT_DENTRY_DOT) {
270 /*
271 * Most likely '.' or '..'.
272 * It cannot occur in a regular file name.
273 */
274 return FAT_DENTRY_SKIP;
275 }
276 return FAT_DENTRY_VALID;
277}
278
279static void fat_node_sync(fat_node_t *node)
280{
281 /* TODO */
282}
283
284/** Internal version of fat_node_get().
285 *
286 * @param idxp Locked index structure.
287 */
288static void *fat_node_get_core(fat_idx_t *idxp)
289{
290 block_t *b;
291 fat_dentry_t *d;
292 fat_node_t *nodep;
293 unsigned bps;
294 unsigned dps;
295
296 if (idxp->nodep) {
297 /*
298 * We are lucky.
299 * The node is already instantiated in memory.
300 */
301 futex_down(&idxp->nodep->lock);
302 if (!idxp->nodep->refcnt++)
303 list_remove(&nodep->ffn_link);
304 futex_up(&idxp->nodep->lock);
305 return idxp->nodep;
306 }
307
308 /*
309 * We must instantiate the node from the file system.
310 */
311
312 assert(idxp->pfc);
313
314 futex_down(&ffn_futex);
315 if (!list_empty(&ffn_head)) {
316 /* Try to use a cached free node structure. */
317 fat_idx_t *idxp_tmp;
318 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
319 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
320 goto skip_cache;
321 idxp_tmp = nodep->idx;
322 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
323 futex_up(&nodep->lock);
324 goto skip_cache;
325 }
326 list_remove(&nodep->ffn_link);
327 futex_up(&ffn_futex);
328 if (nodep->dirty)
329 fat_node_sync(nodep);
330 idxp_tmp->nodep = NULL;
331 futex_up(&nodep->lock);
332 futex_up(&idxp_tmp->lock);
333 } else {
334skip_cache:
335 /* Try to allocate a new node structure. */
336 futex_up(&ffn_futex);
337 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
338 if (!nodep)
339 return NULL;
340 }
341 fat_node_initialize(nodep);
342
343 bps = fat_bps_get(idxp->dev_handle);
344 dps = bps / sizeof(fat_dentry_t);
345
346 /* Read the block that contains the dentry of interest. */
347 b = _fat_block_get(idxp->dev_handle, idxp->pfc,
348 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
349 assert(b);
350
351 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
352 if (d->attr & FAT_ATTR_SUBDIR) {
353 /*
354 * The only directory which does not have this bit set is the
355 * root directory itself. The root directory node is handled
356 * and initialized elsewhere.
357 */
358 nodep->type = FAT_DIRECTORY;
359 } else {
360 nodep->type = FAT_FILE;
361 }
362 nodep->firstc = uint16_t_le2host(d->firstc);
363 nodep->size = uint32_t_le2host(d->size);
364 nodep->lnkcnt = 1;
365 nodep->refcnt = 1;
366
367 block_put(b);
368
369 /* Link the idx structure with the node structure. */
370 nodep->idx = idxp;
371 idxp->nodep = nodep;
372
373 return nodep;
374}
375
376/** Instantiate a FAT in-core node. */
377static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
378{
379 void *node;
380 fat_idx_t *idxp;
381
382 idxp = fat_idx_get_by_index(dev_handle, index);
383 if (!idxp)
384 return NULL;
385 /* idxp->lock held */
386 node = fat_node_get_core(idxp);
387 futex_up(&idxp->lock);
388 return node;
389}
390
391static void fat_node_put(void *node)
392{
393 fat_node_t *nodep = (fat_node_t *)node;
394
395 futex_down(&nodep->lock);
396 if (!--nodep->refcnt) {
397 futex_down(&ffn_futex);
398 list_append(&nodep->ffn_link, &ffn_head);
399 futex_up(&ffn_futex);
400 }
401 futex_up(&nodep->lock);
402}
403
404static void *fat_create(int flags)
405{
406 return NULL; /* not supported at the moment */
407}
408
409static int fat_destroy(void *node)
410{
411 return ENOTSUP; /* not supported at the moment */
412}
413
414static bool fat_link(void *prnt, void *chld, const char *name)
415{
416 return false; /* not supported at the moment */
417}
418
419static int fat_unlink(void *prnt, void *chld)
420{
421 return ENOTSUP; /* not supported at the moment */
422}
423
424static void *fat_match(void *prnt, const char *component)
425{
426 fat_node_t *parentp = (fat_node_t *)prnt;
427 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
428 unsigned i, j;
429 unsigned bps; /* bytes per sector */
430 unsigned dps; /* dentries per sector */
431 unsigned blocks;
432 fat_dentry_t *d;
433 block_t *b;
434
435 futex_down(&parentp->idx->lock);
436 bps = fat_bps_get(parentp->idx->dev_handle);
437 dps = bps / sizeof(fat_dentry_t);
438 blocks = parentp->size / bps + (parentp->size % bps != 0);
439 for (i = 0; i < blocks; i++) {
440 unsigned dentries;
441
442 b = fat_block_get(parentp, i);
443 dentries = (i == blocks - 1) ?
444 parentp->size % sizeof(fat_dentry_t) :
445 dps;
446 for (j = 0; j < dentries; j++) {
447 d = ((fat_dentry_t *)b->data) + j;
448 switch (fat_classify_dentry(d)) {
449 case FAT_DENTRY_SKIP:
450 continue;
451 case FAT_DENTRY_LAST:
452 block_put(b);
453 futex_up(&parentp->idx->lock);
454 return NULL;
455 default:
456 case FAT_DENTRY_VALID:
457 dentry_name_canonify(d, name);
458 break;
459 }
460 if (strcmp(name, component) == 0) {
461 /* hit */
462 void *node;
463 /*
464 * Assume tree hierarchy for locking. We
465 * already have the parent and now we are going
466 * to lock the child. Never lock in the oposite
467 * order.
468 */
469 fat_idx_t *idx = fat_idx_get_by_pos(
470 parentp->idx->dev_handle, parentp->firstc,
471 i * dps + j);
472 futex_up(&parentp->idx->lock);
473 if (!idx) {
474 /*
475 * Can happen if memory is low or if we
476 * run out of 32-bit indices.
477 */
478 block_put(b);
479 return NULL;
480 }
481 node = fat_node_get_core(idx);
482 futex_up(&idx->lock);
483 block_put(b);
484 return node;
485 }
486 }
487 block_put(b);
488 }
489 futex_up(&parentp->idx->lock);
490 return NULL;
491}
492
493static fs_index_t fat_index_get(void *node)
494{
495 fat_node_t *fnodep = (fat_node_t *)node;
496 if (!fnodep)
497 return 0;
498 return fnodep->idx->index;
499}
500
501static size_t fat_size_get(void *node)
502{
503 return ((fat_node_t *)node)->size;
504}
505
506static unsigned fat_lnkcnt_get(void *node)
507{
508 return ((fat_node_t *)node)->lnkcnt;
509}
510
511static bool fat_has_children(void *node)
512{
513 fat_node_t *nodep = (fat_node_t *)node;
514 unsigned bps;
515 unsigned dps;
516 unsigned blocks;
517 block_t *b;
518 unsigned i, j;
519
520 if (nodep->type != FAT_DIRECTORY)
521 return false;
522
523 futex_down(&nodep->idx->lock);
524 bps = fat_bps_get(nodep->idx->dev_handle);
525 dps = bps / sizeof(fat_dentry_t);
526
527 blocks = nodep->size / bps + (nodep->size % bps != 0);
528
529 for (i = 0; i < blocks; i++) {
530 unsigned dentries;
531 fat_dentry_t *d;
532
533 b = fat_block_get(nodep, i);
534 dentries = (i == blocks - 1) ?
535 nodep->size % sizeof(fat_dentry_t) :
536 dps;
537 for (j = 0; j < dentries; j++) {
538 d = ((fat_dentry_t *)b->data) + j;
539 switch (fat_classify_dentry(d)) {
540 case FAT_DENTRY_SKIP:
541 continue;
542 case FAT_DENTRY_LAST:
543 block_put(b);
544 futex_up(&nodep->idx->lock);
545 return false;
546 default:
547 case FAT_DENTRY_VALID:
548 block_put(b);
549 futex_up(&nodep->idx->lock);
550 return true;
551 }
552 block_put(b);
553 futex_up(&nodep->idx->lock);
554 return true;
555 }
556 block_put(b);
557 }
558
559 futex_up(&nodep->idx->lock);
560 return false;
561}
562
563static void *fat_root_get(dev_handle_t dev_handle)
564{
565 return fat_node_get(dev_handle, 0);
566}
567
568static char fat_plb_get_char(unsigned pos)
569{
570 return fat_reg.plb_ro[pos % PLB_SIZE];
571}
572
573static bool fat_is_directory(void *node)
574{
575 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
576}
577
578static bool fat_is_file(void *node)
579{
580 return ((fat_node_t *)node)->type == FAT_FILE;
581}
582
583/** libfs operations */
584libfs_ops_t fat_libfs_ops = {
585 .match = fat_match,
586 .node_get = fat_node_get,
587 .node_put = fat_node_put,
588 .create = fat_create,
589 .destroy = fat_destroy,
590 .link = fat_link,
591 .unlink = fat_unlink,
592 .index_get = fat_index_get,
593 .size_get = fat_size_get,
594 .lnkcnt_get = fat_lnkcnt_get,
595 .has_children = fat_has_children,
596 .root_get = fat_root_get,
597 .plb_get_char = fat_plb_get_char,
598 .is_directory = fat_is_directory,
599 .is_file = fat_is_file
600};
601
602void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
603{
604 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
605 block_t *bb;
606 uint16_t bps;
607 uint16_t rde;
608 int rc;
609
610 /*
611 * For now, we don't bother to remember dev_handle, dev_phone or
612 * dev_buffer in some data structure. We use global variables because we
613 * know there will be at most one mount on this file system.
614 * Of course, this is a huge TODO item.
615 */
616 dev_buffer = mmap(NULL, BS_SIZE, PROTO_READ | PROTO_WRITE,
617 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
618
619 if (!dev_buffer) {
620 ipc_answer_0(rid, ENOMEM);
621 return;
622 }
623
624 dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
625 DEVMAP_CONNECT_TO_DEVICE, dev_handle);
626
627 if (dev_phone < 0) {
628 munmap(dev_buffer, BS_SIZE);
629 ipc_answer_0(rid, dev_phone);
630 return;
631 }
632
633 rc = ipc_share_out_start(dev_phone, dev_buffer,
634 AS_AREA_READ | AS_AREA_WRITE);
635 if (rc != EOK) {
636 munmap(dev_buffer, BS_SIZE);
637 ipc_answer_0(rid, rc);
638 return;
639 }
640
641 /* Read the number of root directory entries. */
642 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
643 bps = uint16_t_le2host(FAT_BS(bb)->bps);
644 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
645 block_put(bb);
646
647 if (bps != BS_SIZE) {
648 munmap(dev_buffer, BS_SIZE);
649 ipc_answer_0(rid, ENOTSUP);
650 return;
651 }
652
653 rc = fat_idx_init_by_dev_handle(dev_handle);
654 if (rc != EOK) {
655 munmap(dev_buffer, BS_SIZE);
656 ipc_answer_0(rid, rc);
657 return;
658 }
659
660 /* Initialize the root node. */
661 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
662 if (!rootp) {
663 munmap(dev_buffer, BS_SIZE);
664 fat_idx_fini_by_dev_handle(dev_handle);
665 ipc_answer_0(rid, ENOMEM);
666 return;
667 }
668 fat_node_initialize(rootp);
669
670 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
671 if (!ridxp) {
672 munmap(dev_buffer, BS_SIZE);
673 free(rootp);
674 fat_idx_fini_by_dev_handle(dev_handle);
675 ipc_answer_0(rid, ENOMEM);
676 return;
677 }
678 assert(ridxp->index == 0);
679 /* ridxp->lock held */
680
681 rootp->type = FAT_DIRECTORY;
682 rootp->firstc = FAT_CLST_ROOT;
683 rootp->refcnt = 1;
684 rootp->size = rde * sizeof(fat_dentry_t);
685 rootp->idx = ridxp;
686 ridxp->nodep = rootp;
687
688 futex_up(&ridxp->lock);
689
690 ipc_answer_0(rid, EOK);
691}
692
693void fat_mount(ipc_callid_t rid, ipc_call_t *request)
694{
695 ipc_answer_0(rid, ENOTSUP);
696}
697
698void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
699{
700 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
701}
702
703/**
704 * @}
705 */
Note: See TracBrowser for help on using the repository browser.