source: mainline/uspace/srv/fs/fat/fat_ops.c@ 6c71a1f

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

Start implementing fat_truncate().

  • Property mode set to 100644
File size: 17.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);
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
105/** Internal version of fat_node_get().
106 *
107 * @param idxp Locked index structure.
108 */
109static void *fat_node_get_core(fat_idx_t *idxp)
110{
111 block_t *b;
112 fat_bs_t *bs;
113 fat_dentry_t *d;
114 fat_node_t *nodep = NULL;
115 unsigned bps;
116 unsigned dps;
117
118 if (idxp->nodep) {
119 /*
120 * We are lucky.
121 * The node is already instantiated in memory.
122 */
123 futex_down(&idxp->nodep->lock);
124 if (!idxp->nodep->refcnt++)
125 list_remove(&idxp->nodep->ffn_link);
126 futex_up(&idxp->nodep->lock);
127 return idxp->nodep;
128 }
129
130 /*
131 * We must instantiate the node from the file system.
132 */
133
134 assert(idxp->pfc);
135
136 futex_down(&ffn_futex);
137 if (!list_empty(&ffn_head)) {
138 /* Try to use a cached free node structure. */
139 fat_idx_t *idxp_tmp;
140 nodep = list_get_instance(ffn_head.next, fat_node_t, ffn_link);
141 if (futex_trydown(&nodep->lock) == ESYNCH_WOULD_BLOCK)
142 goto skip_cache;
143 idxp_tmp = nodep->idx;
144 if (futex_trydown(&idxp_tmp->lock) == ESYNCH_WOULD_BLOCK) {
145 futex_up(&nodep->lock);
146 goto skip_cache;
147 }
148 list_remove(&nodep->ffn_link);
149 futex_up(&ffn_futex);
150 if (nodep->dirty)
151 fat_node_sync(nodep);
152 idxp_tmp->nodep = NULL;
153 futex_up(&nodep->lock);
154 futex_up(&idxp_tmp->lock);
155 } else {
156skip_cache:
157 /* Try to allocate a new node structure. */
158 futex_up(&ffn_futex);
159 nodep = (fat_node_t *)malloc(sizeof(fat_node_t));
160 if (!nodep)
161 return NULL;
162 }
163 fat_node_initialize(nodep);
164
165 bs = block_bb_get(idxp->dev_handle);
166 bps = uint16_t_le2host(bs->bps);
167 dps = bps / sizeof(fat_dentry_t);
168
169 /* Read the block that contains the dentry of interest. */
170 b = _fat_block_get(bs, idxp->dev_handle, idxp->pfc,
171 (idxp->pdi * sizeof(fat_dentry_t)) / bps);
172 assert(b);
173
174 d = ((fat_dentry_t *)b->data) + (idxp->pdi % dps);
175 if (d->attr & FAT_ATTR_SUBDIR) {
176 /*
177 * The only directory which does not have this bit set is the
178 * root directory itself. The root directory node is handled
179 * and initialized elsewhere.
180 */
181 nodep->type = FAT_DIRECTORY;
182 /*
183 * Unfortunately, the 'size' field of the FAT dentry is not
184 * defined for the directory entry type. We must determine the
185 * size of the directory by walking the FAT.
186 */
187 nodep->size = bps * _fat_blcks_get(bs, idxp->dev_handle,
188 uint16_t_le2host(d->firstc), NULL);
189 } else {
190 nodep->type = FAT_FILE;
191 nodep->size = uint32_t_le2host(d->size);
192 }
193 nodep->firstc = uint16_t_le2host(d->firstc);
194 nodep->lnkcnt = 1;
195 nodep->refcnt = 1;
196
197 block_put(b);
198
199 /* Link the idx structure with the node structure. */
200 nodep->idx = idxp;
201 idxp->nodep = nodep;
202
203 return nodep;
204}
205
206/** Instantiate a FAT in-core node. */
207static void *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
208{
209 void *node;
210 fat_idx_t *idxp;
211
212 idxp = fat_idx_get_by_index(dev_handle, index);
213 if (!idxp)
214 return NULL;
215 /* idxp->lock held */
216 node = fat_node_get_core(idxp);
217 futex_up(&idxp->lock);
218 return node;
219}
220
221static void fat_node_put(void *node)
222{
223 fat_node_t *nodep = (fat_node_t *)node;
224
225 futex_down(&nodep->lock);
226 if (!--nodep->refcnt) {
227 futex_down(&ffn_futex);
228 list_append(&nodep->ffn_link, &ffn_head);
229 futex_up(&ffn_futex);
230 }
231 futex_up(&nodep->lock);
232}
233
234static void *fat_create(int flags)
235{
236 return NULL; /* not supported at the moment */
237}
238
239static int fat_destroy(void *node)
240{
241 return ENOTSUP; /* not supported at the moment */
242}
243
244static bool fat_link(void *prnt, void *chld, const char *name)
245{
246 return false; /* not supported at the moment */
247}
248
249static int fat_unlink(void *prnt, void *chld)
250{
251 return ENOTSUP; /* not supported at the moment */
252}
253
254static void *fat_match(void *prnt, const char *component)
255{
256 fat_bs_t *bs;
257 fat_node_t *parentp = (fat_node_t *)prnt;
258 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
259 unsigned i, j;
260 unsigned bps; /* bytes per sector */
261 unsigned dps; /* dentries per sector */
262 unsigned blocks;
263 fat_dentry_t *d;
264 block_t *b;
265
266 futex_down(&parentp->idx->lock);
267 bs = block_bb_get(parentp->idx->dev_handle);
268 bps = uint16_t_le2host(bs->bps);
269 dps = bps / sizeof(fat_dentry_t);
270 blocks = parentp->size / bps;
271 for (i = 0; i < blocks; i++) {
272 b = fat_block_get(bs, parentp, i);
273 for (j = 0; j < dps; j++) {
274 d = ((fat_dentry_t *)b->data) + j;
275 switch (fat_classify_dentry(d)) {
276 case FAT_DENTRY_SKIP:
277 continue;
278 case FAT_DENTRY_LAST:
279 block_put(b);
280 futex_up(&parentp->idx->lock);
281 return NULL;
282 default:
283 case FAT_DENTRY_VALID:
284 dentry_name_canonify(d, name);
285 break;
286 }
287 if (stricmp(name, component) == 0) {
288 /* hit */
289 void *node;
290 /*
291 * Assume tree hierarchy for locking. We
292 * already have the parent and now we are going
293 * to lock the child. Never lock in the oposite
294 * order.
295 */
296 fat_idx_t *idx = fat_idx_get_by_pos(
297 parentp->idx->dev_handle, parentp->firstc,
298 i * dps + j);
299 futex_up(&parentp->idx->lock);
300 if (!idx) {
301 /*
302 * Can happen if memory is low or if we
303 * run out of 32-bit indices.
304 */
305 block_put(b);
306 return NULL;
307 }
308 node = fat_node_get_core(idx);
309 futex_up(&idx->lock);
310 block_put(b);
311 return node;
312 }
313 }
314 block_put(b);
315 }
316
317 futex_up(&parentp->idx->lock);
318 return NULL;
319}
320
321static fs_index_t fat_index_get(void *node)
322{
323 fat_node_t *fnodep = (fat_node_t *)node;
324 if (!fnodep)
325 return 0;
326 return fnodep->idx->index;
327}
328
329static size_t fat_size_get(void *node)
330{
331 return ((fat_node_t *)node)->size;
332}
333
334static unsigned fat_lnkcnt_get(void *node)
335{
336 return ((fat_node_t *)node)->lnkcnt;
337}
338
339static bool fat_has_children(void *node)
340{
341 fat_bs_t *bs;
342 fat_node_t *nodep = (fat_node_t *)node;
343 unsigned bps;
344 unsigned dps;
345 unsigned blocks;
346 block_t *b;
347 unsigned i, j;
348
349 if (nodep->type != FAT_DIRECTORY)
350 return false;
351
352 futex_down(&nodep->idx->lock);
353 bs = block_bb_get(nodep->idx->dev_handle);
354 bps = uint16_t_le2host(bs->bps);
355 dps = bps / sizeof(fat_dentry_t);
356
357 blocks = nodep->size / bps;
358
359 for (i = 0; i < blocks; i++) {
360 fat_dentry_t *d;
361
362 b = fat_block_get(bs, nodep, i);
363 for (j = 0; j < dps; j++) {
364 d = ((fat_dentry_t *)b->data) + j;
365 switch (fat_classify_dentry(d)) {
366 case FAT_DENTRY_SKIP:
367 continue;
368 case FAT_DENTRY_LAST:
369 block_put(b);
370 futex_up(&nodep->idx->lock);
371 return false;
372 default:
373 case FAT_DENTRY_VALID:
374 block_put(b);
375 futex_up(&nodep->idx->lock);
376 return true;
377 }
378 block_put(b);
379 futex_up(&nodep->idx->lock);
380 return true;
381 }
382 block_put(b);
383 }
384
385 futex_up(&nodep->idx->lock);
386 return false;
387}
388
389static void *fat_root_get(dev_handle_t dev_handle)
390{
391 return fat_node_get(dev_handle, 0);
392}
393
394static char fat_plb_get_char(unsigned pos)
395{
396 return fat_reg.plb_ro[pos % PLB_SIZE];
397}
398
399static bool fat_is_directory(void *node)
400{
401 return ((fat_node_t *)node)->type == FAT_DIRECTORY;
402}
403
404static bool fat_is_file(void *node)
405{
406 return ((fat_node_t *)node)->type == FAT_FILE;
407}
408
409/** libfs operations */
410libfs_ops_t fat_libfs_ops = {
411 .match = fat_match,
412 .node_get = fat_node_get,
413 .node_put = fat_node_put,
414 .create = fat_create,
415 .destroy = fat_destroy,
416 .link = fat_link,
417 .unlink = fat_unlink,
418 .index_get = fat_index_get,
419 .size_get = fat_size_get,
420 .lnkcnt_get = fat_lnkcnt_get,
421 .has_children = fat_has_children,
422 .root_get = fat_root_get,
423 .plb_get_char = fat_plb_get_char,
424 .is_directory = fat_is_directory,
425 .is_file = fat_is_file
426};
427
428void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
429{
430 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
431 fat_bs_t *bs;
432 uint16_t bps;
433 uint16_t rde;
434 int rc;
435
436 /* initialize libblock */
437 rc = block_init(dev_handle, BS_SIZE);
438 if (rc != EOK) {
439 ipc_answer_0(rid, rc);
440 return;
441 }
442
443 /* prepare the boot block */
444 rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
445 if (rc != EOK) {
446 block_fini(dev_handle);
447 ipc_answer_0(rid, rc);
448 return;
449 }
450
451 /* get the buffer with the boot sector */
452 bs = block_bb_get(dev_handle);
453
454 /* Read the number of root directory entries. */
455 bps = uint16_t_le2host(bs->bps);
456 rde = uint16_t_le2host(bs->root_ent_max);
457
458 if (bps != BS_SIZE) {
459 block_fini(dev_handle);
460 ipc_answer_0(rid, ENOTSUP);
461 return;
462 }
463
464 /* Initialize the block cache */
465 rc = block_cache_init(dev_handle, bps, 0 /* XXX */);
466 if (rc != EOK) {
467 block_fini(dev_handle);
468 ipc_answer_0(rid, rc);
469 return;
470 }
471
472 rc = fat_idx_init_by_dev_handle(dev_handle);
473 if (rc != EOK) {
474 block_fini(dev_handle);
475 ipc_answer_0(rid, rc);
476 return;
477 }
478
479 /* Initialize the root node. */
480 fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
481 if (!rootp) {
482 block_fini(dev_handle);
483 fat_idx_fini_by_dev_handle(dev_handle);
484 ipc_answer_0(rid, ENOMEM);
485 return;
486 }
487 fat_node_initialize(rootp);
488
489 fat_idx_t *ridxp = fat_idx_get_by_pos(dev_handle, FAT_CLST_ROOTPAR, 0);
490 if (!ridxp) {
491 block_fini(dev_handle);
492 free(rootp);
493 fat_idx_fini_by_dev_handle(dev_handle);
494 ipc_answer_0(rid, ENOMEM);
495 return;
496 }
497 assert(ridxp->index == 0);
498 /* ridxp->lock held */
499
500 rootp->type = FAT_DIRECTORY;
501 rootp->firstc = FAT_CLST_ROOT;
502 rootp->refcnt = 1;
503 rootp->lnkcnt = 0; /* FS root is not linked */
504 rootp->size = rde * sizeof(fat_dentry_t);
505 rootp->idx = ridxp;
506 ridxp->nodep = rootp;
507
508 futex_up(&ridxp->lock);
509
510 ipc_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
511}
512
513void fat_mount(ipc_callid_t rid, ipc_call_t *request)
514{
515 ipc_answer_0(rid, ENOTSUP);
516}
517
518void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
519{
520 libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
521}
522
523void fat_read(ipc_callid_t rid, ipc_call_t *request)
524{
525 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
526 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
527 off_t pos = (off_t)IPC_GET_ARG3(*request);
528 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
529 fat_bs_t *bs;
530 uint16_t bps;
531 size_t bytes;
532 block_t *b;
533
534 if (!nodep) {
535 ipc_answer_0(rid, ENOENT);
536 return;
537 }
538
539 ipc_callid_t callid;
540 size_t len;
541 if (!ipc_data_read_receive(&callid, &len)) {
542 fat_node_put(nodep);
543 ipc_answer_0(callid, EINVAL);
544 ipc_answer_0(rid, EINVAL);
545 return;
546 }
547
548 bs = block_bb_get(dev_handle);
549 bps = uint16_t_le2host(bs->bps);
550
551 if (nodep->type == FAT_FILE) {
552 /*
553 * Our strategy for regular file reads is to read one block at
554 * most and make use of the possibility to return less data than
555 * requested. This keeps the code very simple.
556 */
557 if (pos >= nodep->size) {
558 /* reading beyond the EOF */
559 bytes = 0;
560 (void) ipc_data_read_finalize(callid, NULL, 0);
561 } else {
562 bytes = min(len, bps - pos % bps);
563 bytes = min(bytes, nodep->size - pos);
564 b = fat_block_get(bs, nodep, pos / bps);
565 (void) ipc_data_read_finalize(callid, b->data + pos % bps,
566 bytes);
567 block_put(b);
568 }
569 } else {
570 unsigned bnum;
571 off_t spos = pos;
572 char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
573 fat_dentry_t *d;
574
575 assert(nodep->type == FAT_DIRECTORY);
576 assert(nodep->size % bps == 0);
577 assert(bps % sizeof(fat_dentry_t) == 0);
578
579 /*
580 * Our strategy for readdir() is to use the position pointer as
581 * an index into the array of all dentries. On entry, it points
582 * to the first unread dentry. If we skip any dentries, we bump
583 * the position pointer accordingly.
584 */
585 bnum = (pos * sizeof(fat_dentry_t)) / bps;
586 while (bnum < nodep->size / bps) {
587 off_t o;
588
589 b = fat_block_get(bs, nodep, bnum);
590 for (o = pos % (bps / sizeof(fat_dentry_t));
591 o < bps / sizeof(fat_dentry_t);
592 o++, pos++) {
593 d = ((fat_dentry_t *)b->data) + o;
594 switch (fat_classify_dentry(d)) {
595 case FAT_DENTRY_SKIP:
596 continue;
597 case FAT_DENTRY_LAST:
598 block_put(b);
599 goto miss;
600 default:
601 case FAT_DENTRY_VALID:
602 dentry_name_canonify(d, name);
603 block_put(b);
604 goto hit;
605 }
606 }
607 block_put(b);
608 bnum++;
609 }
610miss:
611 fat_node_put(nodep);
612 ipc_answer_0(callid, ENOENT);
613 ipc_answer_1(rid, ENOENT, 0);
614 return;
615hit:
616 (void) ipc_data_read_finalize(callid, name, strlen(name) + 1);
617 bytes = (pos - spos) + 1;
618 }
619
620 fat_node_put(nodep);
621 ipc_answer_1(rid, EOK, (ipcarg_t)bytes);
622}
623
624void fat_write(ipc_callid_t rid, ipc_call_t *request)
625{
626 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
627 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
628 off_t pos = (off_t)IPC_GET_ARG3(*request);
629 fat_node_t *nodep = (fat_node_t *)fat_node_get(dev_handle, index);
630 fat_bs_t *bs;
631 size_t bytes;
632 block_t *b;
633 uint16_t bps;
634 unsigned spc;
635 off_t boundary;
636
637 if (!nodep) {
638 ipc_answer_0(rid, ENOENT);
639 return;
640 }
641
642 /* XXX remove me when you are ready */
643 {
644 ipc_answer_0(rid, ENOTSUP);
645 fat_node_put(nodep);
646 return;
647 }
648
649 ipc_callid_t callid;
650 size_t len;
651 if (!ipc_data_write_receive(&callid, &len)) {
652 fat_node_put(nodep);
653 ipc_answer_0(callid, EINVAL);
654 ipc_answer_0(rid, EINVAL);
655 return;
656 }
657
658 /*
659 * In all scenarios, we will attempt to write out only one block worth
660 * of data at maximum. There might be some more efficient approaches,
661 * but this one greatly simplifies fat_write(). Note that we can afford
662 * to do this because the client must be ready to handle the return
663 * value signalizing a smaller number of bytes written.
664 */
665 bytes = min(len, bps - pos % bps);
666
667 bs = block_bb_get(dev_handle);
668 bps = uint16_t_le2host(bs->bps);
669 spc = bs->spc;
670
671 boundary = ROUND_UP(nodep->size, bps * spc);
672 if (pos < boundary) {
673 /*
674 * This is the easier case - we are either overwriting already
675 * existing contents or writing behind the EOF, but still within
676 * the limits of the last cluster. The node size may grow to the
677 * next block size boundary.
678 */
679 fat_fill_gap(bs, nodep, FAT_CLST_RES0, pos);
680 b = fat_block_get(bs, nodep, pos / bps);
681 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
682 bytes);
683 b->dirty = true; /* need to sync block */
684 block_put(b);
685 if (pos + bytes > nodep->size) {
686 nodep->size = pos + bytes;
687 nodep->dirty = true; /* need to sync node */
688 }
689 fat_node_put(nodep);
690 ipc_answer_1(rid, EOK, bytes);
691 return;
692 } else {
693 /*
694 * This is the more difficult case. We must allocate new
695 * clusters for the node and zero them out.
696 */
697 int status;
698 unsigned nclsts;
699 fat_cluster_t mcl, lcl;
700
701 nclsts = (ROUND_UP(pos + bytes, bps * spc) - boundary) /
702 bps * spc;
703 /* create an independent chain of nclsts clusters in all FATs */
704 status = fat_alloc_clusters(bs, dev_handle, nclsts, &mcl,
705 &lcl);
706 if (status != EOK) {
707 /* could not allocate a chain of nclsts clusters */
708 fat_node_put(nodep);
709 ipc_answer_0(callid, status);
710 ipc_answer_0(rid, status);
711 return;
712 }
713 /* zero fill any gaps */
714 fat_fill_gap(bs, nodep, mcl, pos);
715 b = _fat_block_get(bs, dev_handle, lcl,
716 (pos / bps) % spc);
717 (void) ipc_data_write_finalize(callid, b->data + pos % bps,
718 bytes);
719 b->dirty = true; /* need to sync block */
720 block_put(b);
721 /*
722 * Append the cluster chain starting in mcl to the end of the
723 * node's cluster chain.
724 */
725 fat_append_clusters(bs, nodep, mcl);
726 nodep->size = pos + bytes;
727 nodep->dirty = true; /* need to sync node */
728 fat_node_put(nodep);
729 ipc_answer_1(rid, EOK, bytes);
730 return;
731 }
732}
733
734void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
735{
736 ipc_answer_0(rid, ENOTSUP);
737}
738
739/**
740 * @}
741 */
Note: See TracBrowser for help on using the repository browser.