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

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

FAT dentry type used for subdirectories doesn't store the directory size in the 'size' field.
Introduce a simple workaround that will let us have 64 directory entries at maximum.

  • Property mode set to 100644
File size: 16.2 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 break;
80 if (d->name[i] == FAT_DENTRY_E5_ESC)
81 *buf++ = 0xe5;
82 else
83 *buf++ = d->name[i];
84 }
85 if (d->ext[0] != FAT_PAD)
86 *buf++ = '.';
87 for (i = 0; i < FAT_EXT_LEN; i++) {
88 if (d->ext[i] == FAT_PAD) {
89 *buf = '\0';
90 return;
91 }
92 if (d->ext[i] == FAT_DENTRY_E5_ESC)
93 *buf++ = 0xe5;
94 else
95 *buf++ = d->ext[i];
96 }
97 *buf = '\0';
98}
99
100static int dev_phone = -1; /* FIXME */
101static void *dev_buffer = NULL; /* FIXME */
102
103/* TODO move somewhere else */
104typedef struct {
105 void *data;
106 size_t size;
107} block_t;
108
109static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
110{
111 /* FIXME */
112 block_t *b;
113 off_t bufpos = 0;
114 size_t buflen = 0;
115 off_t pos = offset * bs;
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, &pos,
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 /*
360 * TODO: determine the size by walking FAT. Surprisingly, the
361 * 'size' filed of the FAT dentry is not defined for the
362 * directory entry type.
363 */
364 nodep->size = 64 * sizeof(fat_dentry_t);
365 } else {
366 nodep->type = FAT_FILE;
367 nodep->size = uint32_t_le2host(d->size);
368 }
369 nodep->firstc = uint16_t_le2host(d->firstc);
370 nodep->lnkcnt = 1;
371 nodep->refcnt = 1;
372
373 block_put(b);
374
375 /* Link the idx structure with the node structure. */
376 nodep->idx = idxp;
377 idxp->nodep = nodep;
378
379 return nodep;
380}
381
382/** Instantiate a FAT in-core node. */
383static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
384{
385 void *node;
386 fat_idx_t *idxp;
387
388 idxp = fat_idx_get_by_index(dev_handle, index);
389 if (!idxp)
390 return NULL;
391 /* idxp->lock held */
392 node = fat_node_get_core(idxp);
393 futex_up(&idxp->lock);
394 return node;
395}
396
397static void fat_node_put(void *node)
398{
399 fat_node_t *nodep = (fat_node_t *)node;
400
401 futex_down(&nodep->lock);
402 if (!--nodep->refcnt) {
403 futex_down(&ffn_futex);
404 list_append(&nodep->ffn_link, &ffn_head);
405 futex_up(&ffn_futex);
406 }
407 futex_up(&nodep->lock);
408}
409
410static void *fat_create(int flags)
411{
412 return NULL; /* not supported at the moment */
413}
414
415static int fat_destroy(void *node)
416{
417 return ENOTSUP; /* not supported at the moment */
418}
419
420static bool fat_link(void *prnt, void *chld, const char *name)
421{
422 return false; /* not supported at the moment */
423}
424
425static int fat_unlink(void *prnt, void *chld)
426{
427 return ENOTSUP; /* not supported at the moment */
428}
429
430static void *fat_match(void *prnt, const char *component)
431{
432 fat_node_t *parentp = (fat_node_t *)prnt;
433 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
434 unsigned i, j;
435 unsigned bps; /* bytes per sector */
436 unsigned dps; /* dentries per sector */
437 unsigned blocks;
438 fat_dentry_t *d;
439 block_t *b;
440
441 futex_down(&parentp->idx->lock);
442 bps = fat_bps_get(parentp->idx->dev_handle);
443 dps = bps / sizeof(fat_dentry_t);
444 blocks = parentp->size / bps + (parentp->size % bps != 0);
445 for (i = 0; i < blocks; i++) {
446 unsigned dentries;
447
448 b = fat_block_get(parentp, i);
449 dentries = (i == blocks - 1) ?
450 parentp->size % sizeof(fat_dentry_t) :
451 dps;
452 for (j = 0; j < dentries; j++) {
453 d = ((fat_dentry_t *)b->data) + j;
454 switch (fat_classify_dentry(d)) {
455 case FAT_DENTRY_SKIP:
456 continue;
457 case FAT_DENTRY_LAST:
458 block_put(b);
459 futex_up(&parentp->idx->lock);
460 return NULL;
461 default:
462 case FAT_DENTRY_VALID:
463 dentry_name_canonify(d, name);
464 break;
465 }
466 if (stricmp(name, component) == 0) {
467 /* hit */
468 void *node;
469 /*
470 * Assume tree hierarchy for locking. We
471 * already have the parent and now we are going
472 * to lock the child. Never lock in the oposite
473 * order.
474 */
475 fat_idx_t *idx = fat_idx_get_by_pos(
476 parentp->idx->dev_handle, parentp->firstc,
477 i * dps + j);
478 futex_up(&parentp->idx->lock);
479 if (!idx) {
480 /*
481 * Can happen if memory is low or if we
482 * run out of 32-bit indices.
483 */
484 block_put(b);
485 return NULL;
486 }
487 node = fat_node_get_core(idx);
488 futex_up(&idx->lock);
489 block_put(b);
490 return node;
491 }
492 }
493 block_put(b);
494 }
495 futex_up(&parentp->idx->lock);
496 return NULL;
497}
498
499static fs_index_t fat_index_get(void *node)
500{
501 fat_node_t *fnodep = (fat_node_t *)node;
502 if (!fnodep)
503 return 0;
504 return fnodep->idx->index;
505}
506
507static size_t fat_size_get(void *node)
508{
509 return ((fat_node_t *)node)->size;
510}
511
512static unsigned fat_lnkcnt_get(void *node)
513{
514 return ((fat_node_t *)node)->lnkcnt;
515}
516
517static bool fat_has_children(void *node)
518{
519 fat_node_t *nodep = (fat_node_t *)node;
520 unsigned bps;
521 unsigned dps;
522 unsigned blocks;
523 block_t *b;
524 unsigned i, j;
525
526 if (nodep->type != FAT_DIRECTORY)
527 return false;
528
529 futex_down(&nodep->idx->lock);
530 bps = fat_bps_get(nodep->idx->dev_handle);
531 dps = bps / sizeof(fat_dentry_t);
532
533 blocks = nodep->size / bps + (nodep->size % bps != 0);
534
535 for (i = 0; i < blocks; i++) {
536 unsigned dentries;
537 fat_dentry_t *d;
538
539 b = fat_block_get(nodep, i);
540 dentries = (i == blocks - 1) ?
541 nodep->size % sizeof(fat_dentry_t) :
542 dps;
543 for (j = 0; j < dentries; j++) {
544 d = ((fat_dentry_t *)b->data) + j;
545 switch (fat_classify_dentry(d)) {
546 case FAT_DENTRY_SKIP:
547 continue;
548 case FAT_DENTRY_LAST:
549 block_put(b);
550 futex_up(&nodep->idx->lock);
551 return false;
552 default:
553 case FAT_DENTRY_VALID:
554 block_put(b);
555 futex_up(&nodep->idx->lock);
556 return true;
557 }
558 block_put(b);
559 futex_up(&nodep->idx->lock);
560 return true;
561 }
562 block_put(b);
563 }
564
565 futex_up(&nodep->idx->lock);
566 return false;
567}
568
569static void *fat_root_get(dev_handle_t dev_handle)
570{
571 return fat_node_get(dev_handle, 0);
572}
573
574static char fat_plb_get_char(unsigned pos)
575{
576 return fat_reg.plb_ro[pos % PLB_SIZE];
577}
578
579static bool fat_is_directory(void *node)
580{
581 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
582}
583
584static bool fat_is_file(void *node)
585{
586 return ((fat_node_t *)node)->type == FAT_FILE;
587}
588
589/** libfs operations */
590libfs_ops_t fat_libfs_ops = {
591 .match = fat_match,
592 .node_get = fat_node_get,
593 .node_put = fat_node_put,
594 .create = fat_create,
595 .destroy = fat_destroy,
596 .link = fat_link,
597 .unlink = fat_unlink,
598 .index_get = fat_index_get,
599 .size_get = fat_size_get,
600 .lnkcnt_get = fat_lnkcnt_get,
601 .has_children = fat_has_children,
602 .root_get = fat_root_get,
603 .plb_get_char = fat_plb_get_char,
604 .is_directory = fat_is_directory,
605 .is_file = fat_is_file
606};
607
608void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
609{
610 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
611 block_t *bb;
612 uint16_t bps;
613 uint16_t rde;
614 int rc;
615
616 /*
617 * For now, we don't bother to remember dev_handle, dev_phone or
618 * dev_buffer in some data structure. We use global variables because we
619 * know there will be at most one mount on this file system.
620 * Of course, this is a huge TODO item.
621 */
622 dev_buffer = mmap(NULL, BS_SIZE, PROTO_READ | PROTO_WRITE,
623 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
624
625 if (!dev_buffer) {
626 ipc_answer_0(rid, ENOMEM);
627 return;
628 }
629
630 dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
631 DEVMAP_CONNECT_TO_DEVICE, dev_handle);
632
633 if (dev_phone < 0) {
634 munmap(dev_buffer, BS_SIZE);
635 ipc_answer_0(rid, dev_phone);
636 return;
637 }
638
639 rc = ipc_share_out_start(dev_phone, dev_buffer,
640 AS_AREA_READ | AS_AREA_WRITE);
641 if (rc != EOK) {
642 munmap(dev_buffer, BS_SIZE);
643 ipc_answer_0(rid, rc);
644 return;
645 }
646
647 /* Read the number of root directory entries. */
648 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
649 bps = uint16_t_le2host(FAT_BS(bb)->bps);
650 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
651 block_put(bb);
652
653 if (bps != BS_SIZE) {
654 munmap(dev_buffer, BS_SIZE);
655 ipc_answer_0(rid, ENOTSUP);
656 return;
657 }
658
659 rc = fat_idx_init_by_dev_handle(dev_handle);
660 if (rc != EOK) {
661 munmap(dev_buffer, BS_SIZE);
662 ipc_answer_0(rid, rc);
663 return;
664 }
665
666 /* Initialize the root node. */
667 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
668 if (!rootp) {
669 munmap(dev_buffer, BS_SIZE);
670 fat_idx_fini_by_dev_handle(dev_handle);
671 ipc_answer_0(rid, ENOMEM);
672 return;
673 }
674 fat_node_initialize(rootp);
675
676 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
677 if (!ridxp) {
678 munmap(dev_buffer, BS_SIZE);
679 free(rootp);
680 fat_idx_fini_by_dev_handle(dev_handle);
681 ipc_answer_0(rid, ENOMEM);
682 return;
683 }
684 assert(ridxp->index == 0);
685 /* ridxp->lock held */
686
687 rootp->type = FAT_DIRECTORY;
688 rootp->firstc = FAT_CLST_ROOT;
689 rootp->refcnt = 1;
690 rootp->size = rde * sizeof(fat_dentry_t);
691 rootp->idx = ridxp;
692 ridxp->nodep = rootp;
693
694 futex_up(&ridxp->lock);
695
696 ipc_answer_0(rid, EOK);
697}
698
699void fat_mount(ipc_callid_t rid, ipc_call_t *request)
700{
701 ipc_answer_0(rid, ENOTSUP);
702}
703
704void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
705{
706 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
707}
708
709/**
710 * @}
711 */
Note: See TracBrowser for help on using the repository browser.