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

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

Add fs_handle argument to libfs_mount() needed to recognize homogenous mounts.

  • Property mode set to 100644
File size: 28.9 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"
[033ef7d3]39#include "fat_dentry.h"
40#include "fat_fat.h"
[6364d3c]41#include "../../vfs/vfs.h"
[a2aa1dec]42#include <libfs.h>
[fc840d9]43#include <libblock.h>
[be815bc]44#include <ipc/ipc.h>
[7a35204a]45#include <ipc/services.h>
46#include <ipc/devmap.h>
[be815bc]47#include <async.h>
48#include <errno.h>
[a2aa1dec]49#include <string.h>
[776f2e6]50#include <byteorder.h>
[e1e3b26]51#include <libadt/hash_table.h>
52#include <libadt/list.h>
53#include <assert.h>
[d9e9caf]54#include <futex.h>
[7a35204a]55#include <sys/mman.h>
[8d32152]56#include <align.h>
[e1e3b26]57
[b6035ba]58#define FAT_NODE(node) ((node) ? (fat_node_t *) (node)->data : NULL)
59#define FS_NODE(node) ((node) ? (node)->bp : NULL)
60
[add5835]61/** Futex protecting the list of cached free FAT nodes. */
62static futex_t ffn_futex = FUTEX_INITIALIZER;
63
64/** List of cached free FAT nodes. */
65static LIST_INITIALIZE(ffn_head);
[6364d3c]66
[e1e3b26]67static void fat_node_initialize(fat_node_t *node)
[a2aa1dec]68{
[add5835]69 futex_initialize(&node->lock, 1);
[b6035ba]70 node->bp = NULL;
[869e546]71 node->idx = NULL;
[e1e3b26]72 node->type = 0;
73 link_initialize(&node->ffn_link);
74 node->size = 0;
75 node->lnkcnt = 0;
76 node->refcnt = 0;
77 node->dirty = false;
78}
79
[2c4bbcde]80static void fat_node_sync(fat_node_t *node)
[e1e3b26]81{
[7858bc5f]82 block_t *b;
83 fat_bs_t *bs;
[beb17734]84 fat_dentry_t *d;
85 uint16_t bps;
86 unsigned dps;
87
88 assert(node->dirty);
89
[7858bc5f]90 bs = block_bb_get(node->idx->dev_handle);
91 bps = uint16_t_le2host(bs->bps);
[beb17734]92 dps = bps / sizeof(fat_dentry_t);
93
94 /* Read the block that contains the dentry of interest. */
[7858bc5f]95 b = _fat_block_get(bs, node->idx->dev_handle, node->idx->pfc,
[1d8cdb1]96 (node->idx->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
[beb17734]97
98 d = ((fat_dentry_t *)b->data) + (node->idx->pdi % dps);
99
100 d->firstc = host2uint16_t_le(node->firstc);
[a5da446]101 if (node->type == FAT_FILE) {
[beb17734]102 d->size = host2uint32_t_le(node->size);
[a5da446]103 } else if (node->type == FAT_DIRECTORY) {
104 d->attr = FAT_ATTR_SUBDIR;
105 }
106
107 /* TODO: update other fields? (e.g time fields) */
[beb17734]108
109 b->dirty = true; /* need to sync block */
110 block_put(b);
[e1e3b26]111}
112
[9a3d5f0]113static fat_node_t *fat_node_get_new(void)
114{
[b6035ba]115 fs_node_t *fn;
[9a3d5f0]116 fat_node_t *nodep;
117
118 futex_down(&ffn_futex);
119 if (!list_empty(&ffn_head)) {
120 /* Try to use a cached free node structure. */
121 fat_idx_t *idxp_tmp;
122 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
123 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
124 goto skip_cache;
125 idxp_tmp = nodep->idx;
126 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
127 futex_up(&nodep->lock);
128 goto skip_cache;
129 }
130 list_remove(&nodep->ffn_link);
131 futex_up(&ffn_futex);
132 if (nodep->dirty)
133 fat_node_sync(nodep);
134 idxp_tmp->nodep = NULL;
135 futex_up(&nodep->lock);
136 futex_up(&idxp_tmp->lock);
[b6035ba]137 fn = FS_NODE(nodep);
[9a3d5f0]138 } else {
139skip_cache:
140 /* Try to allocate a new node structure. */
141 futex_up(&ffn_futex);
[b6035ba]142 fn = (fs_node_t *)malloc(sizeof(fs_node_t));
143 if (!fn)
144 return NULL;
[9a3d5f0]145 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
[b6035ba]146 if (!nodep) {
147 free(fn);
[9a3d5f0]148 return NULL;
[b6035ba]149 }
[9a3d5f0]150 }
151 fat_node_initialize(nodep);
[83937ccd]152 fs_node_initialize(fn);
[b6035ba]153 fn->data = nodep;
154 nodep->bp = fn;
[9a3d5f0]155
156 return nodep;
157}
158
[add5835]159/** Internal version of fat_node_get().
160 *
161 * @param idxp Locked index structure.
162 */
[b6035ba]163static fat_node_t *fat_node_get_core(fat_idx_t *idxp)
[e1e3b26]164{
[7858bc5f]165 block_t *b;
166 fat_bs_t *bs;
[4573a79]167 fat_dentry_t *d;
[c06dbf9]168 fat_node_t *nodep = NULL;
[4573a79]169 unsigned bps;
[4f1c0b4]170 unsigned spc;
[4573a79]171 unsigned dps;
172
[add5835]173 if (idxp->nodep) {
[4573a79]174 /*
175 * We are lucky.
176 * The node is already instantiated in memory.
177 */
[add5835]178 futex_down(&idxp->nodep->lock);
179 if (!idxp->nodep->refcnt++)
[c06dbf9]180 list_remove(&idxp->nodep->ffn_link);
[add5835]181 futex_up(&idxp->nodep->lock);
182 return idxp->nodep;
[4573a79]183 }
184
185 /*
186 * We must instantiate the node from the file system.
187 */
188
[add5835]189 assert(idxp->pfc);
[4573a79]190
[9a3d5f0]191 nodep = fat_node_get_new();
192 if (!nodep)
193 return NULL;
[4573a79]194
[7858bc5f]195 bs = block_bb_get(idxp->dev_handle);
196 bps = uint16_t_le2host(bs->bps);
[4f1c0b4]197 spc = bs->spc;
[4573a79]198 dps = bps / sizeof(fat_dentry_t);
199
[2c4bbcde]200 /* Read the block that contains the dentry of interest. */
[7858bc5f]201 b = _fat_block_get(bs, idxp->dev_handle, idxp->pfc,
[1d8cdb1]202 (idxp->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
[4573a79]203 assert(b);
204
[add5835]205 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
[2c4bbcde]206 if (d->attr & FAT_ATTR_SUBDIR) {
207 /*
208 * The only directory which does not have this bit set is the
209 * root directory itself. The root directory node is handled
210 * and initialized elsewhere.
211 */
212 nodep->type = FAT_DIRECTORY;
[2ab1023]213 /*
[e2115311]214 * Unfortunately, the 'size' field of the FAT dentry is not
215 * defined for the directory entry type. We must determine the
216 * size of the directory by walking the FAT.
[2ab1023]217 */
[4f1c0b4]218 nodep->size = bps * spc * fat_clusters_get(bs, idxp->dev_handle,
219 uint16_t_le2host(d->firstc));
[2c4bbcde]220 } else {
221 nodep->type = FAT_FILE;
[2ab1023]222 nodep->size = uint32_t_le2host(d->size);
[2c4bbcde]223 }
224 nodep->firstc = uint16_t_le2host(d->firstc);
225 nodep->lnkcnt = 1;
226 nodep->refcnt = 1;
227
228 block_put(b);
229
230 /* Link the idx structure with the node structure. */
[add5835]231 nodep->idx = idxp;
232 idxp->nodep = nodep;
[2c4bbcde]233
234 return nodep;
[a2aa1dec]235}
236
[50e5b25]237/*
238 * Forward declarations of FAT libfs operations.
239 */
[b6035ba]240static fs_node_t *fat_node_get(dev_handle_t, fs_index_t);
241static void fat_node_put(fs_node_t *);
242static fs_node_t *fat_create_node(dev_handle_t, int);
243static int fat_destroy_node(fs_node_t *);
244static int fat_link(fs_node_t *, fs_node_t *, const char *);
[cf95bc0]245static int fat_unlink(fs_node_t *, fs_node_t *, const char *);
[b6035ba]246static fs_node_t *fat_match(fs_node_t *, const char *);
247static fs_index_t fat_index_get(fs_node_t *);
248static size_t fat_size_get(fs_node_t *);
249static unsigned fat_lnkcnt_get(fs_node_t *);
250static bool fat_has_children(fs_node_t *);
251static fs_node_t *fat_root_get(dev_handle_t);
[50e5b25]252static char fat_plb_get_char(unsigned);
[b6035ba]253static bool fat_is_directory(fs_node_t *);
254static bool fat_is_file(fs_node_t *node);
[50e5b25]255
256/*
257 * FAT libfs operations.
258 */
259
[add5835]260/** Instantiate a FAT in-core node. */
[b6035ba]261fs_node_t *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
[add5835]262{
[b6035ba]263 fat_node_t *nodep;
[add5835]264 fat_idx_t *idxp;
265
266 idxp = fat_idx_get_by_index(dev_handle, index);
267 if (!idxp)
268 return NULL;
269 /* idxp->lock held */
[b6035ba]270 nodep = fat_node_get_core(idxp);
[add5835]271 futex_up(&idxp->lock);
[b6035ba]272 return FS_NODE(nodep);
[add5835]273}
274
[b6035ba]275void fat_node_put(fs_node_t *fn)
[06901c6b]276{
[b6035ba]277 fat_node_t *nodep = FAT_NODE(fn);
[6571b78]278 bool destroy = false;
[34b3ce3]279
[add5835]280 futex_down(&nodep->lock);
[34b3ce3]281 if (!--nodep->refcnt) {
[6571b78]282 if (nodep->idx) {
283 futex_down(&ffn_futex);
284 list_append(&nodep->ffn_link, &ffn_head);
285 futex_up(&ffn_futex);
286 } else {
287 /*
288 * The node does not have any index structure associated
289 * with itself. This can only mean that we are releasing
290 * the node after a failed attempt to allocate the index
291 * structure for it.
292 */
293 destroy = true;
294 }
[34b3ce3]295 }
[add5835]296 futex_up(&nodep->lock);
[b6035ba]297 if (destroy) {
298 free(nodep->bp);
299 free(nodep);
300 }
[06901c6b]301}
302
[b6035ba]303fs_node_t *fat_create_node(dev_handle_t dev_handle, int flags)
[80e8482]304{
[6571b78]305 fat_idx_t *idxp;
306 fat_node_t *nodep;
[49df572]307 fat_bs_t *bs;
308 fat_cluster_t mcl, lcl;
309 uint16_t bps;
310 int rc;
311
312 bs = block_bb_get(dev_handle);
313 bps = uint16_t_le2host(bs->bps);
314 if (flags & L_DIRECTORY) {
315 /* allocate a cluster */
316 rc = fat_alloc_clusters(bs, dev_handle, 1, &mcl, &lcl);
317 if (rc != EOK)
318 return NULL;
319 }
[6571b78]320
321 nodep = fat_node_get_new();
[49df572]322 if (!nodep) {
323 fat_free_clusters(bs, dev_handle, mcl);
[6571b78]324 return NULL;
[49df572]325 }
[6571b78]326 idxp = fat_idx_get_new(dev_handle);
327 if (!idxp) {
[49df572]328 fat_free_clusters(bs, dev_handle, mcl);
[b6035ba]329 fat_node_put(FS_NODE(nodep));
[6571b78]330 return NULL;
331 }
332 /* idxp->lock held */
333 if (flags & L_DIRECTORY) {
[49df572]334 int i;
335 block_t *b;
336
337 /*
338 * Populate the new cluster with unused dentries.
339 */
340 for (i = 0; i < bs->spc; i++) {
341 b = _fat_block_get(bs, dev_handle, mcl, i,
342 BLOCK_FLAGS_NOREAD);
343 /* mark all dentries as never-used */
344 memset(b->data, 0, bps);
345 b->dirty = false;
346 block_put(b);
347 }
[6571b78]348 nodep->type = FAT_DIRECTORY;
[49df572]349 nodep->firstc = mcl;
350 nodep->size = bps * bs->spc;
[6571b78]351 } else {
352 nodep->type = FAT_FILE;
[49df572]353 nodep->firstc = FAT_CLST_RES0;
354 nodep->size = 0;
[6571b78]355 }
356 nodep->lnkcnt = 0; /* not linked anywhere */
357 nodep->refcnt = 1;
[49df572]358 nodep->dirty = true;
[6571b78]359
360 nodep->idx = idxp;
361 idxp->nodep = nodep;
362
363 futex_up(&idxp->lock);
[b6035ba]364 return FS_NODE(nodep);
[80e8482]365}
366
[b6035ba]367int fat_destroy_node(fs_node_t *fn)
[80e8482]368{
[b6035ba]369 fat_node_t *nodep = FAT_NODE(fn);
[50e5b25]370 fat_bs_t *bs;
371
372 /*
373 * The node is not reachable from the file system. This means that the
374 * link count should be zero and that the index structure cannot be
375 * found in the position hash. Obviously, we don't need to lock the node
376 * nor its index structure.
377 */
378 assert(nodep->lnkcnt == 0);
379
380 /*
381 * The node may not have any children.
382 */
[b6035ba]383 assert(fat_has_children(fn) == false);
[50e5b25]384
385 bs = block_bb_get(nodep->idx->dev_handle);
386 if (nodep->firstc != FAT_CLST_RES0) {
387 assert(nodep->size);
388 /* Free all clusters allocated to the node. */
389 fat_free_clusters(bs, nodep->idx->dev_handle, nodep->firstc);
390 }
391
392 fat_idx_destroy(nodep->idx);
[b6035ba]393 free(nodep->bp);
[50e5b25]394 free(nodep);
395 return EOK;
[80e8482]396}
397
[b6035ba]398int fat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[80e8482]399{
[b6035ba]400 fat_node_t *parentp = FAT_NODE(pfn);
401 fat_node_t *childp = FAT_NODE(cfn);
[0fdd6bb]402 fat_dentry_t *d;
403 fat_bs_t *bs;
404 block_t *b;
405 int i, j;
406 uint16_t bps;
407 unsigned dps;
408 unsigned blocks;
[e32b65a]409 fat_cluster_t mcl, lcl;
410 int rc;
[0fdd6bb]411
412 futex_down(&childp->lock);
413 if (childp->lnkcnt == 1) {
414 /*
415 * On FAT, we don't support multiple hard links.
416 */
417 futex_up(&childp->lock);
418 return EMLINK;
419 }
420 assert(childp->lnkcnt == 0);
421 futex_up(&childp->lock);
422
423 if (!fat_dentry_name_verify(name)) {
424 /*
425 * Attempt to create unsupported name.
426 */
427 return ENOTSUP;
428 }
429
430 /*
431 * Get us an unused parent node's dentry or grow the parent and allocate
432 * a new one.
433 */
434
435 futex_down(&parentp->idx->lock);
436 bs = block_bb_get(parentp->idx->dev_handle);
437 bps = uint16_t_le2host(bs->bps);
438 dps = bps / sizeof(fat_dentry_t);
439
440 blocks = parentp->size / bps;
441
442 for (i = 0; i < blocks; i++) {
443 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
444 for (j = 0; j < dps; j++) {
445 d = ((fat_dentry_t *)b->data) + j;
446 switch (fat_classify_dentry(d)) {
447 case FAT_DENTRY_SKIP:
448 case FAT_DENTRY_VALID:
449 /* skipping used and meta entries */
450 continue;
451 case FAT_DENTRY_FREE:
452 case FAT_DENTRY_LAST:
453 /* found an empty slot */
454 goto hit;
455 }
456 }
457 block_put(b);
458 }
[699743c]459 j = 0;
[0fdd6bb]460
461 /*
462 * We need to grow the parent in order to create a new unused dentry.
463 */
[e32b65a]464 if (parentp->idx->pfc == FAT_CLST_ROOT) {
465 /* Can't grow the root directory. */
466 futex_up(&parentp->idx->lock);
467 return ENOSPC;
468 }
469 rc = fat_alloc_clusters(bs, parentp->idx->dev_handle, 1, &mcl, &lcl);
470 if (rc != EOK) {
471 futex_up(&parentp->idx->lock);
472 return rc;
473 }
474 fat_append_clusters(bs, parentp, mcl);
475 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NOREAD);
476 d = (fat_dentry_t *)b->data;
477 /*
478 * Clear all dentries in the block except for the first one (the first
479 * dentry will be cleared in the next step).
480 */
481 memset(d + 1, 0, bps - sizeof(fat_dentry_t));
[0fdd6bb]482
483hit:
484 /*
485 * At this point we only establish the link between the parent and the
486 * child. The dentry, except of the name and the extension, will remain
[e32b65a]487 * uninitialized until the corresponding node is synced. Thus the valid
488 * dentry data is kept in the child node structure.
[0fdd6bb]489 */
490 memset(d, 0, sizeof(fat_dentry_t));
491 fat_dentry_name_set(d, name);
492 b->dirty = true; /* need to sync block */
493 block_put(b);
494 futex_up(&parentp->idx->lock);
495
496 futex_down(&childp->idx->lock);
[1baec4b]497
498 /*
499 * If possible, create the Sub-directory Identifier Entry and the
500 * Sub-directory Parent Pointer Entry (i.e. "." and ".."). These entries
501 * are not mandatory according to Standard ECMA-107 and HelenOS VFS does
502 * not use them anyway, so this is rather a sign of our good will.
503 */
504 b = fat_block_get(bs, childp, 0, BLOCK_FLAGS_NONE);
505 d = (fat_dentry_t *)b->data;
506 if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
[92fd52d7]507 str_cmp(d->name, FAT_NAME_DOT) == 0) {
[1baec4b]508 memset(d, 0, sizeof(fat_dentry_t));
[6eb2e96]509 str_cpy(d->name, 8, FAT_NAME_DOT);
510 str_cpy(d->ext, 3, FAT_EXT_PAD);
[1baec4b]511 d->attr = FAT_ATTR_SUBDIR;
512 d->firstc = host2uint16_t_le(childp->firstc);
513 /* TODO: initialize also the date/time members. */
514 }
515 d++;
516 if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
[92fd52d7]517 str_cmp(d->name, FAT_NAME_DOT_DOT) == 0) {
[1baec4b]518 memset(d, 0, sizeof(fat_dentry_t));
[6eb2e96]519 str_cpy(d->name, 8, FAT_NAME_DOT_DOT);
520 str_cpy(d->ext, 3, FAT_EXT_PAD);
[1baec4b]521 d->attr = FAT_ATTR_SUBDIR;
522 d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
523 host2uint16_t_le(FAT_CLST_RES0) :
524 host2uint16_t_le(parentp->firstc);
525 /* TODO: initialize also the date/time members. */
526 }
527 b->dirty = true; /* need to sync block */
528 block_put(b);
529
[0fdd6bb]530 childp->idx->pfc = parentp->firstc;
531 childp->idx->pdi = i * dps + j;
532 futex_up(&childp->idx->lock);
533
534 futex_down(&childp->lock);
535 childp->lnkcnt = 1;
536 childp->dirty = true; /* need to sync node */
537 futex_up(&childp->lock);
538
539 /*
540 * Hash in the index structure into the position hash.
541 */
542 fat_idx_hashin(childp->idx);
543
544 return EOK;
[80e8482]545}
546
[cf95bc0]547int fat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
[80e8482]548{
[b6035ba]549 fat_node_t *parentp = FAT_NODE(pfn);
550 fat_node_t *childp = FAT_NODE(cfn);
[a31c1ccf]551 fat_bs_t *bs;
552 fat_dentry_t *d;
553 uint16_t bps;
554 block_t *b;
555
[770d281]556 if (!parentp)
557 return EBUSY;
[0be3e8b]558
559 if (fat_has_children(cfn))
560 return ENOTEMPTY;
[770d281]561
[a31c1ccf]562 futex_down(&parentp->lock);
563 futex_down(&childp->lock);
564 assert(childp->lnkcnt == 1);
565 futex_down(&childp->idx->lock);
566 bs = block_bb_get(childp->idx->dev_handle);
567 bps = uint16_t_le2host(bs->bps);
568
569 b = _fat_block_get(bs, childp->idx->dev_handle, childp->idx->pfc,
570 (childp->idx->pdi * sizeof(fat_dentry_t)) / bps,
571 BLOCK_FLAGS_NONE);
572 d = (fat_dentry_t *)b->data +
573 (childp->idx->pdi % (bps / sizeof(fat_dentry_t)));
574 /* mark the dentry as not-currently-used */
575 d->name[0] = FAT_DENTRY_ERASED;
576 b->dirty = true; /* need to sync block */
577 block_put(b);
578
579 /* remove the index structure from the position hash */
580 fat_idx_hashout(childp->idx);
581 /* clear position information */
582 childp->idx->pfc = FAT_CLST_RES0;
583 childp->idx->pdi = 0;
584 futex_up(&childp->idx->lock);
585 childp->lnkcnt = 0;
586 childp->dirty = true;
587 futex_up(&childp->lock);
588 futex_up(&parentp->lock);
589
590 return EOK;
[80e8482]591}
592
[b6035ba]593fs_node_t *fat_match(fs_node_t *pfn, const char *component)
[a2aa1dec]594{
[7858bc5f]595 fat_bs_t *bs;
[b6035ba]596 fat_node_t *parentp = FAT_NODE(pfn);
[a2aa1dec]597 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
[79dbc3e]598 unsigned i, j;
[5446bee0]599 unsigned bps; /* bytes per sector */
[79dbc3e]600 unsigned dps; /* dentries per sector */
601 unsigned blocks;
[a2aa1dec]602 fat_dentry_t *d;
[7858bc5f]603 block_t *b;
[79dbc3e]604
[e811bde]605 futex_down(&parentp->idx->lock);
[7858bc5f]606 bs = block_bb_get(parentp->idx->dev_handle);
607 bps = uint16_t_le2host(bs->bps);
[5446bee0]608 dps = bps / sizeof(fat_dentry_t);
[b0247bac]609 blocks = parentp->size / bps;
[79dbc3e]610 for (i = 0; i < blocks; i++) {
[1d8cdb1]611 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
[b0247bac]612 for (j = 0; j < dps; j++) {
[79dbc3e]613 d = ((fat_dentry_t *)b->data) + j;
[32fb10ed]614 switch (fat_classify_dentry(d)) {
615 case FAT_DENTRY_SKIP:
[0fdd6bb]616 case FAT_DENTRY_FREE:
[79dbc3e]617 continue;
[32fb10ed]618 case FAT_DENTRY_LAST:
[79dbc3e]619 block_put(b);
[e811bde]620 futex_up(&parentp->idx->lock);
[79dbc3e]621 return NULL;
[32fb10ed]622 default:
623 case FAT_DENTRY_VALID:
[0fdd6bb]624 fat_dentry_name_get(d, name);
[32fb10ed]625 break;
[79dbc3e]626 }
[14c331a]627 if (fat_dentry_namecmp(name, component) == 0) {
[79dbc3e]628 /* hit */
[b6035ba]629 fat_node_t *nodep;
[e811bde]630 /*
631 * Assume tree hierarchy for locking. We
632 * already have the parent and now we are going
633 * to lock the child. Never lock in the oposite
634 * order.
635 */
[4797132]636 fat_idx_t *idx = fat_idx_get_by_pos(
[5a324099]637 parentp->idx->dev_handle, parentp->firstc,
[869e546]638 i * dps + j);
[e811bde]639 futex_up(&parentp->idx->lock);
[4797132]640 if (!idx) {
641 /*
642 * Can happen if memory is low or if we
643 * run out of 32-bit indices.
644 */
645 block_put(b);
646 return NULL;
647 }
[b6035ba]648 nodep = fat_node_get_core(idx);
[add5835]649 futex_up(&idx->lock);
[79dbc3e]650 block_put(b);
[b6035ba]651 return FS_NODE(nodep);
[79dbc3e]652 }
[9119d25]653 }
[79dbc3e]654 block_put(b);
[9119d25]655 }
[cb682eb]656
[e811bde]657 futex_up(&parentp->idx->lock);
[a2aa1dec]658 return NULL;
[6364d3c]659}
660
[b6035ba]661fs_index_t fat_index_get(fs_node_t *fn)
[e1e3b26]662{
[b6035ba]663 return FAT_NODE(fn)->idx->index;
[e1e3b26]664}
665
[b6035ba]666size_t fat_size_get(fs_node_t *fn)
[e1e3b26]667{
[b6035ba]668 return FAT_NODE(fn)->size;
[e1e3b26]669}
670
[b6035ba]671unsigned fat_lnkcnt_get(fs_node_t *fn)
[e1e3b26]672{
[b6035ba]673 return FAT_NODE(fn)->lnkcnt;
[e1e3b26]674}
675
[b6035ba]676bool fat_has_children(fs_node_t *fn)
[32fb10ed]677{
[7858bc5f]678 fat_bs_t *bs;
[b6035ba]679 fat_node_t *nodep = FAT_NODE(fn);
[32fb10ed]680 unsigned bps;
681 unsigned dps;
682 unsigned blocks;
[7858bc5f]683 block_t *b;
[32fb10ed]684 unsigned i, j;
685
686 if (nodep->type != FAT_DIRECTORY)
687 return false;
[b0247bac]688
[add5835]689 futex_down(&nodep->idx->lock);
[7858bc5f]690 bs = block_bb_get(nodep->idx->dev_handle);
691 bps = uint16_t_le2host(bs->bps);
[32fb10ed]692 dps = bps / sizeof(fat_dentry_t);
693
[b0247bac]694 blocks = nodep->size / bps;
[32fb10ed]695
696 for (i = 0; i < blocks; i++) {
697 fat_dentry_t *d;
698
[1d8cdb1]699 b = fat_block_get(bs, nodep, i, BLOCK_FLAGS_NONE);
[b0247bac]700 for (j = 0; j < dps; j++) {
[32fb10ed]701 d = ((fat_dentry_t *)b->data) + j;
702 switch (fat_classify_dentry(d)) {
703 case FAT_DENTRY_SKIP:
[0fdd6bb]704 case FAT_DENTRY_FREE:
[32fb10ed]705 continue;
706 case FAT_DENTRY_LAST:
707 block_put(b);
[add5835]708 futex_up(&nodep->idx->lock);
[32fb10ed]709 return false;
710 default:
711 case FAT_DENTRY_VALID:
712 block_put(b);
[add5835]713 futex_up(&nodep->idx->lock);
[32fb10ed]714 return true;
715 }
716 block_put(b);
[add5835]717 futex_up(&nodep->idx->lock);
[32fb10ed]718 return true;
719 }
720 block_put(b);
721 }
722
[add5835]723 futex_up(&nodep->idx->lock);
[32fb10ed]724 return false;
725}
726
[b6035ba]727fs_node_t *fat_root_get(dev_handle_t dev_handle)
[74ea3c6]728{
[689f036]729 return fat_node_get(dev_handle, 0);
[74ea3c6]730}
731
[50e5b25]732char fat_plb_get_char(unsigned pos)
[74ea3c6]733{
734 return fat_reg.plb_ro[pos % PLB_SIZE];
735}
736
[b6035ba]737bool fat_is_directory(fs_node_t *fn)
[e1e3b26]738{
[b6035ba]739 return FAT_NODE(fn)->type == FAT_DIRECTORY;
[e1e3b26]740}
741
[b6035ba]742bool fat_is_file(fs_node_t *fn)
[e1e3b26]743{
[b6035ba]744 return FAT_NODE(fn)->type == FAT_FILE;
[e1e3b26]745}
746
[a2aa1dec]747/** libfs operations */
748libfs_ops_t fat_libfs_ops = {
749 .match = fat_match,
750 .node_get = fat_node_get,
[06901c6b]751 .node_put = fat_node_put,
[6571b78]752 .create = fat_create_node,
753 .destroy = fat_destroy_node,
[80e8482]754 .link = fat_link,
755 .unlink = fat_unlink,
[e1e3b26]756 .index_get = fat_index_get,
757 .size_get = fat_size_get,
758 .lnkcnt_get = fat_lnkcnt_get,
[32fb10ed]759 .has_children = fat_has_children,
[74ea3c6]760 .root_get = fat_root_get,
761 .plb_get_char = fat_plb_get_char,
[e1e3b26]762 .is_directory = fat_is_directory,
763 .is_file = fat_is_file
[a2aa1dec]764};
765
[0013b9ce]766/*
767 * VFS operations.
768 */
769
[cde485d]770void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
771{
772 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
[7858bc5f]773 fat_bs_t *bs;
[7a35204a]774 uint16_t bps;
[689f036]775 uint16_t rde;
[cde485d]776 int rc;
777
[594303b]778 /* accept the mount options */
779 ipc_callid_t callid;
780 size_t size;
781 if (!ipc_data_write_receive(&callid, &size)) {
782 ipc_answer_0(callid, EINVAL);
783 ipc_answer_0(rid, EINVAL);
784 return;
785 }
786 char *opts = malloc(size + 1);
787 if (!opts) {
788 ipc_answer_0(callid, ENOMEM);
789 ipc_answer_0(rid, ENOMEM);
790 return;
791 }
792 ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
793 if (retval != EOK) {
794 ipc_answer_0(rid, retval);
795 free(opts);
796 return;
797 }
798 opts[size] = '\0';
799
[7858bc5f]800 /* initialize libblock */
[6284978]801 rc = block_init(dev_handle, BS_SIZE);
[7a35204a]802 if (rc != EOK) {
[6284978]803 ipc_answer_0(rid, rc);
804 return;
805 }
806
807 /* prepare the boot block */
808 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
809 if (rc != EOK) {
810 block_fini(dev_handle);
811 ipc_answer_0(rid, rc);
[7a35204a]812 return;
813 }
814
[7858bc5f]815 /* get the buffer with the boot sector */
816 bs = block_bb_get(dev_handle);
817
[689f036]818 /* Read the number of root directory entries. */
[7858bc5f]819 bps = uint16_t_le2host(bs->bps);
820 rde = uint16_t_le2host(bs->root_ent_max);
[689f036]821
[7a35204a]822 if (bps != BS_SIZE) {
[7858bc5f]823 block_fini(dev_handle);
[7a35204a]824 ipc_answer_0(rid, ENOTSUP);
825 return;
826 }
827
[f1ba5d6]828 /* Initialize the block cache */
829 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
830 if (rc != EOK) {
831 block_fini(dev_handle);
832 ipc_answer_0(rid, rc);
833 return;
834 }
835
[cde485d]836 rc = fat_idx_init_by_dev_handle(dev_handle);
837 if (rc != EOK) {
[7858bc5f]838 block_fini(dev_handle);
[cde485d]839 ipc_answer_0(rid, rc);
840 return;
841 }
842
[689f036]843 /* Initialize the root node. */
[b6035ba]844 fs_node_t *rfn = (fs_node_t *)malloc(sizeof(fs_node_t));
845 if (!rfn) {
846 block_fini(dev_handle);
847 fat_idx_fini_by_dev_handle(dev_handle);
848 ipc_answer_0(rid, ENOMEM);
849 return;
850 }
[83937ccd]851 fs_node_initialize(rfn);
[689f036]852 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
853 if (!rootp) {
[b6035ba]854 free(rfn);
[7858bc5f]855 block_fini(dev_handle);
[689f036]856 fat_idx_fini_by_dev_handle(dev_handle);
857 ipc_answer_0(rid, ENOMEM);
858 return;
859 }
860 fat_node_initialize(rootp);
861
862 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
863 if (!ridxp) {
[b6035ba]864 free(rfn);
[689f036]865 free(rootp);
[b6035ba]866 block_fini(dev_handle);
[689f036]867 fat_idx_fini_by_dev_handle(dev_handle);
868 ipc_answer_0(rid, ENOMEM);
869 return;
870 }
871 assert(ridxp->index == 0);
872 /* ridxp->lock held */
873
874 rootp->type = FAT_DIRECTORY;
875 rootp->firstc = FAT_CLST_ROOT;
876 rootp->refcnt = 1;
[5ab597d]877 rootp->lnkcnt = 0; /* FS root is not linked */
[689f036]878 rootp->size = rde * sizeof(fat_dentry_t);
879 rootp->idx = ridxp;
880 ridxp->nodep = rootp;
[b6035ba]881 rootp->bp = rfn;
882 rfn->data = rootp;
[689f036]883
884 futex_up(&ridxp->lock);
885
[5ab597d]886 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
[cde485d]887}
888
889void fat_mount(ipc_callid_t rid, ipc_call_t *request)
890{
[16d17ca]891 libfs_mount(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
[cde485d]892}
893
[be815bc]894void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
895{
[a2aa1dec]896 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
[be815bc]897}
898
[4bf40f6]899void fat_read(ipc_callid_t rid, ipc_call_t *request)
900{
901 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
902 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
903 off_t pos = (off_t)IPC_GET_ARG3(*request);
[b6035ba]904 fs_node_t *fn = fat_node_get(dev_handle, index);
905 fat_node_t *nodep;
[7858bc5f]906 fat_bs_t *bs;
[cb682eb]907 uint16_t bps;
[79d031b]908 size_t bytes;
[7858bc5f]909 block_t *b;
[79d031b]910
[b6035ba]911 if (!fn) {
[4bf40f6]912 ipc_answer_0(rid, ENOENT);
913 return;
914 }
[b6035ba]915 nodep = FAT_NODE(fn);
[4bf40f6]916
917 ipc_callid_t callid;
918 size_t len;
[6808614]919 if (!ipc_data_read_receive(&callid, &len)) {
[b6035ba]920 fat_node_put(fn);
[4bf40f6]921 ipc_answer_0(callid, EINVAL);
922 ipc_answer_0(rid, EINVAL);
923 return;
924 }
925
[7858bc5f]926 bs = block_bb_get(dev_handle);
927 bps = uint16_t_le2host(bs->bps);
[cb682eb]928
[4bf40f6]929 if (nodep->type == FAT_FILE) {
[ddd1219]930 /*
931 * Our strategy for regular file reads is to read one block at
932 * most and make use of the possibility to return less data than
933 * requested. This keeps the code very simple.
934 */
[0d974d8]935 if (pos >= nodep->size) {
[7d861950]936 /* reading beyond the EOF */
937 bytes = 0;
[0d974d8]938 (void) ipc_data_read_finalize(callid, NULL, 0);
939 } else {
940 bytes = min(len, bps - pos % bps);
941 bytes = min(bytes, nodep->size - pos);
[1d8cdb1]942 b = fat_block_get(bs, nodep, pos / bps,
943 BLOCK_FLAGS_NONE);
[0d974d8]944 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
945 bytes);
946 block_put(b);
947 }
[4bf40f6]948 } else {
[ddd1219]949 unsigned bnum;
950 off_t spos = pos;
951 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
952 fat_dentry_t *d;
953
[4bf40f6]954 assert(nodep->type == FAT_DIRECTORY);
[ddd1219]955 assert(nodep->size % bps == 0);
956 assert(bps % sizeof(fat_dentry_t) == 0);
957
958 /*
959 * Our strategy for readdir() is to use the position pointer as
960 * an index into the array of all dentries. On entry, it points
961 * to the first unread dentry. If we skip any dentries, we bump
962 * the position pointer accordingly.
963 */
964 bnum = (pos * sizeof(fat_dentry_t)) / bps;
965 while (bnum < nodep->size / bps) {
966 off_t o;
967
[1d8cdb1]968 b = fat_block_get(bs, nodep, bnum, BLOCK_FLAGS_NONE);
[ddd1219]969 for (o = pos % (bps / sizeof(fat_dentry_t));
970 o < bps / sizeof(fat_dentry_t);
971 o++, pos++) {
972 d = ((fat_dentry_t *)b->data) + o;
973 switch (fat_classify_dentry(d)) {
974 case FAT_DENTRY_SKIP:
[0fdd6bb]975 case FAT_DENTRY_FREE:
[ddd1219]976 continue;
977 case FAT_DENTRY_LAST:
978 block_put(b);
979 goto miss;
980 default:
981 case FAT_DENTRY_VALID:
[0fdd6bb]982 fat_dentry_name_get(d, name);
[ddd1219]983 block_put(b);
984 goto hit;
985 }
986 }
987 block_put(b);
988 bnum++;
989 }
990miss:
[b6035ba]991 fat_node_put(fn);
[ddd1219]992 ipc_answer_0(callid, ENOENT);
993 ipc_answer_1(rid, ENOENT, 0);
[4bf40f6]994 return;
[ddd1219]995hit:
[92fd52d7]996 (void) ipc_data_read_finalize(callid, name, str_size(name) + 1);
[ddd1219]997 bytes = (pos - spos) + 1;
[4bf40f6]998 }
999
[b6035ba]1000 fat_node_put(fn);
[79d031b]1001 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
[4bf40f6]1002}
1003
[c947dda]1004void fat_write(ipc_callid_t rid, ipc_call_t *request)
1005{
[8d32152]1006 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1007 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1008 off_t pos = (off_t)IPC_GET_ARG3(*request);
[b6035ba]1009 fs_node_t *fn = fat_node_get(dev_handle, index);
1010 fat_node_t *nodep;
[7858bc5f]1011 fat_bs_t *bs;
[8d32152]1012 size_t bytes;
[7858bc5f]1013 block_t *b;
[8d32152]1014 uint16_t bps;
1015 unsigned spc;
[913a821c]1016 unsigned bpc; /* bytes per cluster */
[b4b7187]1017 off_t boundary;
[1d8cdb1]1018 int flags = BLOCK_FLAGS_NONE;
[8d32152]1019
[b6035ba]1020 if (!fn) {
[8d32152]1021 ipc_answer_0(rid, ENOENT);
1022 return;
1023 }
[b6035ba]1024 nodep = FAT_NODE(fn);
[8d32152]1025
1026 ipc_callid_t callid;
1027 size_t len;
1028 if (!ipc_data_write_receive(&callid, &len)) {
[b6035ba]1029 fat_node_put(fn);
[8d32152]1030 ipc_answer_0(callid, EINVAL);
1031 ipc_answer_0(rid, EINVAL);
1032 return;
1033 }
1034
[913a821c]1035 bs = block_bb_get(dev_handle);
1036 bps = uint16_t_le2host(bs->bps);
1037 spc = bs->spc;
1038 bpc = bps * spc;
1039
[8d32152]1040 /*
1041 * In all scenarios, we will attempt to write out only one block worth
1042 * of data at maximum. There might be some more efficient approaches,
1043 * but this one greatly simplifies fat_write(). Note that we can afford
1044 * to do this because the client must be ready to handle the return
1045 * value signalizing a smaller number of bytes written.
1046 */
1047 bytes = min(len, bps - pos % bps);
[1d8cdb1]1048 if (bytes == bps)
1049 flags |= BLOCK_FLAGS_NOREAD;
[8d32152]1050
[913a821c]1051 boundary = ROUND_UP(nodep->size, bpc);
[b4b7187]1052 if (pos < boundary) {
[8d32152]1053 /*
1054 * This is the easier case - we are either overwriting already
1055 * existing contents or writing behind the EOF, but still within
1056 * the limits of the last cluster. The node size may grow to the
1057 * next block size boundary.
1058 */
[7858bc5f]1059 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
[1d8cdb1]1060 b = fat_block_get(bs, nodep, pos / bps, flags);
[8d32152]1061 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1062 bytes);
1063 b->dirty = true; /* need to sync block */
[6f2dfd1]1064 block_put(b);
[8d32152]1065 if (pos + bytes > nodep->size) {
1066 nodep->size = pos + bytes;
1067 nodep->dirty = true; /* need to sync node */
1068 }
[ac49f5d1]1069 ipc_answer_2(rid, EOK, bytes, nodep->size);
[b6035ba]1070 fat_node_put(fn);
[8d32152]1071 return;
1072 } else {
1073 /*
1074 * This is the more difficult case. We must allocate new
1075 * clusters for the node and zero them out.
1076 */
[6f2dfd1]1077 int status;
[8d32152]1078 unsigned nclsts;
[8334a427]1079 fat_cluster_t mcl, lcl;
1080
[913a821c]1081 nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
[6f2dfd1]1082 /* create an independent chain of nclsts clusters in all FATs */
[913a821c]1083 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
[6f2dfd1]1084 if (status != EOK) {
1085 /* could not allocate a chain of nclsts clusters */
[b6035ba]1086 fat_node_put(fn);
[6f2dfd1]1087 ipc_answer_0(callid, status);
1088 ipc_answer_0(rid, status);
1089 return;
1090 }
1091 /* zero fill any gaps */
[7858bc5f]1092 fat_fill_gap(bs, nodep, mcl, pos);
[1d8cdb1]1093 b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
1094 flags);
[6f2dfd1]1095 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
1096 bytes);
[b4b7187]1097 b->dirty = true; /* need to sync block */
[6f2dfd1]1098 block_put(b);
1099 /*
1100 * Append the cluster chain starting in mcl to the end of the
1101 * node's cluster chain.
1102 */
[7858bc5f]1103 fat_append_clusters(bs, nodep, mcl);
[6f2dfd1]1104 nodep->size = pos + bytes;
[b4b7187]1105 nodep->dirty = true; /* need to sync node */
[ac49f5d1]1106 ipc_answer_2(rid, EOK, bytes, nodep->size);
[b6035ba]1107 fat_node_put(fn);
[6f2dfd1]1108 return;
[8d32152]1109 }
[c947dda]1110}
1111
[6c71a1f]1112void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
1113{
[8334a427]1114 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1115 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1116 size_t size = (off_t)IPC_GET_ARG3(*request);
[b6035ba]1117 fs_node_t *fn = fat_node_get(dev_handle, index);
1118 fat_node_t *nodep;
[913a821c]1119 fat_bs_t *bs;
1120 uint16_t bps;
1121 uint8_t spc;
1122 unsigned bpc; /* bytes per cluster */
[8334a427]1123 int rc;
1124
[b6035ba]1125 if (!fn) {
[8334a427]1126 ipc_answer_0(rid, ENOENT);
1127 return;
1128 }
[b6035ba]1129 nodep = FAT_NODE(fn);
[8334a427]1130
[913a821c]1131 bs = block_bb_get(dev_handle);
1132 bps = uint16_t_le2host(bs->bps);
1133 spc = bs->spc;
1134 bpc = bps * spc;
1135
[8334a427]1136 if (nodep->size == size) {
1137 rc = EOK;
1138 } else if (nodep->size < size) {
1139 /*
[913a821c]1140 * The standard says we have the freedom to grow the node.
[8334a427]1141 * For now, we simply return an error.
1142 */
1143 rc = EINVAL;
[913a821c]1144 } else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
1145 /*
1146 * The node will be shrunk, but no clusters will be deallocated.
1147 */
1148 nodep->size = size;
1149 nodep->dirty = true; /* need to sync node */
1150 rc = EOK;
[8334a427]1151 } else {
1152 /*
[913a821c]1153 * The node will be shrunk, clusters will be deallocated.
[8334a427]1154 */
[913a821c]1155 if (size == 0) {
1156 fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
1157 } else {
1158 fat_cluster_t lastc;
1159 (void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
1160 &lastc, (size - 1) / bpc);
1161 fat_chop_clusters(bs, nodep, lastc);
1162 }
1163 nodep->size = size;
1164 nodep->dirty = true; /* need to sync node */
1165 rc = EOK;
[8334a427]1166 }
[b6035ba]1167 fat_node_put(fn);
[8334a427]1168 ipc_answer_0(rid, rc);
1169 return;
[6c71a1f]1170}
1171
[50e5b25]1172void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
1173{
1174 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
1175 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
1176 int rc;
1177
[b6035ba]1178 fs_node_t *fn = fat_node_get(dev_handle, index);
1179 if (!fn) {
[50e5b25]1180 ipc_answer_0(rid, ENOENT);
1181 return;
1182 }
1183
[b6035ba]1184 rc = fat_destroy_node(fn);
[50e5b25]1185 ipc_answer_0(rid, rc);
1186}
1187
[be815bc]1188/**
1189 * @}
1190 */
Note: See TracBrowser for help on using the repository browser.