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
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 bps = fat_bps_get(parentp->idx->dev_handle);
395 dps = bps / sizeof(fat_dentry_t);
396 blocks = parentp->size / bps + (parentp->size % bps != 0);
397 for (i = 0; i < blocks; i++) {
398 unsigned dentries;
399
400 b = fat_block_get(parentp, i);
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;
406 switch (fat_classify_dentry(d)) {
407 case FAT_DENTRY_SKIP:
408 continue;
409 case FAT_DENTRY_LAST:
410 block_put(b);
411 return NULL;
412 default:
413 case FAT_DENTRY_VALID:
414 dentry_name_canonify(d, name);
415 break;
416 }
417 if (strcmp(name, component) == 0) {
418 /* hit */
419 void *node;
420 fat_idx_t *idx = fat_idx_get_by_pos(
421 parentp->idx->dev_handle, parentp->firstc,
422 i * dps + j);
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 }
431 node = fat_node_get_core(idx);
432 futex_up(&idx->lock);
433 block_put(b);
434 return node;
435 }
436 }
437 block_put(b);
438 }
439
440 return NULL;
441}
442
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;
448 return fnodep->idx->index;
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
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
473 futex_down(&nodep->idx->lock);
474 bps = fat_bps_get(nodep->idx->dev_handle);
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
483 b = fat_block_get(nodep, i);
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);
494 futex_up(&nodep->idx->lock);
495 return false;
496 default:
497 case FAT_DENTRY_VALID:
498 block_put(b);
499 futex_up(&nodep->idx->lock);
500 return true;
501 }
502 block_put(b);
503 futex_up(&nodep->idx->lock);
504 return true;
505 }
506 block_put(b);
507 }
508
509 futex_up(&nodep->idx->lock);
510 return false;
511}
512
513static void *fat_root_get(dev_handle_t dev_handle)
514{
515 return NULL; /* TODO */
516}
517
518static char fat_plb_get_char(unsigned pos)
519{
520 return fat_reg.plb_ro[pos % PLB_SIZE];
521}
522
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
533/** libfs operations */
534libfs_ops_t fat_libfs_ops = {
535 .match = fat_match,
536 .node_get = fat_node_get,
537 .node_put = fat_node_put,
538 .create = fat_create,
539 .destroy = fat_destroy,
540 .link = fat_link,
541 .unlink = fat_unlink,
542 .index_get = fat_index_get,
543 .size_get = fat_size_get,
544 .lnkcnt_get = fat_lnkcnt_get,
545 .has_children = fat_has_children,
546 .root_get = fat_root_get,
547 .plb_get_char = fat_plb_get_char,
548 .is_directory = fat_is_directory,
549 .is_file = fat_is_file
550};
551
552void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
553{
554 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
555}
556
557/**
558 * @}
559 */
Note: See TracBrowser for help on using the repository browser.