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

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

Implementation of fat_alloc_clusters().

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