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
RevLine 
[be815bc]1/*
[a2aa1dec]2 * Copyright (c) 2008 Jakub Jermar
[be815bc]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"
[6364d3c]39#include "../../vfs/vfs.h"
[a2aa1dec]40#include <libfs.h>
[be815bc]41#include <ipc/ipc.h>
[7a35204a]42#include <ipc/services.h>
43#include <ipc/devmap.h>
[be815bc]44#include <async.h>
45#include <errno.h>
[a2aa1dec]46#include <string.h>
[776f2e6]47#include <byteorder.h>
[e1e3b26]48#include <libadt/hash_table.h>
49#include <libadt/list.h>
50#include <assert.h>
[d9e9caf]51#include <futex.h>
[7a35204a]52#include <sys/mman.h>
[8d32152]53#include <align.h>
[e1e3b26]54
[e22632a9]55#define BS_BLOCK 0
[a61d1fc3]56#define BS_SIZE 512
[e22632a9]57
[add5835]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);
[6364d3c]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
[79d031b]74#define min(a, b) ((a) < (b) ? (a) : (b))
75
[a2aa1dec]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++) {
[d2e9c47]81 if (d->name[i] == FAT_PAD)
[a2aa1dec]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 }
[d2e9c47]100 *buf = '\0';
[a2aa1dec]101}
102
[7a35204a]103static int dev_phone = -1; /* FIXME */
104static void *dev_buffer = NULL; /* FIXME */
105
[34b3ce3]106/* TODO move somewhere else */
[79dbc3e]107typedef struct {
108 void *data;
[7a35204a]109 size_t size;
[8d32152]110 bool dirty;
[79dbc3e]111} block_t;
112
[a61d1fc3]113static block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs)
[a2aa1dec]114{
[7a35204a]115 /* FIXME */
116 block_t *b;
117 off_t bufpos = 0;
118 size_t buflen = 0;
[d2e9c47]119 off_t pos = offset * bs;
[7a35204a]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
[d2e9c47]135 if (!libfs_blockread(dev_phone, dev_buffer, &bufpos, &buflen, &pos,
[7a35204a]136 b->data, bs, bs)) {
137 free(b->data);
138 free(b);
139 return NULL;
140 }
141
142 return b;
[a2aa1dec]143}
144
[79dbc3e]145static void block_put(block_t *block)
[6364d3c]146{
[7a35204a]147 /* FIXME */
148 free(block->data);
149 free(block);
[a2aa1dec]150}
151
[9a5f0cb]152#define FAT_BS(b) ((fat_bs_t *)((b)->data))
153
[869e546]154#define FAT_CLST_RES0 0x0000
[689f036]155#define FAT_CLST_RES1 0x0001
[9a5f0cb]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
[689f036]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
[4573a79]166#define fat_block_get(np, off) \
167 _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
168
169static block_t *
[2c4bbcde]170_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
[9a5f0cb]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;
[2c4bbcde]183 fat_cluster_t clst = firstc;
[9a5f0cb]184 unsigned i;
185
[a61d1fc3]186 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
[9a5f0cb]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
[689f036]199 if (firstc == FAT_CLST_ROOT) {
[9a5f0cb]200 /* root directory special case */
201 assert(offset < rds);
[a61d1fc3]202 b = block_get(dev_handle, rscnt + fatcnt * sf + offset, bps);
[9a5f0cb]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);
[869e546]212 fsec = (clst * sizeof(fat_cluster_t)) / bps;
213 fidx = clst % (bps / sizeof(fat_cluster_t));
[9a5f0cb]214 /* read FAT1 */
[a61d1fc3]215 b = block_get(dev_handle, rscnt + fsec, bps);
[869e546]216 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
[9a5f0cb]217 assert(clst != FAT_CLST_BAD);
218 assert(clst < FAT_CLST_LAST1);
219 block_put(b);
220 }
221
[4573a79]222 b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
[a61d1fc3]223 offset % spc, bps);
[9a5f0cb]224
225 return b;
226}
227
[e2115311]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
[e1e3b26]275static void fat_node_initialize(fat_node_t *node)
[a2aa1dec]276{
[add5835]277 futex_initialize(&node->lock, 1);
[869e546]278 node->idx = NULL;
[e1e3b26]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
[e22632a9]287static uint16_t fat_bps_get(dev_handle_t dev_handle)
288{
289 block_t *bb;
290 uint16_t bps;
291
[a61d1fc3]292 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
[e22632a9]293 assert(bb != NULL);
[9a5f0cb]294 bps = uint16_t_le2host(FAT_BS(bb)->bps);
[e22632a9]295 block_put(bb);
296
297 return bps;
298}
299
[32fb10ed]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
[2c4bbcde]330static void fat_node_sync(fat_node_t *node)
[e1e3b26]331{
332 /* TODO */
333}
334
[add5835]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)
[e1e3b26]340{
[4573a79]341 block_t *b;
342 fat_dentry_t *d;
[c06dbf9]343 fat_node_t *nodep = NULL;
[4573a79]344 unsigned bps;
345 unsigned dps;
346
[add5835]347 if (idxp->nodep) {
[4573a79]348 /*
349 * We are lucky.
350 * The node is already instantiated in memory.
351 */
[add5835]352 futex_down(&idxp->nodep->lock);
353 if (!idxp->nodep->refcnt++)
[c06dbf9]354 list_remove(&idxp->nodep->ffn_link);
[add5835]355 futex_up(&idxp->nodep->lock);
356 return idxp->nodep;
[4573a79]357 }
358
359 /*
360 * We must instantiate the node from the file system.
361 */
362
[add5835]363 assert(idxp->pfc);
[4573a79]364
[add5835]365 futex_down(&ffn_futex);
[2c4bbcde]366 if (!list_empty(&ffn_head)) {
[add5835]367 /* Try to use a cached free node structure. */
368 fat_idx_t *idxp_tmp;
[2c4bbcde]369 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
[add5835]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);
[2c4bbcde]379 if (nodep->dirty)
380 fat_node_sync(nodep);
[add5835]381 idxp_tmp->nodep = NULL;
382 futex_up(&nodep->lock);
383 futex_up(&idxp_tmp->lock);
[2c4bbcde]384 } else {
[add5835]385skip_cache:
[2c4bbcde]386 /* Try to allocate a new node structure. */
[add5835]387 futex_up(&ffn_futex);
[2c4bbcde]388 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
389 if (!nodep)
390 return NULL;
391 }
[4573a79]392 fat_node_initialize(nodep);
393
[add5835]394 bps = fat_bps_get(idxp->dev_handle);
[4573a79]395 dps = bps / sizeof(fat_dentry_t);
396
[2c4bbcde]397 /* Read the block that contains the dentry of interest. */
[add5835]398 b = _fat_block_get(idxp->dev_handle, idxp->pfc,
399 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
[4573a79]400 assert(b);
401
[add5835]402 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
[2c4bbcde]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;
[2ab1023]410 /*
[e2115311]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.
[2ab1023]414 */
[e2115311]415 nodep->size = bps * _fat_blcks_get(idxp->dev_handle,
416 uint16_t_le2host(d->firstc));
[2c4bbcde]417 } else {
418 nodep->type = FAT_FILE;
[2ab1023]419 nodep->size = uint32_t_le2host(d->size);
[2c4bbcde]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. */
[add5835]428 nodep->idx = idxp;
429 idxp->nodep = nodep;
[2c4bbcde]430
431 return nodep;
[a2aa1dec]432}
433
[add5835]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
[06901c6b]449static void fat_node_put(void *node)
450{
[34b3ce3]451 fat_node_t *nodep = (fat_node_t *)node;
452
[add5835]453 futex_down(&nodep->lock);
[34b3ce3]454 if (!--nodep->refcnt) {
[add5835]455 futex_down(&ffn_futex);
[34b3ce3]456 list_append(&nodep->ffn_link, &ffn_head);
[add5835]457 futex_up(&ffn_futex);
[34b3ce3]458 }
[add5835]459 futex_up(&nodep->lock);
[06901c6b]460}
461
[80e8482]462static void *fat_create(int flags)
463{
464 return NULL; /* not supported at the moment */
465}
466
[45f244b]467static int fat_destroy(void *node)
[80e8482]468{
[45f244b]469 return ENOTSUP; /* not supported at the moment */
[80e8482]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
[a2aa1dec]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];
[79dbc3e]486 unsigned i, j;
[5446bee0]487 unsigned bps; /* bytes per sector */
[79dbc3e]488 unsigned dps; /* dentries per sector */
489 unsigned blocks;
[a2aa1dec]490 fat_dentry_t *d;
[79dbc3e]491 block_t *b;
492
[e811bde]493 futex_down(&parentp->idx->lock);
[869e546]494 bps = fat_bps_get(parentp->idx->dev_handle);
[5446bee0]495 dps = bps / sizeof(fat_dentry_t);
496 blocks = parentp->size / bps + (parentp->size % bps != 0);
[79dbc3e]497 for (i = 0; i < blocks; i++) {
498 unsigned dentries;
499
[869e546]500 b = fat_block_get(parentp, i);
[79dbc3e]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;
[32fb10ed]506 switch (fat_classify_dentry(d)) {
507 case FAT_DENTRY_SKIP:
[79dbc3e]508 continue;
[32fb10ed]509 case FAT_DENTRY_LAST:
[79dbc3e]510 block_put(b);
[e811bde]511 futex_up(&parentp->idx->lock);
[79dbc3e]512 return NULL;
[32fb10ed]513 default:
514 case FAT_DENTRY_VALID:
515 dentry_name_canonify(d, name);
516 break;
[79dbc3e]517 }
[d2e9c47]518 if (stricmp(name, component) == 0) {
[79dbc3e]519 /* hit */
[add5835]520 void *node;
[e811bde]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 */
[4797132]527 fat_idx_t *idx = fat_idx_get_by_pos(
[5a324099]528 parentp->idx->dev_handle, parentp->firstc,
[869e546]529 i * dps + j);
[e811bde]530 futex_up(&parentp->idx->lock);
[4797132]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 }
[add5835]539 node = fat_node_get_core(idx);
540 futex_up(&idx->lock);
[79dbc3e]541 block_put(b);
542 return node;
543 }
[9119d25]544 }
[79dbc3e]545 block_put(b);
[9119d25]546 }
[e811bde]547 futex_up(&parentp->idx->lock);
[a2aa1dec]548 return NULL;
[6364d3c]549}
550
[e1e3b26]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;
[869e546]556 return fnodep->idx->index;
[e1e3b26]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
[32fb10ed]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
[add5835]581 futex_down(&nodep->idx->lock);
[869e546]582 bps = fat_bps_get(nodep->idx->dev_handle);
[32fb10ed]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
[869e546]591 b = fat_block_get(nodep, i);
[32fb10ed]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);
[add5835]602 futex_up(&nodep->idx->lock);
[32fb10ed]603 return false;
604 default:
605 case FAT_DENTRY_VALID:
606 block_put(b);
[add5835]607 futex_up(&nodep->idx->lock);
[32fb10ed]608 return true;
609 }
610 block_put(b);
[add5835]611 futex_up(&nodep->idx->lock);
[32fb10ed]612 return true;
613 }
614 block_put(b);
615 }
616
[add5835]617 futex_up(&nodep->idx->lock);
[32fb10ed]618 return false;
619}
620
[74ea3c6]621static void *fat_root_get(dev_handle_t dev_handle)
622{
[689f036]623 return fat_node_get(dev_handle, 0);
[74ea3c6]624}
625
626static char fat_plb_get_char(unsigned pos)
627{
628 return fat_reg.plb_ro[pos % PLB_SIZE];
629}
630
[e1e3b26]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
[a2aa1dec]641/** libfs operations */
642libfs_ops_t fat_libfs_ops = {
643 .match = fat_match,
644 .node_get = fat_node_get,
[06901c6b]645 .node_put = fat_node_put,
[80e8482]646 .create = fat_create,
647 .destroy = fat_destroy,
648 .link = fat_link,
649 .unlink = fat_unlink,
[e1e3b26]650 .index_get = fat_index_get,
651 .size_get = fat_size_get,
652 .lnkcnt_get = fat_lnkcnt_get,
[32fb10ed]653 .has_children = fat_has_children,
[74ea3c6]654 .root_get = fat_root_get,
655 .plb_get_char = fat_plb_get_char,
[e1e3b26]656 .is_directory = fat_is_directory,
657 .is_file = fat_is_file
[a2aa1dec]658};
659
[cde485d]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);
[689f036]663 block_t *bb;
[7a35204a]664 uint16_t bps;
[689f036]665 uint16_t rde;
[cde485d]666 int rc;
667
[7a35204a]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
[689f036]699 /* Read the number of root directory entries. */
[a61d1fc3]700 bb = block_get(dev_handle, BS_BLOCK, BS_SIZE);
[7a35204a]701 bps = uint16_t_le2host(FAT_BS(bb)->bps);
[689f036]702 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
703 block_put(bb);
704
[7a35204a]705 if (bps != BS_SIZE) {
706 munmap(dev_buffer, BS_SIZE);
707 ipc_answer_0(rid, ENOTSUP);
708 return;
709 }
710
[cde485d]711 rc = fat_idx_init_by_dev_handle(dev_handle);
712 if (rc != EOK) {
[7a35204a]713 munmap(dev_buffer, BS_SIZE);
[cde485d]714 ipc_answer_0(rid, rc);
715 return;
716 }
717
[689f036]718 /* Initialize the root node. */
719 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
720 if (!rootp) {
[7a35204a]721 munmap(dev_buffer, BS_SIZE);
[689f036]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) {
[7a35204a]730 munmap(dev_buffer, BS_SIZE);
[689f036]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;
[5ab597d]742 rootp->lnkcnt = 0; /* FS root is not linked */
[689f036]743 rootp->size = rde * sizeof(fat_dentry_t);
744 rootp->idx = ridxp;
745 ridxp->nodep = rootp;
746
747 futex_up(&ridxp->lock);
748
[5ab597d]749 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
[cde485d]750}
751
752void fat_mount(ipc_callid_t rid, ipc_call_t *request)
753{
754 ipc_answer_0(rid, ENOTSUP);
755}
756
[be815bc]757void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
758{
[a2aa1dec]759 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
[be815bc]760}
761
[4bf40f6]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);
[79d031b]768 uint16_t bps = fat_bps_get(dev_handle);
769 size_t bytes;
[ddd1219]770 block_t *b;
[79d031b]771
[4bf40f6]772 if (!nodep) {
773 ipc_answer_0(rid, ENOENT);
774 return;
775 }
776
777 ipc_callid_t callid;
778 size_t len;
[6808614]779 if (!ipc_data_read_receive(&callid, &len)) {
[4bf40f6]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) {
[ddd1219]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 */
[79d031b]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);
[4bf40f6]797 } else {
[ddd1219]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
[4bf40f6]803 assert(nodep->type == FAT_DIRECTORY);
[ddd1219]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:
[4bf40f6]839 fat_node_put(nodep);
[ddd1219]840 ipc_answer_0(callid, ENOENT);
841 ipc_answer_1(rid, ENOENT, 0);
[4bf40f6]842 return;
[ddd1219]843hit:
844 (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
845 bytes = (pos - spos) + 1;
[4bf40f6]846 }
847
848 fat_node_put(nodep);
[79d031b]849 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
[4bf40f6]850}
851
[6f2dfd1]852static void
853fat_fill_gap(fat_node_t *nodep, fat_cluster_t mclst, off_t pos)
[8d32152]854{
855 /* TODO */
856}
857
[6f2dfd1]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
[c947dda]869void fat_write(ipc_callid_t rid, ipc_call_t *request)
870{
[8d32152]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 */
[6f2dfd1]924 fat_fill_gap(nodep, FAT_CLST_RES0, pos);
[8d32152]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 */
[6f2dfd1]929 block_put(b);
[8d32152]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 */
[6f2dfd1]942 int status;
[8d32152]943 unsigned nclsts;
[6f2dfd1]944 fat_cluster_t mcl, lcl;
945
[8d32152]946 nclsts = (ROUND_UP(pos + bytes, bps * spc) - clst_boundary) /
[6f2dfd1]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;
[8d32152]974 }
[c947dda]975}
976
[be815bc]977/**
978 * @}
979 */
Note: See TracBrowser for help on using the repository browser.