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

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

Add locks to FAT index structures, FAT in-core node structures. Add futex to
protect the list of free cached FAT in-core nodes. Make fat_node_get() and
fat_node_put() and some other functions aware of these new locks.

  • Property mode set to 100644
File size: 12.7 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>
42#include <async.h>
43#include <errno.h>
[a2aa1dec]44#include <string.h>
[776f2e6]45#include <byteorder.h>
[e1e3b26]46#include <libadt/hash_table.h>
47#include <libadt/list.h>
48#include <assert.h>
[d9e9caf]49#include <futex.h>
[e1e3b26]50
[e22632a9]51#define BS_BLOCK 0
52
[add5835]53/** Futex protecting the list of cached free FAT nodes. */
54static futex_t ffn_futex = FUTEX_INITIALIZER;
55
56/** List of cached free FAT nodes. */
57static LIST_INITIALIZE(ffn_head);
[6364d3c]58
59#define FAT_NAME_LEN 8
60#define FAT_EXT_LEN 3
61
62#define FAT_PAD ' '
63
64#define FAT_DENTRY_UNUSED 0x00
65#define FAT_DENTRY_E5_ESC 0x05
66#define FAT_DENTRY_DOT 0x2e
67#define FAT_DENTRY_ERASED 0xe5
68
[a2aa1dec]69static void dentry_name_canonify(fat_dentry_t *d, char *buf)
70{
71 int i;
72
73 for (i = 0; i < FAT_NAME_LEN; i++) {
74 if (d->name[i] == FAT_PAD) {
75 buf++;
76 break;
77 }
78 if (d->name[i] == FAT_DENTRY_E5_ESC)
79 *buf++ = 0xe5;
80 else
81 *buf++ = d->name[i];
82 }
83 if (d->ext[0] != FAT_PAD)
84 *buf++ = '.';
85 for (i = 0; i < FAT_EXT_LEN; i++) {
86 if (d->ext[i] == FAT_PAD) {
87 *buf = '\0';
88 return;
89 }
90 if (d->ext[i] == FAT_DENTRY_E5_ESC)
91 *buf++ = 0xe5;
92 else
93 *buf++ = d->ext[i];
94 }
95}
96
[34b3ce3]97/* TODO move somewhere else */
[79dbc3e]98typedef struct {
99 void *data;
100} block_t;
101
102static block_t *block_get(dev_handle_t dev_handle, off_t offset)
[a2aa1dec]103{
104 return NULL; /* TODO */
105}
106
[79dbc3e]107static void block_put(block_t *block)
[6364d3c]108{
[a2aa1dec]109 /* TODO */
110}
111
[9a5f0cb]112#define FAT_BS(b) ((fat_bs_t *)((b)->data))
113
[869e546]114#define FAT_CLST_RES0 0x0000
115#define FAT_CLST_RES1 0x0001 /* internally used to mark root directory */
[9a5f0cb]116#define FAT_CLST_FIRST 0x0002
117#define FAT_CLST_BAD 0xfff7
118#define FAT_CLST_LAST1 0xfff8
119#define FAT_CLST_LAST8 0xffff
120
[4573a79]121#define fat_block_get(np, off) \
122 _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
123
124static block_t *
[2c4bbcde]125_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
[9a5f0cb]126{
127 block_t *bb;
128 block_t *b;
129 unsigned bps;
130 unsigned spc;
131 unsigned rscnt; /* block address of the first FAT */
132 unsigned fatcnt;
133 unsigned rde;
134 unsigned rds; /* root directory size */
135 unsigned sf;
136 unsigned ssa; /* size of the system area */
137 unsigned clusters;
[2c4bbcde]138 fat_cluster_t clst = firstc;
[9a5f0cb]139 unsigned i;
140
[4573a79]141 bb = block_get(dev_handle, BS_BLOCK);
[9a5f0cb]142 bps = uint16_t_le2host(FAT_BS(bb)->bps);
143 spc = FAT_BS(bb)->spc;
144 rscnt = uint16_t_le2host(FAT_BS(bb)->rscnt);
145 fatcnt = FAT_BS(bb)->fatcnt;
146 rde = uint16_t_le2host(FAT_BS(bb)->root_ent_max);
147 sf = uint16_t_le2host(FAT_BS(bb)->sec_per_fat);
148 block_put(bb);
149
150 rds = (sizeof(fat_dentry_t) * rde) / bps;
151 rds += ((sizeof(fat_dentry_t) * rde) % bps != 0);
152 ssa = rscnt + fatcnt * sf + rds;
153
[2c4bbcde]154 if (firstc == FAT_CLST_RES1) {
[9a5f0cb]155 /* root directory special case */
156 assert(offset < rds);
[4573a79]157 b = block_get(dev_handle, rscnt + fatcnt * sf + offset);
[9a5f0cb]158 return b;
159 }
160
161 clusters = offset / spc;
162 for (i = 0; i < clusters; i++) {
163 unsigned fsec; /* sector offset relative to FAT1 */
164 unsigned fidx; /* FAT1 entry index */
165
166 assert(clst >= FAT_CLST_FIRST && clst < FAT_CLST_BAD);
[869e546]167 fsec = (clst * sizeof(fat_cluster_t)) / bps;
168 fidx = clst % (bps / sizeof(fat_cluster_t));
[9a5f0cb]169 /* read FAT1 */
[4573a79]170 b = block_get(dev_handle, rscnt + fsec);
[869e546]171 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
[9a5f0cb]172 assert(clst != FAT_CLST_BAD);
173 assert(clst < FAT_CLST_LAST1);
174 block_put(b);
175 }
176
[4573a79]177 b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
178 offset % spc);
[9a5f0cb]179
180 return b;
181}
182
[e1e3b26]183static void fat_node_initialize(fat_node_t *node)
[a2aa1dec]184{
[add5835]185 futex_initialize(&node->lock, 1);
[869e546]186 node->idx = NULL;
[e1e3b26]187 node->type = 0;
188 link_initialize(&node->ffn_link);
189 node->size = 0;
190 node->lnkcnt = 0;
191 node->refcnt = 0;
192 node->dirty = false;
193}
194
[e22632a9]195static uint16_t fat_bps_get(dev_handle_t dev_handle)
196{
197 block_t *bb;
198 uint16_t bps;
199
200 bb = block_get(dev_handle, BS_BLOCK);
201 assert(bb != NULL);
[9a5f0cb]202 bps = uint16_t_le2host(FAT_BS(bb)->bps);
[e22632a9]203 block_put(bb);
204
205 return bps;
206}
207
[32fb10ed]208typedef enum {
209 FAT_DENTRY_SKIP,
210 FAT_DENTRY_LAST,
211 FAT_DENTRY_VALID
212} fat_dentry_clsf_t;
213
214static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
215{
216 if (d->attr & FAT_ATTR_VOLLABEL) {
217 /* volume label entry */
218 return FAT_DENTRY_SKIP;
219 }
220 if (d->name[0] == FAT_DENTRY_ERASED) {
221 /* not-currently-used entry */
222 return FAT_DENTRY_SKIP;
223 }
224 if (d->name[0] == FAT_DENTRY_UNUSED) {
225 /* never used entry */
226 return FAT_DENTRY_LAST;
227 }
228 if (d->name[0] == FAT_DENTRY_DOT) {
229 /*
230 * Most likely '.' or '..'.
231 * It cannot occur in a regular file name.
232 */
233 return FAT_DENTRY_SKIP;
234 }
235 return FAT_DENTRY_VALID;
236}
237
[2c4bbcde]238static void fat_node_sync(fat_node_t *node)
[e1e3b26]239{
240 /* TODO */
241}
242
[add5835]243/** Internal version of fat_node_get().
244 *
245 * @param idxp Locked index structure.
246 */
247static void *fat_node_get_core(fat_idx_t *idxp)
[e1e3b26]248{
[4573a79]249 block_t *b;
250 fat_dentry_t *d;
251 fat_node_t *nodep;
252 unsigned bps;
253 unsigned dps;
254
[add5835]255 if (idxp->nodep) {
[4573a79]256 /*
257 * We are lucky.
258 * The node is already instantiated in memory.
259 */
[add5835]260 futex_down(&idxp->nodep->lock);
261 if (!idxp->nodep->refcnt++)
[34b3ce3]262 list_remove(&nodep->ffn_link);
[add5835]263 futex_up(&idxp->nodep->lock);
264 return idxp->nodep;
[4573a79]265 }
266
267 /*
268 * We must instantiate the node from the file system.
269 */
270
[add5835]271 assert(idxp->pfc);
[4573a79]272
[add5835]273 futex_down(&ffn_futex);
[2c4bbcde]274 if (!list_empty(&ffn_head)) {
[add5835]275 /* Try to use a cached free node structure. */
276 fat_idx_t *idxp_tmp;
[2c4bbcde]277 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
[add5835]278 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
279 goto skip_cache;
280 idxp_tmp = nodep->idx;
281 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
282 futex_up(&nodep->lock);
283 goto skip_cache;
284 }
285 list_remove(&nodep->ffn_link);
286 futex_up(&ffn_futex);
[2c4bbcde]287 if (nodep->dirty)
288 fat_node_sync(nodep);
[add5835]289 idxp_tmp->nodep = NULL;
290 futex_up(&nodep->lock);
291 futex_up(&idxp_tmp->lock);
[2c4bbcde]292 } else {
[add5835]293skip_cache:
[2c4bbcde]294 /* Try to allocate a new node structure. */
[add5835]295 futex_up(&ffn_futex);
[2c4bbcde]296 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
297 if (!nodep)
298 return NULL;
299 }
[4573a79]300 fat_node_initialize(nodep);
301
[add5835]302 bps = fat_bps_get(idxp->dev_handle);
[4573a79]303 dps = bps / sizeof(fat_dentry_t);
304
[2c4bbcde]305 /* Read the block that contains the dentry of interest. */
[add5835]306 b = _fat_block_get(idxp->dev_handle, idxp->pfc,
307 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
[4573a79]308 assert(b);
309
[add5835]310 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
[2c4bbcde]311 if (d->attr & FAT_ATTR_SUBDIR) {
312 /*
313 * The only directory which does not have this bit set is the
314 * root directory itself. The root directory node is handled
315 * and initialized elsewhere.
316 */
317 nodep->type = FAT_DIRECTORY;
318 } else {
319 nodep->type = FAT_FILE;
320 }
321 nodep->firstc = uint16_t_le2host(d->firstc);
322 nodep->size = uint32_t_le2host(d->size);
323 nodep->lnkcnt = 1;
324 nodep->refcnt = 1;
325
326 block_put(b);
327
328 /* Link the idx structure with the node structure. */
[add5835]329 nodep->idx = idxp;
330 idxp->nodep = nodep;
[2c4bbcde]331
332 return nodep;
[a2aa1dec]333}
334
[add5835]335/** Instantiate a FAT in-core node. */
336static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
337{
338 void *node;
339 fat_idx_t *idxp;
340
341 idxp = fat_idx_get_by_index(dev_handle, index);
342 if (!idxp)
343 return NULL;
344 /* idxp->lock held */
345 node = fat_node_get_core(idxp);
346 futex_up(&idxp->lock);
347 return node;
348}
349
[06901c6b]350static void fat_node_put(void *node)
351{
[34b3ce3]352 fat_node_t *nodep = (fat_node_t *)node;
353
[add5835]354 futex_down(&nodep->lock);
[34b3ce3]355 if (!--nodep->refcnt) {
[add5835]356 futex_down(&ffn_futex);
[34b3ce3]357 list_append(&nodep->ffn_link, &ffn_head);
[add5835]358 futex_up(&ffn_futex);
[34b3ce3]359 }
[add5835]360 futex_up(&nodep->lock);
[06901c6b]361}
362
[80e8482]363static void *fat_create(int flags)
364{
365 return NULL; /* not supported at the moment */
366}
367
[45f244b]368static int fat_destroy(void *node)
[80e8482]369{
[45f244b]370 return ENOTSUP; /* not supported at the moment */
[80e8482]371}
372
373static bool fat_link(void *prnt, void *chld, const char *name)
374{
375 return false; /* not supported at the moment */
376}
377
378static int fat_unlink(void *prnt, void *chld)
379{
380 return ENOTSUP; /* not supported at the moment */
381}
382
[a2aa1dec]383static void *fat_match(void *prnt, const char *component)
384{
385 fat_node_t *parentp = (fat_node_t *)prnt;
386 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
[79dbc3e]387 unsigned i, j;
[5446bee0]388 unsigned bps; /* bytes per sector */
[79dbc3e]389 unsigned dps; /* dentries per sector */
390 unsigned blocks;
[a2aa1dec]391 fat_dentry_t *d;
[79dbc3e]392 block_t *b;
393
[869e546]394 bps = fat_bps_get(parentp->idx->dev_handle);
[5446bee0]395 dps = bps / sizeof(fat_dentry_t);
396 blocks = parentp->size / bps + (parentp->size % bps != 0);
[79dbc3e]397 for (i = 0; i < blocks; i++) {
398 unsigned dentries;
399
[869e546]400 b = fat_block_get(parentp, i);
[79dbc3e]401 dentries = (i == blocks - 1) ?
402 parentp->size % sizeof(fat_dentry_t) :
403 dps;
404 for (j = 0; j < dentries; j++) {
405 d = ((fat_dentry_t *)b->data) + j;
[32fb10ed]406 switch (fat_classify_dentry(d)) {
407 case FAT_DENTRY_SKIP:
[79dbc3e]408 continue;
[32fb10ed]409 case FAT_DENTRY_LAST:
[79dbc3e]410 block_put(b);
411 return NULL;
[32fb10ed]412 default:
413 case FAT_DENTRY_VALID:
414 dentry_name_canonify(d, name);
415 break;
[79dbc3e]416 }
417 if (strcmp(name, component) == 0) {
418 /* hit */
[add5835]419 void *node;
[4797132]420 fat_idx_t *idx = fat_idx_get_by_pos(
[5a324099]421 parentp->idx->dev_handle, parentp->firstc,
[869e546]422 i * dps + j);
[4797132]423 if (!idx) {
424 /*
425 * Can happen if memory is low or if we
426 * run out of 32-bit indices.
427 */
428 block_put(b);
429 return NULL;
430 }
[add5835]431 node = fat_node_get_core(idx);
432 futex_up(&idx->lock);
[79dbc3e]433 block_put(b);
434 return node;
435 }
[9119d25]436 }
[79dbc3e]437 block_put(b);
[9119d25]438 }
[a2aa1dec]439
440 return NULL;
[6364d3c]441}
442
[e1e3b26]443static fs_index_t fat_index_get(void *node)
444{
445 fat_node_t *fnodep = (fat_node_t *)node;
446 if (!fnodep)
447 return 0;
[869e546]448 return fnodep->idx->index;
[e1e3b26]449}
450
451static size_t fat_size_get(void *node)
452{
453 return ((fat_node_t *)node)->size;
454}
455
456static unsigned fat_lnkcnt_get(void *node)
457{
458 return ((fat_node_t *)node)->lnkcnt;
459}
460
[32fb10ed]461static bool fat_has_children(void *node)
462{
463 fat_node_t *nodep = (fat_node_t *)node;
464 unsigned bps;
465 unsigned dps;
466 unsigned blocks;
467 block_t *b;
468 unsigned i, j;
469
470 if (nodep->type != FAT_DIRECTORY)
471 return false;
472
[add5835]473 futex_down(&nodep->idx->lock);
[869e546]474 bps = fat_bps_get(nodep->idx->dev_handle);
[32fb10ed]475 dps = bps / sizeof(fat_dentry_t);
476
477 blocks = nodep->size / bps + (nodep->size % bps != 0);
478
479 for (i = 0; i < blocks; i++) {
480 unsigned dentries;
481 fat_dentry_t *d;
482
[869e546]483 b = fat_block_get(nodep, i);
[32fb10ed]484 dentries = (i == blocks - 1) ?
485 nodep->size % sizeof(fat_dentry_t) :
486 dps;
487 for (j = 0; j < dentries; j++) {
488 d = ((fat_dentry_t *)b->data) + j;
489 switch (fat_classify_dentry(d)) {
490 case FAT_DENTRY_SKIP:
491 continue;
492 case FAT_DENTRY_LAST:
493 block_put(b);
[add5835]494 futex_up(&nodep->idx->lock);
[32fb10ed]495 return false;
496 default:
497 case FAT_DENTRY_VALID:
498 block_put(b);
[add5835]499 futex_up(&nodep->idx->lock);
[32fb10ed]500 return true;
501 }
502 block_put(b);
[add5835]503 futex_up(&nodep->idx->lock);
[32fb10ed]504 return true;
505 }
506 block_put(b);
507 }
508
[add5835]509 futex_up(&nodep->idx->lock);
[32fb10ed]510 return false;
511}
512
[74ea3c6]513static void *fat_root_get(dev_handle_t dev_handle)
514{
[2c4bbcde]515 return NULL; /* TODO */
[74ea3c6]516}
517
518static char fat_plb_get_char(unsigned pos)
519{
520 return fat_reg.plb_ro[pos % PLB_SIZE];
521}
522
[e1e3b26]523static bool fat_is_directory(void *node)
524{
525 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
526}
527
528static bool fat_is_file(void *node)
529{
530 return ((fat_node_t *)node)->type == FAT_FILE;
531}
532
[a2aa1dec]533/** libfs operations */
534libfs_ops_t fat_libfs_ops = {
535 .match = fat_match,
536 .node_get = fat_node_get,
[06901c6b]537 .node_put = fat_node_put,
[80e8482]538 .create = fat_create,
539 .destroy = fat_destroy,
540 .link = fat_link,
541 .unlink = fat_unlink,
[e1e3b26]542 .index_get = fat_index_get,
543 .size_get = fat_size_get,
544 .lnkcnt_get = fat_lnkcnt_get,
[32fb10ed]545 .has_children = fat_has_children,
[74ea3c6]546 .root_get = fat_root_get,
547 .plb_get_char = fat_plb_get_char,
[e1e3b26]548 .is_directory = fat_is_directory,
549 .is_file = fat_is_file
[a2aa1dec]550};
551
[be815bc]552void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
553{
[a2aa1dec]554 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
[be815bc]555}
556
557/**
558 * @}
559 */
Note: See TracBrowser for help on using the repository browser.