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

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

Add synchronization to fat_match().

  • Property mode set to 100644
File size: 13.0 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file fat_ops.c
35 * @brief Implementation of VFS operations for the FAT file system server.
36 */
37
38#include "fat.h"
39#include "../../vfs/vfs.h"
40#include <libfs.h>
41#include <ipc/ipc.h>
42#include <async.h>
43#include <errno.h>
44#include <string.h>
45#include <byteorder.h>
46#include <libadt/hash_table.h>
47#include <libadt/list.h>
48#include <assert.h>
49#include <futex.h>
50
51#define BS_BLOCK 0
52
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);
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
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
97/* TODO move somewhere else */
98typedef struct {
99 void *data;
100} block_t;
101
102static block_t *block_get(dev_handle_t dev_handle, off_t offset)
103{
104 return NULL; /* TODO */
105}
106
107static void block_put(block_t *block)
108{
109 /* TODO */
110}
111
112#define FAT_BS(b) ((fat_bs_t *)((b)->data))
113
114#define FAT_CLST_RES0 0x0000
115#define FAT_CLST_RES1 0x0001 /* internally used to mark root directory */
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
121#define fat_block_get(np, off) \
122 _fat_block_get((np)->idx->dev_handle, (np)->firstc, (off))
123
124static block_t *
125_fat_block_get(dev_handle_t dev_handle, fat_cluster_t firstc, off_t offset)
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;
138 fat_cluster_t clst = firstc;
139 unsigned i;
140
141 bb = block_get(dev_handle, BS_BLOCK);
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
154 if (firstc == FAT_CLST_RES1) {
155 /* root directory special case */
156 assert(offset < rds);
157 b = block_get(dev_handle, rscnt + fatcnt * sf + offset);
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);
167 fsec = (clst * sizeof(fat_cluster_t)) / bps;
168 fidx = clst % (bps / sizeof(fat_cluster_t));
169 /* read FAT1 */
170 b = block_get(dev_handle, rscnt + fsec);
171 clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
172 assert(clst != FAT_CLST_BAD);
173 assert(clst < FAT_CLST_LAST1);
174 block_put(b);
175 }
176
177 b = block_get(dev_handle, ssa + (clst - FAT_CLST_FIRST) * spc +
178 offset % spc);
179
180 return b;
181}
182
183static void fat_node_initialize(fat_node_t *node)
184{
185 futex_initialize(&node->lock, 1);
186 node->idx = NULL;
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
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);
202 bps = uint16_t_le2host(FAT_BS(bb)->bps);
203 block_put(bb);
204
205 return bps;
206}
207
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
238static void fat_node_sync(fat_node_t *node)
239{
240 /* TODO */
241}
242
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)
248{
249 block_t *b;
250 fat_dentry_t *d;
251 fat_node_t *nodep;
252 unsigned bps;
253 unsigned dps;
254
255 if (idxp->nodep) {
256 /*
257 * We are lucky.
258 * The node is already instantiated in memory.
259 */
260 futex_down(&idxp->nodep->lock);
261 if (!idxp->nodep->refcnt++)
262 list_remove(&nodep->ffn_link);
263 futex_up(&idxp->nodep->lock);
264 return idxp->nodep;
265 }
266
267 /*
268 * We must instantiate the node from the file system.
269 */
270
271 assert(idxp->pfc);
272
273 futex_down(&ffn_futex);
274 if (!list_empty(&ffn_head)) {
275 /* Try to use a cached free node structure. */
276 fat_idx_t *idxp_tmp;
277 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
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);
287 if (nodep->dirty)
288 fat_node_sync(nodep);
289 idxp_tmp->nodep = NULL;
290 futex_up(&nodep->lock);
291 futex_up(&idxp_tmp->lock);
292 } else {
293skip_cache:
294 /* Try to allocate a new node structure. */
295 futex_up(&ffn_futex);
296 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
297 if (!nodep)
298 return NULL;
299 }
300 fat_node_initialize(nodep);
301
302 bps = fat_bps_get(idxp->dev_handle);
303 dps = bps / sizeof(fat_dentry_t);
304
305 /* Read the block that contains the dentry of interest. */
306 b = _fat_block_get(idxp->dev_handle, idxp->pfc,
307 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
308 assert(b);
309
310 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
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. */
329 nodep->idx = idxp;
330 idxp->nodep = nodep;
331
332 return nodep;
333}
334
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
350static void fat_node_put(void *node)
351{
352 fat_node_t *nodep = (fat_node_t *)node;
353
354 futex_down(&nodep->lock);
355 if (!--nodep->refcnt) {
356 futex_down(&ffn_futex);
357 list_append(&nodep->ffn_link, &ffn_head);
358 futex_up(&ffn_futex);
359 }
360 futex_up(&nodep->lock);
361}
362
363static void *fat_create(int flags)
364{
365 return NULL; /* not supported at the moment */
366}
367
368static int fat_destroy(void *node)
369{
370 return ENOTSUP; /* not supported at the moment */
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
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];
387 unsigned i, j;
388 unsigned bps; /* bytes per sector */
389 unsigned dps; /* dentries per sector */
390 unsigned blocks;
391 fat_dentry_t *d;
392 block_t *b;
393
394 futex_down(&parentp->idx->lock);
395 bps = fat_bps_get(parentp->idx->dev_handle);
396 dps = bps / sizeof(fat_dentry_t);
397 blocks = parentp->size / bps + (parentp->size % bps != 0);
398 for (i = 0; i < blocks; i++) {
399 unsigned dentries;
400
401 b = fat_block_get(parentp, i);
402 dentries = (i == blocks - 1) ?
403 parentp->size % sizeof(fat_dentry_t) :
404 dps;
405 for (j = 0; j < dentries; j++) {
406 d = ((fat_dentry_t *)b->data) + j;
407 switch (fat_classify_dentry(d)) {
408 case FAT_DENTRY_SKIP:
409 continue;
410 case FAT_DENTRY_LAST:
411 block_put(b);
412 futex_up(&parentp->idx->lock);
413 return NULL;
414 default:
415 case FAT_DENTRY_VALID:
416 dentry_name_canonify(d, name);
417 break;
418 }
419 if (strcmp(name, component) == 0) {
420 /* hit */
421 void *node;
422 /*
423 * Assume tree hierarchy for locking. We
424 * already have the parent and now we are going
425 * to lock the child. Never lock in the oposite
426 * order.
427 */
428 fat_idx_t *idx = fat_idx_get_by_pos(
429 parentp->idx->dev_handle, parentp->firstc,
430 i * dps + j);
431 futex_up(&parentp->idx->lock);
432 if (!idx) {
433 /*
434 * Can happen if memory is low or if we
435 * run out of 32-bit indices.
436 */
437 block_put(b);
438 return NULL;
439 }
440 node = fat_node_get_core(idx);
441 futex_up(&idx->lock);
442 block_put(b);
443 return node;
444 }
445 }
446 block_put(b);
447 }
448 futex_up(&parentp->idx->lock);
449 return NULL;
450}
451
452static fs_index_t fat_index_get(void *node)
453{
454 fat_node_t *fnodep = (fat_node_t *)node;
455 if (!fnodep)
456 return 0;
457 return fnodep->idx->index;
458}
459
460static size_t fat_size_get(void *node)
461{
462 return ((fat_node_t *)node)->size;
463}
464
465static unsigned fat_lnkcnt_get(void *node)
466{
467 return ((fat_node_t *)node)->lnkcnt;
468}
469
470static bool fat_has_children(void *node)
471{
472 fat_node_t *nodep = (fat_node_t *)node;
473 unsigned bps;
474 unsigned dps;
475 unsigned blocks;
476 block_t *b;
477 unsigned i, j;
478
479 if (nodep->type != FAT_DIRECTORY)
480 return false;
481
482 futex_down(&nodep->idx->lock);
483 bps = fat_bps_get(nodep->idx->dev_handle);
484 dps = bps / sizeof(fat_dentry_t);
485
486 blocks = nodep->size / bps + (nodep->size % bps != 0);
487
488 for (i = 0; i < blocks; i++) {
489 unsigned dentries;
490 fat_dentry_t *d;
491
492 b = fat_block_get(nodep, i);
493 dentries = (i == blocks - 1) ?
494 nodep->size % sizeof(fat_dentry_t) :
495 dps;
496 for (j = 0; j < dentries; j++) {
497 d = ((fat_dentry_t *)b->data) + j;
498 switch (fat_classify_dentry(d)) {
499 case FAT_DENTRY_SKIP:
500 continue;
501 case FAT_DENTRY_LAST:
502 block_put(b);
503 futex_up(&nodep->idx->lock);
504 return false;
505 default:
506 case FAT_DENTRY_VALID:
507 block_put(b);
508 futex_up(&nodep->idx->lock);
509 return true;
510 }
511 block_put(b);
512 futex_up(&nodep->idx->lock);
513 return true;
514 }
515 block_put(b);
516 }
517
518 futex_up(&nodep->idx->lock);
519 return false;
520}
521
522static void *fat_root_get(dev_handle_t dev_handle)
523{
524 return NULL; /* TODO */
525}
526
527static char fat_plb_get_char(unsigned pos)
528{
529 return fat_reg.plb_ro[pos % PLB_SIZE];
530}
531
532static bool fat_is_directory(void *node)
533{
534 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
535}
536
537static bool fat_is_file(void *node)
538{
539 return ((fat_node_t *)node)->type == FAT_FILE;
540}
541
542/** libfs operations */
543libfs_ops_t fat_libfs_ops = {
544 .match = fat_match,
545 .node_get = fat_node_get,
546 .node_put = fat_node_put,
547 .create = fat_create,
548 .destroy = fat_destroy,
549 .link = fat_link,
550 .unlink = fat_unlink,
551 .index_get = fat_index_get,
552 .size_get = fat_size_get,
553 .lnkcnt_get = fat_lnkcnt_get,
554 .has_children = fat_has_children,
555 .root_get = fat_root_get,
556 .plb_get_char = fat_plb_get_char,
557 .is_directory = fat_is_directory,
558 .is_file = fat_is_file
559};
560
561void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
562{
563 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
564}
565
566/**
567 * @}
568 */
Note: See TracBrowser for help on using the repository browser.