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

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

Add implementation of fat_destroy_node() and fat_destroy().

  • Property mode set to 100644
File size: 21.9 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 "fat_dentry.h"
40#include "fat_fat.h"
41#include "../../vfs/vfs.h"
42#include <libfs.h>
43#include <libblock.h>
44#include <ipc/ipc.h>
45#include <ipc/services.h>
46#include <ipc/devmap.h>
47#include <async.h>
48#include <errno.h>
49#include <string.h>
50#include <byteorder.h>
51#include <libadt/hash_table.h>
52#include <libadt/list.h>
53#include <assert.h>
54#include <futex.h>
55#include <sys/mman.h>
56#include <align.h>
57
58/** Futex protecting the list of cached free FAT nodes. */
59static futex_t ffn_futex = FUTEX_INITIALIZER;
60
61/** List of cached free FAT nodes. */
62static LIST_INITIALIZE(ffn_head);
63
64static void fat_node_initialize(fat_node_t *node)
65{
66 futex_initialize(&node->lock, 1);
67 node->idx = NULL;
68 node->type = 0;
69 link_initialize(&node->ffn_link);
70 node->size = 0;
71 node->lnkcnt = 0;
72 node->refcnt = 0;
73 node->dirty = false;
74}
75
76static void fat_node_sync(fat_node_t *node)
77{
78 block_t *b;
79 fat_bs_t *bs;
80 fat_dentry_t *d;
81 uint16_t bps;
82 unsigned dps;
83
84 assert(node->dirty);
85
86 bs = block_bb_get(node->idx->dev_handle);
87 bps = uint16_t_le2host(bs->bps);
88 dps = bps / sizeof(fat_dentry_t);
89
90 /* Read the block that contains the dentry of interest. */
91 b = _fat_block_get(bs, node->idx->dev_handle, node->idx->pfc,
92 (node->idx->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
93
94 d = ((fat_dentry_t *)b->data) + (node->idx->pdi % dps);
95
96 d->firstc = host2uint16_t_le(node->firstc);
97 if (node->type == FAT_FILE)
98 d->size = host2uint32_t_le(node->size);
99 /* TODO: update other fields? (e.g time fields, attr field) */
100
101 b->dirty = true; /* need to sync block */
102 block_put(b);
103}
104
105static fat_node_t *fat_node_get_new(void)
106{
107 fat_node_t *nodep;
108
109 futex_down(&ffn_futex);
110 if (!list_empty(&ffn_head)) {
111 /* Try to use a cached free node structure. */
112 fat_idx_t *idxp_tmp;
113 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
114 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
115 goto skip_cache;
116 idxp_tmp = nodep->idx;
117 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
118 futex_up(&nodep->lock);
119 goto skip_cache;
120 }
121 list_remove(&nodep->ffn_link);
122 futex_up(&ffn_futex);
123 if (nodep->dirty)
124 fat_node_sync(nodep);
125 idxp_tmp->nodep = NULL;
126 futex_up(&nodep->lock);
127 futex_up(&idxp_tmp->lock);
128 } else {
129skip_cache:
130 /* Try to allocate a new node structure. */
131 futex_up(&ffn_futex);
132 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
133 if (!nodep)
134 return NULL;
135 }
136 fat_node_initialize(nodep);
137
138 return nodep;
139}
140
141/** Internal version of fat_node_get().
142 *
143 * @param idxp Locked index structure.
144 */
145static void *fat_node_get_core(fat_idx_t *idxp)
146{
147 block_t *b;
148 fat_bs_t *bs;
149 fat_dentry_t *d;
150 fat_node_t *nodep = NULL;
151 unsigned bps;
152 unsigned spc;
153 unsigned dps;
154
155 if (idxp->nodep) {
156 /*
157 * We are lucky.
158 * The node is already instantiated in memory.
159 */
160 futex_down(&idxp->nodep->lock);
161 if (!idxp->nodep->refcnt++)
162 list_remove(&idxp->nodep->ffn_link);
163 futex_up(&idxp->nodep->lock);
164 return idxp->nodep;
165 }
166
167 /*
168 * We must instantiate the node from the file system.
169 */
170
171 assert(idxp->pfc);
172
173 nodep = fat_node_get_new();
174 if (!nodep)
175 return NULL;
176
177 bs = block_bb_get(idxp->dev_handle);
178 bps = uint16_t_le2host(bs->bps);
179 spc = bs->spc;
180 dps = bps / sizeof(fat_dentry_t);
181
182 /* Read the block that contains the dentry of interest. */
183 b = _fat_block_get(bs, idxp->dev_handle, idxp->pfc,
184 (idxp->pdi * sizeof(fat_dentry_t)) / bps, BLOCK_FLAGS_NONE);
185 assert(b);
186
187 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
188 if (d->attr & FAT_ATTR_SUBDIR) {
189 /*
190 * The only directory which does not have this bit set is the
191 * root directory itself. The root directory node is handled
192 * and initialized elsewhere.
193 */
194 nodep->type = FAT_DIRECTORY;
195 /*
196 * Unfortunately, the 'size' field of the FAT dentry is not
197 * defined for the directory entry type. We must determine the
198 * size of the directory by walking the FAT.
199 */
200 nodep->size = bps * spc * fat_clusters_get(bs, idxp->dev_handle,
201 uint16_t_le2host(d->firstc));
202 } else {
203 nodep->type = FAT_FILE;
204 nodep->size = uint32_t_le2host(d->size);
205 }
206 nodep->firstc = uint16_t_le2host(d->firstc);
207 nodep->lnkcnt = 1;
208 nodep->refcnt = 1;
209
210 block_put(b);
211
212 /* Link the idx structure with the node structure. */
213 nodep->idx = idxp;
214 idxp->nodep = nodep;
215
216 return nodep;
217}
218
219/*
220 * Forward declarations of FAT libfs operations.
221 */
222static void *fat_node_get(dev_handle_t, fs_index_t);
223static void fat_node_put(void *);
224static void *fat_create_node(dev_handle_t, int);
225static int fat_destroy_node(void *);
226static bool fat_link(void *, void *, const char *);
227static int fat_unlink(void *, void *);
228static void *fat_match(void *, const char *);
229static fs_index_t fat_index_get(void *);
230static size_t fat_size_get(void *);
231static unsigned fat_lnkcnt_get(void *);
232static bool fat_has_children(void *);
233static void *fat_root_get(dev_handle_t);
234static char fat_plb_get_char(unsigned);
235static bool fat_is_directory(void *);
236static bool fat_is_file(void *node);
237
238/*
239 * FAT libfs operations.
240 */
241
242/** Instantiate a FAT in-core node. */
243void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
244{
245 void *node;
246 fat_idx_t *idxp;
247
248 idxp = fat_idx_get_by_index(dev_handle, index);
249 if (!idxp)
250 return NULL;
251 /* idxp->lock held */
252 node = fat_node_get_core(idxp);
253 futex_up(&idxp->lock);
254 return node;
255}
256
257void fat_node_put(void *node)
258{
259 fat_node_t *nodep = (fat_node_t *)node;
260 bool destroy = false;
261
262 futex_down(&nodep->lock);
263 if (!--nodep->refcnt) {
264 if (nodep->idx) {
265 futex_down(&ffn_futex);
266 list_append(&nodep->ffn_link, &ffn_head);
267 futex_up(&ffn_futex);
268 } else {
269 /*
270 * The node does not have any index structure associated
271 * with itself. This can only mean that we are releasing
272 * the node after a failed attempt to allocate the index
273 * structure for it.
274 */
275 destroy = true;
276 }
277 }
278 futex_up(&nodep->lock);
279 if (destroy)
280 free(node);
281}
282
283void *fat_create_node(dev_handle_t dev_handle, int flags)
284{
285 fat_idx_t *idxp;
286 fat_node_t *nodep;
287
288 nodep = fat_node_get_new();
289 if (!nodep)
290 return NULL;
291 idxp = fat_idx_get_new(dev_handle);
292 if (!idxp) {
293 fat_node_put(nodep);
294 return NULL;
295 }
296 /* idxp->lock held */
297 if (flags & L_DIRECTORY) {
298 nodep->type = FAT_DIRECTORY;
299 } else {
300 nodep->type = FAT_FILE;
301 }
302 nodep->size = 0;
303 nodep->firstc = FAT_CLST_RES0;
304 nodep->lnkcnt = 0; /* not linked anywhere */
305 nodep->refcnt = 1;
306
307 nodep->idx = idxp;
308 idxp->nodep = nodep;
309
310 futex_up(&idxp->lock);
311 return nodep;
312}
313
314int fat_destroy_node(void *node)
315{
316 fat_node_t *nodep = (fat_node_t *)node;
317 fat_bs_t *bs;
318
319 /*
320 * The node is not reachable from the file system. This means that the
321 * link count should be zero and that the index structure cannot be
322 * found in the position hash. Obviously, we don't need to lock the node
323 * nor its index structure.
324 */
325 assert(nodep->lnkcnt == 0);
326
327 /*
328 * The node may not have any children.
329 */
330 assert(fat_has_children(node) == false);
331
332 bs = block_bb_get(nodep->idx->dev_handle);
333 if (nodep->firstc != FAT_CLST_RES0) {
334 assert(nodep->size);
335 /* Free all clusters allocated to the node. */
336 fat_free_clusters(bs, nodep->idx->dev_handle, nodep->firstc);
337 }
338
339 fat_idx_destroy(nodep->idx);
340 free(nodep);
341 return EOK;
342}
343
344bool fat_link(void *prnt, void *chld, const char *name)
345{
346 return false; /* not supported at the moment */
347}
348
349int fat_unlink(void *prnt, void *chld)
350{
351 return ENOTSUP; /* not supported at the moment */
352}
353
354void *fat_match(void *prnt, const char *component)
355{
356 fat_bs_t *bs;
357 fat_node_t *parentp = (fat_node_t *)prnt;
358 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
359 unsigned i, j;
360 unsigned bps; /* bytes per sector */
361 unsigned dps; /* dentries per sector */
362 unsigned blocks;
363 fat_dentry_t *d;
364 block_t *b;
365
366 futex_down(&parentp->idx->lock);
367 bs = block_bb_get(parentp->idx->dev_handle);
368 bps = uint16_t_le2host(bs->bps);
369 dps = bps / sizeof(fat_dentry_t);
370 blocks = parentp->size / bps;
371 for (i = 0; i < blocks; i++) {
372 b = fat_block_get(bs, parentp, i, BLOCK_FLAGS_NONE);
373 for (j = 0; j < dps; j++) {
374 d = ((fat_dentry_t *)b->data) + j;
375 switch (fat_classify_dentry(d)) {
376 case FAT_DENTRY_SKIP:
377 continue;
378 case FAT_DENTRY_LAST:
379 block_put(b);
380 futex_up(&parentp->idx->lock);
381 return NULL;
382 default:
383 case FAT_DENTRY_VALID:
384 dentry_name_canonify(d, name);
385 break;
386 }
387 if (stricmp(name, component) == 0) {
388 /* hit */
389 void *node;
390 /*
391 * Assume tree hierarchy for locking. We
392 * already have the parent and now we are going
393 * to lock the child. Never lock in the oposite
394 * order.
395 */
396 fat_idx_t *idx = fat_idx_get_by_pos(
397 parentp->idx->dev_handle, parentp->firstc,
398 i * dps + j);
399 futex_up(&parentp->idx->lock);
400 if (!idx) {
401 /*
402 * Can happen if memory is low or if we
403 * run out of 32-bit indices.
404 */
405 block_put(b);
406 return NULL;
407 }
408 node = fat_node_get_core(idx);
409 futex_up(&idx->lock);
410 block_put(b);
411 return node;
412 }
413 }
414 block_put(b);
415 }
416
417 futex_up(&parentp->idx->lock);
418 return NULL;
419}
420
421fs_index_t fat_index_get(void *node)
422{
423 fat_node_t *fnodep = (fat_node_t *)node;
424 if (!fnodep)
425 return 0;
426 return fnodep->idx->index;
427}
428
429size_t fat_size_get(void *node)
430{
431 return ((fat_node_t *)node)->size;
432}
433
434unsigned fat_lnkcnt_get(void *node)
435{
436 return ((fat_node_t *)node)->lnkcnt;
437}
438
439bool fat_has_children(void *node)
440{
441 fat_bs_t *bs;
442 fat_node_t *nodep = (fat_node_t *)node;
443 unsigned bps;
444 unsigned dps;
445 unsigned blocks;
446 block_t *b;
447 unsigned i, j;
448
449 if (nodep->type != FAT_DIRECTORY)
450 return false;
451
452 futex_down(&nodep->idx->lock);
453 bs = block_bb_get(nodep->idx->dev_handle);
454 bps = uint16_t_le2host(bs->bps);
455 dps = bps / sizeof(fat_dentry_t);
456
457 blocks = nodep->size / bps;
458
459 for (i = 0; i < blocks; i++) {
460 fat_dentry_t *d;
461
462 b = fat_block_get(bs, nodep, i, BLOCK_FLAGS_NONE);
463 for (j = 0; j < dps; j++) {
464 d = ((fat_dentry_t *)b->data) + j;
465 switch (fat_classify_dentry(d)) {
466 case FAT_DENTRY_SKIP:
467 continue;
468 case FAT_DENTRY_LAST:
469 block_put(b);
470 futex_up(&nodep->idx->lock);
471 return false;
472 default:
473 case FAT_DENTRY_VALID:
474 block_put(b);
475 futex_up(&nodep->idx->lock);
476 return true;
477 }
478 block_put(b);
479 futex_up(&nodep->idx->lock);
480 return true;
481 }
482 block_put(b);
483 }
484
485 futex_up(&nodep->idx->lock);
486 return false;
487}
488
489void *fat_root_get(dev_handle_t dev_handle)
490{
491 return fat_node_get(dev_handle, 0);
492}
493
494char fat_plb_get_char(unsigned pos)
495{
496 return fat_reg.plb_ro[pos % PLB_SIZE];
497}
498
499bool fat_is_directory(void *node)
500{
501 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
502}
503
504bool fat_is_file(void *node)
505{
506 return ((fat_node_t *)node)->type == FAT_FILE;
507}
508
509/** libfs operations */
510libfs_ops_t fat_libfs_ops = {
511 .match = fat_match,
512 .node_get = fat_node_get,
513 .node_put = fat_node_put,
514 .create = fat_create_node,
515 .destroy = fat_destroy_node,
516 .link = fat_link,
517 .unlink = fat_unlink,
518 .index_get = fat_index_get,
519 .size_get = fat_size_get,
520 .lnkcnt_get = fat_lnkcnt_get,
521 .has_children = fat_has_children,
522 .root_get = fat_root_get,
523 .plb_get_char = fat_plb_get_char,
524 .is_directory = fat_is_directory,
525 .is_file = fat_is_file
526};
527
528void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
529{
530 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
531 fat_bs_t *bs;
532 uint16_t bps;
533 uint16_t rde;
534 int rc;
535
536 /* initialize libblock */
537 rc = block_init(dev_handle, BS_SIZE);
538 if (rc != EOK) {
539 ipc_answer_0(rid, rc);
540 return;
541 }
542
543 /* prepare the boot block */
544 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
545 if (rc != EOK) {
546 block_fini(dev_handle);
547 ipc_answer_0(rid, rc);
548 return;
549 }
550
551 /* get the buffer with the boot sector */
552 bs = block_bb_get(dev_handle);
553
554 /* Read the number of root directory entries. */
555 bps = uint16_t_le2host(bs->bps);
556 rde = uint16_t_le2host(bs->root_ent_max);
557
558 if (bps != BS_SIZE) {
559 block_fini(dev_handle);
560 ipc_answer_0(rid, ENOTSUP);
561 return;
562 }
563
564 /* Initialize the block cache */
565 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
566 if (rc != EOK) {
567 block_fini(dev_handle);
568 ipc_answer_0(rid, rc);
569 return;
570 }
571
572 rc = fat_idx_init_by_dev_handle(dev_handle);
573 if (rc != EOK) {
574 block_fini(dev_handle);
575 ipc_answer_0(rid, rc);
576 return;
577 }
578
579 /* Initialize the root node. */
580 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
581 if (!rootp) {
582 block_fini(dev_handle);
583 fat_idx_fini_by_dev_handle(dev_handle);
584 ipc_answer_0(rid, ENOMEM);
585 return;
586 }
587 fat_node_initialize(rootp);
588
589 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
590 if (!ridxp) {
591 block_fini(dev_handle);
592 free(rootp);
593 fat_idx_fini_by_dev_handle(dev_handle);
594 ipc_answer_0(rid, ENOMEM);
595 return;
596 }
597 assert(ridxp->index == 0);
598 /* ridxp->lock held */
599
600 rootp->type = FAT_DIRECTORY;
601 rootp->firstc = FAT_CLST_ROOT;
602 rootp->refcnt = 1;
603 rootp->lnkcnt = 0; /* FS root is not linked */
604 rootp->size = rde * sizeof(fat_dentry_t);
605 rootp->idx = ridxp;
606 ridxp->nodep = rootp;
607
608 futex_up(&ridxp->lock);
609
610 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
611}
612
613void fat_mount(ipc_callid_t rid, ipc_call_t *request)
614{
615 ipc_answer_0(rid, ENOTSUP);
616}
617
618void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
619{
620 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
621}
622
623void fat_read(ipc_callid_t rid, ipc_call_t *request)
624{
625 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
626 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
627 off_t pos = (off_t)IPC_GET_ARG3(*request);
628 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
629 fat_bs_t *bs;
630 uint16_t bps;
631 size_t bytes;
632 block_t *b;
633
634 if (!nodep) {
635 ipc_answer_0(rid, ENOENT);
636 return;
637 }
638
639 ipc_callid_t callid;
640 size_t len;
641 if (!ipc_data_read_receive(&callid, &len)) {
642 fat_node_put(nodep);
643 ipc_answer_0(callid, EINVAL);
644 ipc_answer_0(rid, EINVAL);
645 return;
646 }
647
648 bs = block_bb_get(dev_handle);
649 bps = uint16_t_le2host(bs->bps);
650
651 if (nodep->type == FAT_FILE) {
652 /*
653 * Our strategy for regular file reads is to read one block at
654 * most and make use of the possibility to return less data than
655 * requested. This keeps the code very simple.
656 */
657 if (pos >= nodep->size) {
658 /* reading beyond the EOF */
659 bytes = 0;
660 (void) ipc_data_read_finalize(callid, NULL, 0);
661 } else {
662 bytes = min(len, bps - pos % bps);
663 bytes = min(bytes, nodep->size - pos);
664 b = fat_block_get(bs, nodep, pos / bps,
665 BLOCK_FLAGS_NONE);
666 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
667 bytes);
668 block_put(b);
669 }
670 } else {
671 unsigned bnum;
672 off_t spos = pos;
673 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
674 fat_dentry_t *d;
675
676 assert(nodep->type == FAT_DIRECTORY);
677 assert(nodep->size % bps == 0);
678 assert(bps % sizeof(fat_dentry_t) == 0);
679
680 /*
681 * Our strategy for readdir() is to use the position pointer as
682 * an index into the array of all dentries. On entry, it points
683 * to the first unread dentry. If we skip any dentries, we bump
684 * the position pointer accordingly.
685 */
686 bnum = (pos * sizeof(fat_dentry_t)) / bps;
687 while (bnum < nodep->size / bps) {
688 off_t o;
689
690 b = fat_block_get(bs, nodep, bnum, BLOCK_FLAGS_NONE);
691 for (o = pos % (bps / sizeof(fat_dentry_t));
692 o < bps / sizeof(fat_dentry_t);
693 o++, pos++) {
694 d = ((fat_dentry_t *)b->data) + o;
695 switch (fat_classify_dentry(d)) {
696 case FAT_DENTRY_SKIP:
697 continue;
698 case FAT_DENTRY_LAST:
699 block_put(b);
700 goto miss;
701 default:
702 case FAT_DENTRY_VALID:
703 dentry_name_canonify(d, name);
704 block_put(b);
705 goto hit;
706 }
707 }
708 block_put(b);
709 bnum++;
710 }
711miss:
712 fat_node_put(nodep);
713 ipc_answer_0(callid, ENOENT);
714 ipc_answer_1(rid, ENOENT, 0);
715 return;
716hit:
717 (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
718 bytes = (pos - spos) + 1;
719 }
720
721 fat_node_put(nodep);
722 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
723}
724
725void fat_write(ipc_callid_t rid, ipc_call_t *request)
726{
727 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
728 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
729 off_t pos = (off_t)IPC_GET_ARG3(*request);
730 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
731 fat_bs_t *bs;
732 size_t bytes;
733 block_t *b;
734 uint16_t bps;
735 unsigned spc;
736 unsigned bpc; /* bytes per cluster */
737 off_t boundary;
738 int flags = BLOCK_FLAGS_NONE;
739
740 if (!nodep) {
741 ipc_answer_0(rid, ENOENT);
742 return;
743 }
744
745 ipc_callid_t callid;
746 size_t len;
747 if (!ipc_data_write_receive(&callid, &len)) {
748 fat_node_put(nodep);
749 ipc_answer_0(callid, EINVAL);
750 ipc_answer_0(rid, EINVAL);
751 return;
752 }
753
754 bs = block_bb_get(dev_handle);
755 bps = uint16_t_le2host(bs->bps);
756 spc = bs->spc;
757 bpc = bps * spc;
758
759 /*
760 * In all scenarios, we will attempt to write out only one block worth
761 * of data at maximum. There might be some more efficient approaches,
762 * but this one greatly simplifies fat_write(). Note that we can afford
763 * to do this because the client must be ready to handle the return
764 * value signalizing a smaller number of bytes written.
765 */
766 bytes = min(len, bps - pos % bps);
767 if (bytes == bps)
768 flags |= BLOCK_FLAGS_NOREAD;
769
770 boundary = ROUND_UP(nodep->size, bpc);
771 if (pos < boundary) {
772 /*
773 * This is the easier case - we are either overwriting already
774 * existing contents or writing behind the EOF, but still within
775 * the limits of the last cluster. The node size may grow to the
776 * next block size boundary.
777 */
778 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
779 b = fat_block_get(bs, nodep, pos / bps, flags);
780 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
781 bytes);
782 b->dirty = true; /* need to sync block */
783 block_put(b);
784 if (pos + bytes > nodep->size) {
785 nodep->size = pos + bytes;
786 nodep->dirty = true; /* need to sync node */
787 }
788 ipc_answer_2(rid, EOK, bytes, nodep->size);
789 fat_node_put(nodep);
790 return;
791 } else {
792 /*
793 * This is the more difficult case. We must allocate new
794 * clusters for the node and zero them out.
795 */
796 int status;
797 unsigned nclsts;
798 fat_cluster_t mcl, lcl;
799
800 nclsts = (ROUND_UP(pos + bytes, bpc) - boundary) / bpc;
801 /* create an independent chain of nclsts clusters in all FATs */
802 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl, &lcl);
803 if (status != EOK) {
804 /* could not allocate a chain of nclsts clusters */
805 fat_node_put(nodep);
806 ipc_answer_0(callid, status);
807 ipc_answer_0(rid, status);
808 return;
809 }
810 /* zero fill any gaps */
811 fat_fill_gap(bs, nodep, mcl, pos);
812 b = _fat_block_get(bs, dev_handle, lcl, (pos / bps) % spc,
813 flags);
814 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
815 bytes);
816 b->dirty = true; /* need to sync block */
817 block_put(b);
818 /*
819 * Append the cluster chain starting in mcl to the end of the
820 * node's cluster chain.
821 */
822 fat_append_clusters(bs, nodep, mcl);
823 nodep->size = pos + bytes;
824 nodep->dirty = true; /* need to sync node */
825 ipc_answer_2(rid, EOK, bytes, nodep->size);
826 fat_node_put(nodep);
827 return;
828 }
829}
830
831void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
832{
833 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
834 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
835 size_t size = (off_t)IPC_GET_ARG3(*request);
836 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
837 fat_bs_t *bs;
838 uint16_t bps;
839 uint8_t spc;
840 unsigned bpc; /* bytes per cluster */
841 int rc;
842
843 if (!nodep) {
844 ipc_answer_0(rid, ENOENT);
845 return;
846 }
847
848 bs = block_bb_get(dev_handle);
849 bps = uint16_t_le2host(bs->bps);
850 spc = bs->spc;
851 bpc = bps * spc;
852
853 if (nodep->size == size) {
854 rc = EOK;
855 } else if (nodep->size < size) {
856 /*
857 * The standard says we have the freedom to grow the node.
858 * For now, we simply return an error.
859 */
860 rc = EINVAL;
861 } else if (ROUND_UP(nodep->size, bpc) == ROUND_UP(size, bpc)) {
862 /*
863 * The node will be shrunk, but no clusters will be deallocated.
864 */
865 nodep->size = size;
866 nodep->dirty = true; /* need to sync node */
867 rc = EOK;
868 } else {
869 /*
870 * The node will be shrunk, clusters will be deallocated.
871 */
872 if (size == 0) {
873 fat_chop_clusters(bs, nodep, FAT_CLST_RES0);
874 } else {
875 fat_cluster_t lastc;
876 (void) fat_cluster_walk(bs, dev_handle, nodep->firstc,
877 &lastc, (size - 1) / bpc);
878 fat_chop_clusters(bs, nodep, lastc);
879 }
880 nodep->size = size;
881 nodep->dirty = true; /* need to sync node */
882 rc = EOK;
883 }
884 fat_node_put(nodep);
885 ipc_answer_0(rid, rc);
886 return;
887}
888
889void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
890{
891 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
892 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
893 int rc;
894
895 fat_node_t *nodep = fat_node_get(dev_handle, index);
896 if (!nodep) {
897 ipc_answer_0(rid, ENOENT);
898 return;
899 }
900
901 rc = fat_destroy_node(nodep);
902 ipc_answer_0(rid, rc);
903}
904
905/**
906 * @}
907 */
Note: See TracBrowser for help on using the repository browser.