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

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

libfs destroy operation should rather return an error code instead of a truth
value.

  • Property mode set to 100644
File size: 11.1 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#define FIN_KEY_DEV_HANDLE 0
54#define FIN_KEY_INDEX 1
55
56/** Futex protecting both fin_hash and ffn_head. */
57futex_t fin_futex = FUTEX_INITIALIZER;
58
59/** Hash table of FAT in-core nodes. */
60hash_table_t fin_hash;
61
62/** List of free FAT in-core nodes. */
63link_t ffn_head;
64
65#define FAT_NAME_LEN 8
66#define FAT_EXT_LEN 3
67
68#define FAT_PAD ' '
69
70#define FAT_DENTRY_UNUSED 0x00
71#define FAT_DENTRY_E5_ESC 0x05
72#define FAT_DENTRY_DOT 0x2e
73#define FAT_DENTRY_ERASED 0xe5
74
75static void dentry_name_canonify(fat_dentry_t *d, char *buf)
76{
77 int i;
78
79 for (i = 0; i < FAT_NAME_LEN; i++) {
80 if (d->name[i] == FAT_PAD) {
81 buf++;
82 break;
83 }
84 if (d->name[i] == FAT_DENTRY_E5_ESC)
85 *buf++ = 0xe5;
86 else
87 *buf++ = d->name[i];
88 }
89 if (d->ext[0] != FAT_PAD)
90 *buf++ = '.';
91 for (i = 0; i < FAT_EXT_LEN; i++) {
92 if (d->ext[i] == FAT_PAD) {
93 *buf = '\0';
94 return;
95 }
96 if (d->ext[i] == FAT_DENTRY_E5_ESC)
97 *buf++ = 0xe5;
98 else
99 *buf++ = d->ext[i];
100 }
101}
102
103/* TODO and also move somewhere else */
104typedef struct {
105 void *data;
106} block_t;
107
108static block_t *block_get(dev_handle_t dev_handle, off_t offset)
109{
110 return NULL; /* TODO */
111}
112
113static block_t *fat_block_get(dev_handle_t dev_handle, fs_index_t index,
114 off_t offset) {
115 return NULL; /* TODO */
116}
117
118static void block_put(block_t *block)
119{
120 /* TODO */
121}
122
123static void fat_node_initialize(fat_node_t *node)
124{
125 futex_initialize(&node->lock, 1);
126 node->type = 0;
127 node->index = 0;
128 node->pindex = 0;
129 node->dev_handle = 0;
130 link_initialize(&node->fin_link);
131 link_initialize(&node->ffn_link);
132 node->size = 0;
133 node->lnkcnt = 0;
134 node->refcnt = 0;
135 node->dirty = false;
136}
137
138static uint16_t fat_bps_get(dev_handle_t dev_handle)
139{
140 block_t *bb;
141 uint16_t bps;
142
143 bb = block_get(dev_handle, BS_BLOCK);
144 assert(bb != NULL);
145 bps = uint16_t_le2host(((fat_bs_t *)bb->data)->bps);
146 block_put(bb);
147
148 return bps;
149}
150
151typedef enum {
152 FAT_DENTRY_SKIP,
153 FAT_DENTRY_LAST,
154 FAT_DENTRY_VALID
155} fat_dentry_clsf_t;
156
157static fat_dentry_clsf_t fat_classify_dentry(fat_dentry_t *d)
158{
159 if (d->attr & FAT_ATTR_VOLLABEL) {
160 /* volume label entry */
161 return FAT_DENTRY_SKIP;
162 }
163 if (d->name[0] == FAT_DENTRY_ERASED) {
164 /* not-currently-used entry */
165 return FAT_DENTRY_SKIP;
166 }
167 if (d->name[0] == FAT_DENTRY_UNUSED) {
168 /* never used entry */
169 return FAT_DENTRY_LAST;
170 }
171 if (d->name[0] == FAT_DENTRY_DOT) {
172 /*
173 * Most likely '.' or '..'.
174 * It cannot occur in a regular file name.
175 */
176 return FAT_DENTRY_SKIP;
177 }
178 return FAT_DENTRY_VALID;
179}
180
181static void fat_sync_node(fat_node_t *node)
182{
183 /* TODO */
184}
185
186/** Instantiate a FAT in-core node.
187 *
188 * FAT stores the info necessary for instantiation of a node in the parent of
189 * that node. This design necessitated the addition of the parent node index
190 * parameter to this otherwise generic libfs API.
191 */
192static void *
193fat_node_get(dev_handle_t dev_handle, fs_index_t index, fs_index_t pindex)
194{
195 link_t *lnk;
196 fat_node_t *node = NULL;
197 block_t *b;
198 unsigned bps;
199 unsigned dps;
200 fat_dentry_t *d;
201 unsigned i, j;
202
203 unsigned long key[] = {
204 [FIN_KEY_DEV_HANDLE] = dev_handle,
205 [FIN_KEY_INDEX] = index
206 };
207
208 futex_down(&fin_futex);
209 lnk = hash_table_find(&fin_hash, key);
210 if (lnk) {
211 /*
212 * The in-core node was found in the hash table.
213 */
214 node = hash_table_get_instance(lnk, fat_node_t, fin_link);
215 if (!node->refcnt++)
216 list_remove(&node->ffn_link);
217 futex_up(&fin_futex);
218
219 /* Make sure that the node is fully instantiated. */
220 futex_down(&node->lock);
221 futex_up(&node->lock);
222
223 return (void *) node;
224 }
225
226 bps = fat_bps_get(dev_handle);
227 dps = bps / sizeof(fat_dentry_t);
228
229 if (!list_empty(&ffn_head)) {
230 /*
231 * We are going to reuse a node from the free list.
232 */
233 lnk = ffn_head.next;
234 list_remove(lnk);
235 node = list_get_instance(lnk, fat_node_t, ffn_link);
236 assert(!node->refcnt);
237 if (node->dirty)
238 fat_sync_node(node);
239 key[FIN_KEY_DEV_HANDLE] = node->dev_handle;
240 key[FIN_KEY_INDEX] = node->index;
241 hash_table_remove(&fin_hash, key, sizeof(key)/sizeof(*key));
242 } else {
243 /*
244 * We need to allocate a new node.
245 */
246 node = malloc(sizeof(fat_node_t));
247 if (!node)
248 return NULL;
249 }
250 fat_node_initialize(node);
251 node->refcnt++;
252 node->lnkcnt++;
253 node->dev_handle = dev_handle;
254 node->index = index;
255 node->pindex = pindex;
256 key[FIN_KEY_DEV_HANDLE] = node->dev_handle;
257 key[FIN_KEY_INDEX] = node->index;
258 hash_table_insert(&fin_hash, key, &node->fin_link);
259
260 /*
261 * We have already put the node back to fin_hash.
262 * The node is not yet fully instantiated so we lock it prior to
263 * unlocking fin_hash.
264 */
265 futex_down(&node->lock);
266 futex_up(&fin_futex);
267
268 /*
269 * Because of the design of the FAT file system, we have no clue about
270 * how big (i.e. how many directory entries it contains) is the parent
271 * of the node we are trying to instantiate. However, we know that it
272 * must contain a directory entry for our node of interest. We simply
273 * scan the parent until we find it.
274 */
275 for (i = 0; ; i++) {
276 b = fat_block_get(node->dev_handle, node->pindex, i);
277 for (j = 0; j < dps; j++) {
278 d = ((fat_dentry_t *)b->data) + j;
279 if (d->firstc == node->index)
280 goto found;
281 }
282 block_put(b);
283 }
284
285found:
286 if (!(d->attr & (FAT_ATTR_SUBDIR | FAT_ATTR_VOLLABEL)))
287 node->type = FAT_FILE;
288 if ((d->attr & FAT_ATTR_SUBDIR) || !index)
289 node->type = FAT_DIRECTORY;
290 assert((node->type == FAT_FILE) || (node->type == FAT_DIRECTORY));
291
292 node->size = uint32_t_le2host(d->size);
293 block_put(b);
294
295 futex_up(&node->lock);
296 return node;
297}
298
299static void fat_node_put(void *node)
300{
301 fat_node_t *nodep = (fat_node_t *)node;
302
303 futex_down(&fin_futex);
304 if (!--nodep->refcnt)
305 list_append(&nodep->ffn_link, &ffn_head);
306 futex_up(&fin_futex);
307}
308
309static void *fat_create(int flags)
310{
311 return NULL; /* not supported at the moment */
312}
313
314static int fat_destroy(void *node)
315{
316 return ENOTSUP; /* not supported at the moment */
317}
318
319static bool fat_link(void *prnt, void *chld, const char *name)
320{
321 return false; /* not supported at the moment */
322}
323
324static int fat_unlink(void *prnt, void *chld)
325{
326 return ENOTSUP; /* not supported at the moment */
327}
328
329static void *fat_match(void *prnt, const char *component)
330{
331 fat_node_t *parentp = (fat_node_t *)prnt;
332 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
333 unsigned i, j;
334 unsigned bps; /* bytes per sector */
335 unsigned dps; /* dentries per sector */
336 unsigned blocks;
337 fat_dentry_t *d;
338 block_t *b;
339
340 bps = fat_bps_get(parentp->dev_handle);
341 dps = bps / sizeof(fat_dentry_t);
342 blocks = parentp->size / bps + (parentp->size % bps != 0);
343 for (i = 0; i < blocks; i++) {
344 unsigned dentries;
345
346 b = fat_block_get(parentp->dev_handle, parentp->index, i);
347 dentries = (i == blocks - 1) ?
348 parentp->size % sizeof(fat_dentry_t) :
349 dps;
350 for (j = 0; j < dentries; j++) {
351 d = ((fat_dentry_t *)b->data) + j;
352 switch (fat_classify_dentry(d)) {
353 case FAT_DENTRY_SKIP:
354 continue;
355 case FAT_DENTRY_LAST:
356 block_put(b);
357 return NULL;
358 default:
359 case FAT_DENTRY_VALID:
360 dentry_name_canonify(d, name);
361 break;
362 }
363 if (strcmp(name, component) == 0) {
364 /* hit */
365 void *node = fat_node_get(parentp->dev_handle,
366 (fs_index_t)uint16_t_le2host(d->firstc),
367 parentp->index);
368 block_put(b);
369 return node;
370 }
371 }
372 block_put(b);
373 }
374
375 return NULL;
376}
377
378static fs_index_t fat_index_get(void *node)
379{
380 fat_node_t *fnodep = (fat_node_t *)node;
381 if (!fnodep)
382 return 0;
383 return fnodep->index;
384}
385
386static size_t fat_size_get(void *node)
387{
388 return ((fat_node_t *)node)->size;
389}
390
391static unsigned fat_lnkcnt_get(void *node)
392{
393 return ((fat_node_t *)node)->lnkcnt;
394}
395
396static bool fat_has_children(void *node)
397{
398 fat_node_t *nodep = (fat_node_t *)node;
399 unsigned bps;
400 unsigned dps;
401 unsigned blocks;
402 block_t *b;
403 unsigned i, j;
404
405 if (nodep->type != FAT_DIRECTORY)
406 return false;
407
408 bps = fat_bps_get(nodep->dev_handle);
409 dps = bps / sizeof(fat_dentry_t);
410
411 blocks = nodep->size / bps + (nodep->size % bps != 0);
412
413 for (i = 0; i < blocks; i++) {
414 unsigned dentries;
415 fat_dentry_t *d;
416
417 b = fat_block_get(nodep->dev_handle, nodep->index, i);
418 dentries = (i == blocks - 1) ?
419 nodep->size % sizeof(fat_dentry_t) :
420 dps;
421 for (j = 0; j < dentries; j++) {
422 d = ((fat_dentry_t *)b->data) + j;
423 switch (fat_classify_dentry(d)) {
424 case FAT_DENTRY_SKIP:
425 continue;
426 case FAT_DENTRY_LAST:
427 block_put(b);
428 return false;
429 default:
430 case FAT_DENTRY_VALID:
431 block_put(b);
432 return true;
433 }
434 block_put(b);
435 return true;
436 }
437 block_put(b);
438 }
439
440 return false;
441}
442
443static void *fat_root_get(dev_handle_t dev_handle)
444{
445 return fat_node_get(dev_handle, 0, 0);
446}
447
448static char fat_plb_get_char(unsigned pos)
449{
450 return fat_reg.plb_ro[pos % PLB_SIZE];
451}
452
453static bool fat_is_directory(void *node)
454{
455 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
456}
457
458static bool fat_is_file(void *node)
459{
460 return ((fat_node_t *)node)->type == FAT_FILE;
461}
462
463/** libfs operations */
464libfs_ops_t fat_libfs_ops = {
465 .match = fat_match,
466 .node_get = fat_node_get,
467 .node_put = fat_node_put,
468 .create = fat_create,
469 .destroy = fat_destroy,
470 .link = fat_link,
471 .unlink = fat_unlink,
472 .index_get = fat_index_get,
473 .size_get = fat_size_get,
474 .lnkcnt_get = fat_lnkcnt_get,
475 .has_children = fat_has_children,
476 .root_get = fat_root_get,
477 .plb_get_char = fat_plb_get_char,
478 .is_directory = fat_is_directory,
479 .is_file = fat_is_file
480};
481
482void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
483{
484 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
485}
486
487/**
488 * @}
489 */
Note: See TracBrowser for help on using the repository browser.