source: mainline/uspace/srv/fs/exfat/exfat_ops.c@ 8565a42

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8565a42 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 38.0 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2011 Oleg Romanenko
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup fs
31 * @{
32 */
33
34/**
35 * @file exfat_ops.c
36 * @brief Implementation of VFS operations for the exFAT file system
37 * server.
38 */
39
40#include "exfat.h"
41#include "exfat_fat.h"
42#include "exfat_dentry.h"
43#include "exfat_directory.h"
44#include "exfat_bitmap.h"
45#include "../../vfs/vfs.h"
46#include <libfs.h>
47#include <block.h>
48#include <ipc/services.h>
49#include <ipc/loc.h>
50#include <macros.h>
51#include <async.h>
52#include <errno.h>
53#include <str.h>
54#include <byteorder.h>
55#include <adt/hash_table.h>
56#include <adt/hash.h>
57#include <adt/list.h>
58#include <assert.h>
59#include <fibril_synch.h>
60#include <align.h>
61#include <stdio.h>
62#include <stdlib.h>
63
64/** Mutex protecting the list of cached free FAT nodes. */
65static FIBRIL_MUTEX_INITIALIZE(ffn_mutex);
66
67/** List of cached free FAT nodes. */
68static LIST_INITIALIZE(ffn_list);
69
70/*
71 * Forward declarations of FAT libfs operations.
72 */
73
74static errno_t exfat_root_get(fs_node_t **, service_id_t);
75static errno_t exfat_match(fs_node_t **, fs_node_t *, const char *);
76static errno_t exfat_node_get(fs_node_t **, service_id_t, fs_index_t);
77static errno_t exfat_node_open(fs_node_t *);
78/* static errno_t exfat_node_put(fs_node_t *); */
79static errno_t exfat_create_node(fs_node_t **, service_id_t, int);
80static errno_t exfat_destroy_node(fs_node_t *);
81static errno_t exfat_link(fs_node_t *, fs_node_t *, const char *);
82static errno_t exfat_unlink(fs_node_t *, fs_node_t *, const char *);
83static errno_t exfat_has_children(bool *, fs_node_t *);
84static fs_index_t exfat_index_get(fs_node_t *);
85static aoff64_t exfat_size_get(fs_node_t *);
86static unsigned exfat_lnkcnt_get(fs_node_t *);
87static bool exfat_is_directory(fs_node_t *);
88static bool exfat_is_file(fs_node_t *node);
89static service_id_t exfat_service_get(fs_node_t *node);
90static errno_t exfat_size_block(service_id_t, uint32_t *);
91static errno_t exfat_total_block_count(service_id_t, uint64_t *);
92static errno_t exfat_free_block_count(service_id_t, uint64_t *);
93
94/*
95 * Helper functions.
96 */
97static void exfat_node_initialize(exfat_node_t *node)
98{
99 fibril_mutex_initialize(&node->lock);
100 node->bp = NULL;
101 node->idx = NULL;
102 node->type = EXFAT_UNKNOW;
103 link_initialize(&node->ffn_link);
104 node->size = 0;
105 node->lnkcnt = 0;
106 node->refcnt = 0;
107 node->dirty = false;
108 node->fragmented = false;
109 node->lastc_cached_valid = false;
110 node->lastc_cached_value = 0;
111 node->currc_cached_valid = false;
112 node->currc_cached_bn = 0;
113 node->currc_cached_value = 0;
114}
115
116static errno_t exfat_node_sync(exfat_node_t *node)
117{
118 errno_t rc;
119 exfat_directory_t di;
120 exfat_file_dentry_t df;
121 exfat_stream_dentry_t ds;
122
123 if (!(node->type == EXFAT_DIRECTORY || node->type == EXFAT_FILE))
124 return EOK;
125
126 if (node->type == EXFAT_DIRECTORY)
127 df.attr = EXFAT_ATTR_SUBDIR;
128 else
129 df.attr = 0;
130
131 ds.firstc = node->firstc;
132 if (node->size == 0 && node->firstc == 0) {
133 ds.flags = 0;
134 } else {
135 ds.flags = 1;
136 ds.flags |= (!node->fragmented << 1);
137 }
138 ds.valid_data_size = node->size;
139 ds.data_size = node->size;
140
141 exfat_directory_open_parent(&di, node->idx->service_id, node->idx->pfc,
142 node->idx->parent_fragmented);
143 rc = exfat_directory_seek(&di, node->idx->pdi);
144 if (rc != EOK) {
145 (void) exfat_directory_close(&di);
146 return rc;
147 }
148
149 rc = exfat_directory_sync_file(&di, &df, &ds);
150 if (rc != EOK) {
151 (void) exfat_directory_close(&di);
152 return rc;
153 }
154 return exfat_directory_close(&di);
155}
156
157static errno_t exfat_node_fini_by_service_id(service_id_t service_id)
158{
159 errno_t rc;
160
161 /*
162 * We are called from fat_unmounted() and assume that there are already
163 * no nodes belonging to this instance with non-zero refcount. Therefore
164 * it is sufficient to clean up only the FAT free node list.
165 */
166
167restart:
168 fibril_mutex_lock(&ffn_mutex);
169 list_foreach(ffn_list, ffn_link, exfat_node_t, nodep) {
170 if (!fibril_mutex_trylock(&nodep->lock)) {
171 fibril_mutex_unlock(&ffn_mutex);
172 goto restart;
173 }
174 if (!fibril_mutex_trylock(&nodep->idx->lock)) {
175 fibril_mutex_unlock(&nodep->lock);
176 fibril_mutex_unlock(&ffn_mutex);
177 goto restart;
178 }
179 if (nodep->idx->service_id != service_id) {
180 fibril_mutex_unlock(&nodep->idx->lock);
181 fibril_mutex_unlock(&nodep->lock);
182 continue;
183 }
184
185 list_remove(&nodep->ffn_link);
186 fibril_mutex_unlock(&ffn_mutex);
187
188 /*
189 * We can unlock the node and its index structure because we are
190 * the last player on this playground and VFS is preventing new
191 * players from entering.
192 */
193 fibril_mutex_unlock(&nodep->idx->lock);
194 fibril_mutex_unlock(&nodep->lock);
195
196 if (nodep->dirty) {
197 rc = exfat_node_sync(nodep);
198 if (rc != EOK)
199 return rc;
200 }
201 nodep->idx->nodep = NULL;
202 free(nodep->bp);
203 free(nodep);
204
205 /* Need to restart because we changed the ffn_list. */
206 goto restart;
207 }
208 fibril_mutex_unlock(&ffn_mutex);
209
210 return EOK;
211}
212
213static errno_t exfat_node_get_new(exfat_node_t **nodepp)
214{
215 fs_node_t *fn;
216 exfat_node_t *nodep;
217 errno_t rc;
218
219 fibril_mutex_lock(&ffn_mutex);
220 if (!list_empty(&ffn_list)) {
221 /* Try to use a cached free node structure. */
222 exfat_idx_t *idxp_tmp;
223 nodep = list_get_instance(list_first(&ffn_list), exfat_node_t,
224 ffn_link);
225 if (!fibril_mutex_trylock(&nodep->lock))
226 goto skip_cache;
227 idxp_tmp = nodep->idx;
228 if (!fibril_mutex_trylock(&idxp_tmp->lock)) {
229 fibril_mutex_unlock(&nodep->lock);
230 goto skip_cache;
231 }
232 list_remove(&nodep->ffn_link);
233 fibril_mutex_unlock(&ffn_mutex);
234 if (nodep->dirty) {
235 rc = exfat_node_sync(nodep);
236 if (rc != EOK) {
237 idxp_tmp->nodep = NULL;
238 fibril_mutex_unlock(&nodep->lock);
239 fibril_mutex_unlock(&idxp_tmp->lock);
240 free(nodep->bp);
241 free(nodep);
242 return rc;
243 }
244 }
245 idxp_tmp->nodep = NULL;
246 fibril_mutex_unlock(&nodep->lock);
247 fibril_mutex_unlock(&idxp_tmp->lock);
248 fn = FS_NODE(nodep);
249 } else {
250skip_cache:
251 /* Try to allocate a new node structure. */
252 fibril_mutex_unlock(&ffn_mutex);
253 fn = (fs_node_t *)malloc(sizeof(fs_node_t));
254 if (!fn)
255 return ENOMEM;
256 nodep = (exfat_node_t *)malloc(sizeof(exfat_node_t));
257 if (!nodep) {
258 free(fn);
259 return ENOMEM;
260 }
261 }
262 exfat_node_initialize(nodep);
263 fs_node_initialize(fn);
264 fn->data = nodep;
265 nodep->bp = fn;
266
267 *nodepp = nodep;
268 return EOK;
269}
270
271static errno_t exfat_node_get_new_by_pos(exfat_node_t **nodepp,
272 service_id_t service_id, exfat_cluster_t pfc, unsigned pdi)
273{
274 exfat_idx_t *idxp = exfat_idx_get_by_pos(service_id, pfc, pdi);
275 if (!idxp)
276 return ENOMEM;
277 if (exfat_node_get_new(nodepp) != EOK)
278 return ENOMEM;
279 (*nodepp)->idx = idxp;
280 idxp->nodep = *nodepp;
281 return EOK;
282}
283
284
285/** Internal version of exfat_node_get().
286 *
287 * @param idxp Locked index structure.
288 */
289static errno_t exfat_node_get_core(exfat_node_t **nodepp, exfat_idx_t *idxp)
290{
291 exfat_dentry_t *d;
292 exfat_node_t *nodep = NULL;
293 exfat_directory_t di;
294 errno_t rc;
295
296 if (idxp->nodep) {
297 /*
298 * We are lucky.
299 * The node is already instantiated in memory.
300 */
301 fibril_mutex_lock(&idxp->nodep->lock);
302 if (!idxp->nodep->refcnt++) {
303 fibril_mutex_lock(&ffn_mutex);
304 list_remove(&idxp->nodep->ffn_link);
305 fibril_mutex_unlock(&ffn_mutex);
306 }
307 fibril_mutex_unlock(&idxp->nodep->lock);
308 *nodepp = idxp->nodep;
309 return EOK;
310 }
311
312 /*
313 * We must instantiate the node from the file system.
314 */
315
316 assert(idxp->pfc);
317
318 rc = exfat_node_get_new(&nodep);
319 if (rc != EOK)
320 return rc;
321
322 exfat_directory_open_parent(&di, idxp->service_id, idxp->pfc,
323 idxp->parent_fragmented);
324 rc = exfat_directory_seek(&di, idxp->pdi);
325 if (rc != EOK) {
326 (void) exfat_directory_close(&di);
327 (void) exfat_node_put(FS_NODE(nodep));
328 return rc;
329 }
330 rc = exfat_directory_get(&di, &d);
331 if (rc != EOK) {
332 (void) exfat_directory_close(&di);
333 (void) exfat_node_put(FS_NODE(nodep));
334 return rc;
335 }
336
337 switch (exfat_classify_dentry(d)) {
338 case EXFAT_DENTRY_FILE:
339 nodep->type =
340 (uint16_t_le2host(d->file.attr) & EXFAT_ATTR_SUBDIR) ?
341 EXFAT_DIRECTORY : EXFAT_FILE;
342 rc = exfat_directory_next(&di);
343 if (rc != EOK) {
344 (void) exfat_directory_close(&di);
345 (void) exfat_node_put(FS_NODE(nodep));
346 return rc;
347 }
348 rc = exfat_directory_get(&di, &d);
349 if (rc != EOK) {
350 (void) exfat_directory_close(&di);
351 (void) exfat_node_put(FS_NODE(nodep));
352 return rc;
353 }
354 nodep->firstc = uint32_t_le2host(d->stream.firstc);
355 nodep->size = uint64_t_le2host(d->stream.data_size);
356 nodep->fragmented = (d->stream.flags & 0x02) == 0;
357 break;
358 case EXFAT_DENTRY_BITMAP:
359 nodep->type = EXFAT_BITMAP;
360 nodep->firstc = uint32_t_le2host(d->bitmap.firstc);
361 nodep->size = uint64_t_le2host(d->bitmap.size);
362 nodep->fragmented = true;
363 break;
364 case EXFAT_DENTRY_UCTABLE:
365 nodep->type = EXFAT_UCTABLE;
366 nodep->firstc = uint32_t_le2host(d->uctable.firstc);
367 nodep->size = uint64_t_le2host(d->uctable.size);
368 nodep->fragmented = true;
369 break;
370 default:
371 case EXFAT_DENTRY_SKIP:
372 case EXFAT_DENTRY_LAST:
373 case EXFAT_DENTRY_FREE:
374 case EXFAT_DENTRY_VOLLABEL:
375 case EXFAT_DENTRY_GUID:
376 case EXFAT_DENTRY_STREAM:
377 case EXFAT_DENTRY_NAME:
378 (void) exfat_directory_close(&di);
379 (void) exfat_node_put(FS_NODE(nodep));
380 return ENOENT;
381 }
382
383 nodep->lnkcnt = 1;
384 nodep->refcnt = 1;
385
386 rc = exfat_directory_close(&di);
387 if (rc != EOK) {
388 (void) exfat_node_put(FS_NODE(nodep));
389 return rc;
390 }
391
392 /* Link the idx structure with the node structure. */
393 nodep->idx = idxp;
394 idxp->nodep = nodep;
395
396 *nodepp = nodep;
397 return EOK;
398}
399
400errno_t exfat_node_expand(service_id_t service_id, exfat_node_t *nodep,
401 exfat_cluster_t clusters)
402{
403 exfat_bs_t *bs;
404 errno_t rc;
405 bs = block_bb_get(service_id);
406
407 if (!nodep->fragmented) {
408 rc = exfat_bitmap_append_clusters(bs, nodep, clusters);
409 if (rc != ENOSPC)
410 return rc;
411 if (rc == ENOSPC) {
412 nodep->fragmented = true;
413 nodep->dirty = true; /* need to sync node */
414 rc = exfat_bitmap_replicate_clusters(bs, nodep);
415 if (rc != EOK)
416 return rc;
417 }
418 }
419
420 /* If we cant linear expand the node, we should use FAT instead */
421 exfat_cluster_t mcl, lcl;
422
423 /* create an independent chain of nclsts clusters in all FATs */
424 rc = exfat_alloc_clusters(bs, service_id, clusters, &mcl, &lcl);
425 if (rc != EOK)
426 return rc;
427 rc = exfat_zero_cluster(bs, service_id, mcl);
428 if (rc != EOK) {
429 (void) exfat_free_clusters(bs, service_id, mcl);
430 return rc;
431 }
432 /*
433 * Append the cluster chain starting in mcl to the end of the
434 * node's cluster chain.
435 */
436 rc = exfat_append_clusters(bs, nodep, mcl, lcl);
437 if (rc != EOK) {
438 (void) exfat_free_clusters(bs, service_id, mcl);
439 return rc;
440 }
441
442 return EOK;
443}
444
445static errno_t exfat_node_shrink(service_id_t service_id, exfat_node_t *nodep,
446 aoff64_t size)
447{
448 exfat_bs_t *bs;
449 errno_t rc;
450 bs = block_bb_get(service_id);
451
452 if (!nodep->fragmented) {
453 exfat_cluster_t clsts, prev_clsts, new_clsts;
454 prev_clsts = ROUND_UP(nodep->size, BPC(bs)) / BPC(bs);
455 new_clsts = ROUND_UP(size, BPC(bs)) / BPC(bs);
456
457 assert(new_clsts < prev_clsts);
458
459 clsts = prev_clsts - new_clsts;
460 rc = exfat_bitmap_free_clusters(bs, nodep, clsts);
461 if (rc != EOK)
462 return rc;
463 } else {
464 /*
465 * The node will be shrunk, clusters will be deallocated.
466 */
467 if (size == 0) {
468 rc = exfat_chop_clusters(bs, nodep, 0);
469 if (rc != EOK)
470 return rc;
471 } else {
472 exfat_cluster_t lastc;
473 rc = exfat_cluster_walk(bs, service_id, nodep->firstc,
474 &lastc, NULL, (size - 1) / BPC(bs));
475 if (rc != EOK)
476 return rc;
477 rc = exfat_chop_clusters(bs, nodep, lastc);
478 if (rc != EOK)
479 return rc;
480 }
481 }
482
483 nodep->size = size;
484 nodep->dirty = true; /* need to sync node */
485 return EOK;
486}
487
488
489/*
490 * EXFAT libfs operations.
491 */
492
493errno_t exfat_root_get(fs_node_t **rfn, service_id_t service_id)
494{
495 return exfat_node_get(rfn, service_id, EXFAT_ROOT_IDX);
496}
497
498errno_t exfat_bitmap_get(fs_node_t **rfn, service_id_t service_id)
499{
500 return exfat_node_get(rfn, service_id, EXFAT_BITMAP_IDX);
501}
502
503errno_t exfat_uctable_get(fs_node_t **rfn, service_id_t service_id)
504{
505 return exfat_node_get(rfn, service_id, EXFAT_UCTABLE_IDX);
506}
507
508
509errno_t exfat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
510{
511 exfat_node_t *parentp = EXFAT_NODE(pfn);
512 char name[EXFAT_FILENAME_LEN + 1];
513 exfat_file_dentry_t df;
514 exfat_stream_dentry_t ds;
515 service_id_t service_id;
516 errno_t rc;
517
518 fibril_mutex_lock(&parentp->idx->lock);
519 service_id = parentp->idx->service_id;
520 fibril_mutex_unlock(&parentp->idx->lock);
521
522 exfat_directory_t di;
523 rc = exfat_directory_open(parentp, &di);
524 if (rc != EOK)
525 return rc;
526
527 while (exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN, &df,
528 &ds) == EOK) {
529 if (str_casecmp(name, component) == 0) {
530 /* hit */
531 exfat_node_t *nodep;
532 aoff64_t o = di.pos %
533 (BPS(di.bs) / sizeof(exfat_dentry_t));
534 exfat_idx_t *idx = exfat_idx_get_by_pos(service_id,
535 parentp->firstc, di.bnum * DPS(di.bs) + o);
536 if (!idx) {
537 /*
538 * Can happen if memory is low or if we
539 * run out of 32-bit indices.
540 */
541 rc = exfat_directory_close(&di);
542 return (rc == EOK) ? ENOMEM : rc;
543 }
544 rc = exfat_node_get_core(&nodep, idx);
545 fibril_mutex_unlock(&idx->lock);
546 if (rc != EOK) {
547 (void) exfat_directory_close(&di);
548 return rc;
549 }
550 *rfn = FS_NODE(nodep);
551 rc = exfat_directory_close(&di);
552 if (rc != EOK)
553 (void) exfat_node_put(*rfn);
554 return rc;
555 } else {
556 rc = exfat_directory_next(&di);
557 if (rc != EOK)
558 break;
559 }
560 }
561 (void) exfat_directory_close(&di);
562 *rfn = NULL;
563 return EOK;
564}
565
566/** Instantiate a exFAT in-core node. */
567errno_t exfat_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
568{
569 exfat_node_t *nodep;
570 exfat_idx_t *idxp;
571 errno_t rc;
572
573 idxp = exfat_idx_get_by_index(service_id, index);
574 if (!idxp) {
575 *rfn = NULL;
576 return EOK;
577 }
578 /* idxp->lock held */
579 rc = exfat_node_get_core(&nodep, idxp);
580 fibril_mutex_unlock(&idxp->lock);
581 if (rc == EOK)
582 *rfn = FS_NODE(nodep);
583 return rc;
584}
585
586errno_t exfat_node_open(fs_node_t *fn)
587{
588 /*
589 * Opening a file is stateless, nothing
590 * to be done here.
591 */
592 return EOK;
593}
594
595errno_t exfat_node_put(fs_node_t *fn)
596{
597 if (fn == NULL)
598 return EOK;
599
600 exfat_node_t *nodep = EXFAT_NODE(fn);
601 bool destroy = false;
602
603 fibril_mutex_lock(&nodep->lock);
604 if (!--nodep->refcnt) {
605 if (nodep->idx) {
606 fibril_mutex_lock(&ffn_mutex);
607 list_append(&nodep->ffn_link, &ffn_list);
608 fibril_mutex_unlock(&ffn_mutex);
609 } else {
610 /*
611 * The node does not have any index structure associated
612 * with itself. This can only mean that we are releasing
613 * the node after a failed attempt to allocate the index
614 * structure for it.
615 */
616 destroy = true;
617 }
618 }
619 fibril_mutex_unlock(&nodep->lock);
620 if (destroy) {
621 free(nodep->bp);
622 free(nodep);
623 }
624 return EOK;
625}
626
627errno_t exfat_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
628{
629 exfat_idx_t *idxp;
630 exfat_node_t *nodep;
631 exfat_bs_t *bs;
632 errno_t rc;
633
634 bs = block_bb_get(service_id);
635 rc = exfat_node_get_new(&nodep);
636 if (rc != EOK)
637 return rc;
638
639 rc = exfat_idx_get_new(&idxp, service_id);
640 if (rc != EOK) {
641 (void) exfat_node_put(FS_NODE(nodep));
642 return rc;
643 }
644
645 nodep->firstc = 0;
646 nodep->size = 0;
647 nodep->fragmented = false;
648 nodep->lnkcnt = 0; /* not linked anywhere */
649 nodep->refcnt = 1;
650 nodep->dirty = true;
651
652 nodep->idx = idxp;
653 idxp->nodep = nodep;
654 fibril_mutex_unlock(&idxp->lock);
655
656 if (flags & L_DIRECTORY) {
657 nodep->type = EXFAT_DIRECTORY;
658 rc = exfat_node_expand(service_id, nodep, 1);
659 if (rc != EOK) {
660 (void) exfat_node_put(FS_NODE(nodep));
661 return rc;
662 }
663
664 rc = exfat_zero_cluster(bs, service_id, nodep->firstc);
665 if (rc != EOK) {
666 (void) exfat_node_put(FS_NODE(nodep));
667 return rc;
668 }
669
670 nodep->size = BPC(bs);
671 } else {
672 nodep->type = EXFAT_FILE;
673 }
674
675 *rfn = FS_NODE(nodep);
676 return EOK;
677}
678
679errno_t exfat_destroy_node(fs_node_t *fn)
680{
681 exfat_node_t *nodep = EXFAT_NODE(fn);
682 exfat_bs_t *bs;
683 bool has_children;
684 errno_t rc;
685
686 /*
687 * The node is not reachable from the file system. This means that the
688 * link count should be zero and that the index structure cannot be
689 * found in the position hash. Obviously, we don't need to lock the node
690 * nor its index structure.
691 */
692 assert(nodep->lnkcnt == 0);
693
694 /*
695 * The node may not have any children.
696 */
697 rc = exfat_has_children(&has_children, fn);
698 if (rc != EOK)
699 return rc;
700 assert(!has_children);
701
702 bs = block_bb_get(nodep->idx->service_id);
703 if (nodep->firstc != 0) {
704 assert(nodep->size);
705 /* Free all clusters allocated to the node. */
706 if (nodep->fragmented)
707 rc = exfat_free_clusters(bs, nodep->idx->service_id,
708 nodep->firstc);
709 else
710 rc = exfat_bitmap_free_clusters(bs, nodep,
711 ROUND_UP(nodep->size, BPC(bs)) / BPC(bs));
712 }
713
714 exfat_idx_destroy(nodep->idx);
715 free(nodep->bp);
716 free(nodep);
717 return rc;
718}
719
720errno_t exfat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
721{
722 exfat_node_t *parentp = EXFAT_NODE(pfn);
723 exfat_node_t *childp = EXFAT_NODE(cfn);
724 exfat_directory_t di;
725 errno_t rc;
726
727 fibril_mutex_lock(&childp->lock);
728 if (childp->lnkcnt == 1) {
729 /*
730 * We don't support multiple hard links.
731 */
732 fibril_mutex_unlock(&childp->lock);
733 return EMLINK;
734 }
735 assert(childp->lnkcnt == 0);
736 fibril_mutex_unlock(&childp->lock);
737
738 if (!exfat_valid_name(name))
739 return ENOTSUP;
740
741 fibril_mutex_lock(&parentp->idx->lock);
742 rc = exfat_directory_open(parentp, &di);
743 if (rc != EOK)
744 return rc;
745 /*
746 * At this point we only establish the link between the parent and the
747 * child. The dentry, except of the name and the extension, will remain
748 * uninitialized until the corresponding node is synced. Thus the valid
749 * dentry data is kept in the child node structure.
750 */
751 rc = exfat_directory_write_file(&di, name);
752 if (rc != EOK) {
753 (void) exfat_directory_close(&di);
754 fibril_mutex_unlock(&parentp->idx->lock);
755 return rc;
756 }
757 rc = exfat_directory_close(&di);
758 if (rc != EOK) {
759 fibril_mutex_unlock(&parentp->idx->lock);
760 return rc;
761 }
762
763 fibril_mutex_unlock(&parentp->idx->lock);
764 fibril_mutex_lock(&childp->idx->lock);
765
766 childp->idx->pfc = parentp->firstc;
767 childp->idx->parent_fragmented = parentp->fragmented;
768 childp->idx->pdi = di.pos;
769 fibril_mutex_unlock(&childp->idx->lock);
770
771 fibril_mutex_lock(&childp->lock);
772 childp->lnkcnt = 1;
773 childp->dirty = true; /* need to sync node */
774 fibril_mutex_unlock(&childp->lock);
775
776 /*
777 * Hash in the index structure into the position hash.
778 */
779 exfat_idx_hashin(childp->idx);
780
781 return EOK;
782
783}
784
785errno_t exfat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
786{
787 exfat_node_t *parentp = EXFAT_NODE(pfn);
788 exfat_node_t *childp = EXFAT_NODE(cfn);
789 bool has_children;
790 errno_t rc;
791
792 if (!parentp)
793 return EBUSY;
794
795 rc = exfat_has_children(&has_children, cfn);
796 if (rc != EOK)
797 return rc;
798 if (has_children)
799 return ENOTEMPTY;
800
801 fibril_mutex_lock(&parentp->lock);
802 fibril_mutex_lock(&childp->lock);
803 assert(childp->lnkcnt == 1);
804 fibril_mutex_lock(&childp->idx->lock);
805
806 exfat_directory_t di;
807 rc = exfat_directory_open(parentp,&di);
808 if (rc != EOK)
809 goto error;
810 rc = exfat_directory_erase_file(&di, childp->idx->pdi);
811 if (rc != EOK)
812 goto error;
813 rc = exfat_directory_close(&di);
814 if (rc != EOK)
815 goto error;
816
817 /* remove the index structure from the position hash */
818 exfat_idx_hashout(childp->idx);
819 /* clear position information */
820 childp->idx->pfc = 0;
821 childp->idx->pdi = 0;
822 fibril_mutex_unlock(&childp->idx->lock);
823 childp->lnkcnt = 0;
824 childp->refcnt++; /* keep the node in memory until destroyed */
825 childp->dirty = true;
826 fibril_mutex_unlock(&childp->lock);
827 fibril_mutex_unlock(&parentp->lock);
828
829 return EOK;
830
831error:
832 (void) exfat_directory_close(&di);
833 fibril_mutex_unlock(&childp->idx->lock);
834 fibril_mutex_unlock(&childp->lock);
835 fibril_mutex_unlock(&parentp->lock);
836 return rc;
837
838}
839
840errno_t exfat_has_children(bool *has_children, fs_node_t *fn)
841{
842 exfat_directory_t di;
843 exfat_dentry_t *d;
844 exfat_node_t *nodep = EXFAT_NODE(fn);
845 errno_t rc;
846
847 *has_children = false;
848
849 if (nodep->type != EXFAT_DIRECTORY)
850 return EOK;
851
852 fibril_mutex_lock(&nodep->idx->lock);
853
854 rc = exfat_directory_open(nodep, &di);
855 if (rc != EOK) {
856 fibril_mutex_unlock(&nodep->idx->lock);
857 return rc;
858 }
859
860 do {
861 rc = exfat_directory_get(&di, &d);
862 if (rc != EOK) {
863 (void) exfat_directory_close(&di);
864 fibril_mutex_unlock(&nodep->idx->lock);
865 return rc;
866 }
867 switch (exfat_classify_dentry(d)) {
868 case EXFAT_DENTRY_SKIP:
869 case EXFAT_DENTRY_FREE:
870 continue;
871 case EXFAT_DENTRY_LAST:
872 *has_children = false;
873 goto exit;
874 default:
875 *has_children = true;
876 goto exit;
877 }
878 } while (exfat_directory_next(&di) == EOK);
879
880exit:
881 rc = exfat_directory_close(&di);
882 fibril_mutex_unlock(&nodep->idx->lock);
883 return rc;
884}
885
886
887fs_index_t exfat_index_get(fs_node_t *fn)
888{
889 return EXFAT_NODE(fn)->idx->index;
890}
891
892aoff64_t exfat_size_get(fs_node_t *fn)
893{
894 return EXFAT_NODE(fn)->size;
895}
896
897unsigned exfat_lnkcnt_get(fs_node_t *fn)
898{
899 return EXFAT_NODE(fn)->lnkcnt;
900}
901
902bool exfat_is_directory(fs_node_t *fn)
903{
904 return EXFAT_NODE(fn)->type == EXFAT_DIRECTORY;
905}
906
907bool exfat_is_file(fs_node_t *fn)
908{
909 return EXFAT_NODE(fn)->type == EXFAT_FILE;
910}
911
912service_id_t exfat_service_get(fs_node_t *node)
913{
914 return 0;
915}
916
917errno_t exfat_size_block(service_id_t service_id, uint32_t *size)
918{
919 exfat_bs_t *bs;
920 bs = block_bb_get(service_id);
921 *size = BPC(bs);
922
923 return EOK;
924}
925
926errno_t exfat_total_block_count(service_id_t service_id, uint64_t *count)
927{
928 exfat_bs_t *bs;
929 bs = block_bb_get(service_id);
930 *count = DATA_CNT(bs);
931
932 return EOK;
933}
934
935errno_t exfat_free_block_count(service_id_t service_id, uint64_t *count)
936{
937 fs_node_t *node = NULL;
938 exfat_node_t *bmap_node;
939 exfat_bs_t *bs;
940 uint64_t free_block_count = 0;
941 uint64_t block_count;
942 unsigned sector;
943 errno_t rc;
944
945 rc = exfat_total_block_count(service_id, &block_count);
946 if (rc != EOK)
947 goto exit;
948
949 bs = block_bb_get(service_id);
950 rc = exfat_bitmap_get(&node, service_id);
951 if (rc != EOK)
952 goto exit;
953
954 bmap_node = (exfat_node_t *) node->data;
955
956 unsigned const bmap_sectors = ROUND_UP(bmap_node->size, BPS(bs)) /
957 BPS(bs);
958
959 for (sector = 0; sector < bmap_sectors; ++sector) {
960
961 block_t *block;
962 uint8_t *bitmap;
963 unsigned bit;
964
965 rc = exfat_block_get(&block, bs, bmap_node, sector,
966 BLOCK_FLAGS_NONE);
967 if (rc != EOK) {
968 free_block_count = 0;
969 goto exit;
970 }
971
972 bitmap = (uint8_t *) block->data;
973
974 for (bit = 0; bit < BPS(bs) * 8 && block_count > 0;
975 ++bit, --block_count) {
976 if (!(bitmap[bit / 8] & (1 << (bit % 8))))
977 ++free_block_count;
978 }
979
980 block_put(block);
981
982 if (block_count == 0) {
983 /* Reached the end of the bitmap */
984 goto exit;
985 }
986 }
987
988exit:
989 exfat_node_put(node);
990 *count = free_block_count;
991 return rc;
992}
993
994/** libfs operations */
995libfs_ops_t exfat_libfs_ops = {
996 .root_get = exfat_root_get,
997 .match = exfat_match,
998 .node_get = exfat_node_get,
999 .node_open = exfat_node_open,
1000 .node_put = exfat_node_put,
1001 .create = exfat_create_node,
1002 .destroy = exfat_destroy_node,
1003 .link = exfat_link,
1004 .unlink = exfat_unlink,
1005 .has_children = exfat_has_children,
1006 .index_get = exfat_index_get,
1007 .size_get = exfat_size_get,
1008 .lnkcnt_get = exfat_lnkcnt_get,
1009 .is_directory = exfat_is_directory,
1010 .is_file = exfat_is_file,
1011 .service_get = exfat_service_get,
1012 .size_block = exfat_size_block,
1013 .total_block_count = exfat_total_block_count,
1014 .free_block_count = exfat_free_block_count
1015};
1016
1017static errno_t exfat_fs_open(service_id_t service_id, enum cache_mode cmode,
1018 fs_node_t **rrfn, exfat_idx_t **rridxp, vfs_fs_probe_info_t *info)
1019{
1020 errno_t rc;
1021 exfat_node_t *rootp = NULL, *bitmapp = NULL, *uctablep = NULL;
1022 exfat_bs_t *bs;
1023
1024 /* initialize libblock */
1025 rc = block_init(service_id, BS_SIZE);
1026 if (rc != EOK)
1027 return rc;
1028
1029 /* prepare the boot block */
1030 rc = block_bb_read(service_id, BS_BLOCK);
1031 if (rc != EOK) {
1032 block_fini(service_id);
1033 return rc;
1034 }
1035
1036 /* get the buffer with the boot sector */
1037 bs = block_bb_get(service_id);
1038
1039 /* Do some simple sanity checks on the file system. */
1040 rc = exfat_sanity_check(bs);
1041 if (rc != EOK) {
1042 (void) block_cache_fini(service_id);
1043 block_fini(service_id);
1044 return rc;
1045 }
1046
1047 /* Initialize the block cache */
1048 rc = block_cache_init(service_id, BPS(bs), 0 /* XXX */, cmode);
1049 if (rc != EOK) {
1050 block_fini(service_id);
1051 return rc;
1052 }
1053
1054 rc = exfat_idx_init_by_service_id(service_id);
1055 if (rc != EOK) {
1056 (void) block_cache_fini(service_id);
1057 block_fini(service_id);
1058 return rc;
1059 }
1060
1061 /* Initialize the root node. */
1062 rc = exfat_node_get_new_by_pos(&rootp, service_id, EXFAT_ROOT_PAR,
1063 EXFAT_ROOT_POS);
1064 if (rc!=EOK) {
1065 (void) block_cache_fini(service_id);
1066 block_fini(service_id);
1067 exfat_idx_fini_by_service_id(service_id);
1068 return ENOMEM;
1069 }
1070 assert(rootp->idx->index == EXFAT_ROOT_IDX);
1071
1072 rootp->type = EXFAT_DIRECTORY;
1073 rootp->firstc = ROOT_FC(bs);
1074 rootp->fragmented = true;
1075 rootp->refcnt = 1;
1076 rootp->lnkcnt = 0; /* FS root is not linked */
1077
1078 uint32_t clusters;
1079 rc = exfat_clusters_get(&clusters, bs, service_id, rootp->firstc);
1080 if (rc != EOK) {
1081 free(rootp);
1082 (void) block_cache_fini(service_id);
1083 block_fini(service_id);
1084 exfat_idx_fini_by_service_id(service_id);
1085 return ENOTSUP;
1086 }
1087 rootp->size = BPS(bs) * SPC(bs) * clusters;
1088 fibril_mutex_unlock(&rootp->idx->lock);
1089
1090 /* Open root directory and looking for Bitmap and UC-Table */
1091 exfat_directory_t di;
1092 exfat_dentry_t *de;
1093 rc = exfat_directory_open(rootp, &di);
1094 if (rc != EOK) {
1095 free(rootp);
1096 (void) block_cache_fini(service_id);
1097 block_fini(service_id);
1098 exfat_idx_fini_by_service_id(service_id);
1099 return ENOTSUP;
1100 }
1101
1102 /* Initialize the bitmap node. */
1103 rc = exfat_directory_find(&di, EXFAT_DENTRY_BITMAP, &de);
1104 if (rc != EOK) {
1105 free(rootp);
1106 (void) block_cache_fini(service_id);
1107 block_fini(service_id);
1108 exfat_idx_fini_by_service_id(service_id);
1109 return ENOTSUP;
1110 }
1111
1112 rc = exfat_node_get_new_by_pos(&bitmapp, service_id, rootp->firstc,
1113 di.pos);
1114 if (rc != EOK) {
1115 free(rootp);
1116 (void) block_cache_fini(service_id);
1117 block_fini(service_id);
1118 exfat_idx_fini_by_service_id(service_id);
1119 return ENOMEM;
1120 }
1121 assert(bitmapp->idx->index == EXFAT_BITMAP_IDX);
1122 fibril_mutex_unlock(&bitmapp->idx->lock);
1123
1124 bitmapp->type = EXFAT_BITMAP;
1125 bitmapp->firstc = uint32_t_le2host(de->bitmap.firstc);
1126 bitmapp->fragmented = true;
1127 bitmapp->idx->parent_fragmented = true;
1128 bitmapp->refcnt = 1;
1129 bitmapp->lnkcnt = 0;
1130 bitmapp->size = uint64_t_le2host(de->bitmap.size);
1131
1132 /* Initialize the uctable node. */
1133 rc = exfat_directory_seek(&di, 0);
1134 if (rc != EOK) {
1135 free(rootp);
1136 free(bitmapp);
1137 (void) block_cache_fini(service_id);
1138 block_fini(service_id);
1139 exfat_idx_fini_by_service_id(service_id);
1140 return ENOTSUP;
1141 }
1142
1143 rc = exfat_directory_find(&di, EXFAT_DENTRY_UCTABLE, &de);
1144 if (rc != EOK) {
1145 free(rootp);
1146 free(bitmapp);
1147 (void) block_cache_fini(service_id);
1148 block_fini(service_id);
1149 exfat_idx_fini_by_service_id(service_id);
1150 return ENOTSUP;
1151 }
1152
1153 rc = exfat_node_get_new_by_pos(&uctablep, service_id, rootp->firstc,
1154 di.pos);
1155 if (rc != EOK) {
1156 free(rootp);
1157 free(bitmapp);
1158 (void) block_cache_fini(service_id);
1159 block_fini(service_id);
1160 exfat_idx_fini_by_service_id(service_id);
1161 return ENOMEM;
1162 }
1163 assert(uctablep->idx->index == EXFAT_UCTABLE_IDX);
1164 fibril_mutex_unlock(&uctablep->idx->lock);
1165
1166 uctablep->type = EXFAT_UCTABLE;
1167 uctablep->firstc = uint32_t_le2host(de->uctable.firstc);
1168 uctablep->fragmented = true;
1169 uctablep->idx->parent_fragmented = true;
1170 uctablep->refcnt = 1;
1171 uctablep->lnkcnt = 0;
1172 uctablep->size = uint64_t_le2host(de->uctable.size);
1173
1174 if (info != NULL) {
1175 /* Read volume label. */
1176 rc = exfat_directory_read_vollabel(&di, info->label,
1177 FS_LABEL_MAXLEN + 1);
1178 if (rc != EOK) {
1179 free(rootp);
1180 free(bitmapp);
1181 free(uctablep);
1182 (void) block_cache_fini(service_id);
1183 block_fini(service_id);
1184 exfat_idx_fini_by_service_id(service_id);
1185 return ENOTSUP;
1186 }
1187 }
1188
1189 rc = exfat_directory_close(&di);
1190 if (rc != EOK) {
1191 free(rootp);
1192 free(bitmapp);
1193 free(uctablep);
1194 (void) block_cache_fini(service_id);
1195 block_fini(service_id);
1196 exfat_idx_fini_by_service_id(service_id);
1197 return ENOMEM;
1198 }
1199
1200 /* exfat_fsinfo(bs, service_id); */
1201
1202 *rrfn = FS_NODE(rootp);
1203 *rridxp = rootp->idx;
1204
1205 if (info != NULL) {
1206// str_cpy(info->label, FS_LABEL_MAXLEN + 1, label);
1207 }
1208
1209 return EOK;
1210}
1211
1212static void exfat_fs_close(service_id_t service_id, fs_node_t *rfn)
1213{
1214 /*
1215 * Put the root node and force it to the FAT free node list.
1216 */
1217 (void) exfat_node_put(rfn);
1218 (void) exfat_node_put(rfn);
1219
1220 /*
1221 * Perform cleanup of the node structures, index structures and
1222 * associated data. Write back this file system's dirty blocks and
1223 * stop using libblock for this instance.
1224 */
1225 (void) exfat_node_fini_by_service_id(service_id);
1226 exfat_idx_fini_by_service_id(service_id);
1227 (void) block_cache_fini(service_id);
1228 block_fini(service_id);
1229}
1230
1231
1232/*
1233 * VFS_OUT operations.
1234 */
1235
1236/* Print debug info */
1237/*
1238static void exfat_fsinfo(exfat_bs_t *bs, service_id_t service_id)
1239{
1240 printf("exFAT file system mounted\n");
1241 printf("Version: %d.%d\n", bs->version.major, bs->version.minor);
1242 printf("Volume serial: %d\n", uint32_t_le2host(bs->volume_serial));
1243 printf("Volume first sector: %lld\n", VOL_FS(bs));
1244 printf("Volume sectors: %lld\n", VOL_CNT(bs));
1245 printf("FAT first sector: %d\n", FAT_FS(bs));
1246 printf("FAT sectors: %d\n", FAT_CNT(bs));
1247 printf("Data first sector: %d\n", DATA_FS(bs));
1248 printf("Data sectors: %d\n", DATA_CNT(bs));
1249 printf("Root dir first cluster: %d\n", ROOT_FC(bs));
1250 printf("Bytes per sector: %d\n", BPS(bs));
1251 printf("Sectors per cluster: %d\n", SPC(bs));
1252 printf("KBytes per cluster: %d\n", SPC(bs)*BPS(bs)/1024);
1253
1254 int i, rc;
1255 exfat_cluster_t clst;
1256 for (i=0; i<=7; i++) {
1257 rc = exfat_get_cluster(bs, service_id, i, &clst);
1258 if (rc != EOK)
1259 return;
1260 printf("Clst %d: %x", i, clst);
1261 if (i>=2)
1262 printf(", Bitmap: %d\n", bitmap_is_free(bs, service_id, i)!=EOK);
1263 else
1264 printf("\n");
1265 }
1266}
1267*/
1268
1269static errno_t exfat_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
1270{
1271 errno_t rc;
1272 exfat_idx_t *ridxp;
1273 fs_node_t *rfn;
1274
1275 rc = exfat_fs_open(service_id, CACHE_MODE_WT, &rfn, &ridxp, info);
1276 if (rc != EOK)
1277 return rc;
1278
1279 exfat_fs_close(service_id, rfn);
1280 return EOK;
1281}
1282
1283static errno_t
1284exfat_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
1285 aoff64_t *size)
1286{
1287 errno_t rc;
1288 enum cache_mode cmode;
1289 exfat_idx_t *ridxp;
1290 fs_node_t *rfn;
1291
1292 /* Check for option enabling write through. */
1293 if (str_cmp(opts, "wtcache") == 0)
1294 cmode = CACHE_MODE_WT;
1295 else
1296 cmode = CACHE_MODE_WB;
1297
1298 rc = exfat_fs_open(service_id, cmode, &rfn, &ridxp, NULL);
1299 if (rc != EOK)
1300 return rc;
1301
1302 *index = ridxp->index;
1303 *size = EXFAT_NODE(rfn)->size;
1304
1305 return EOK;
1306}
1307
1308static errno_t exfat_unmounted(service_id_t service_id)
1309{
1310 fs_node_t *rfn;
1311 errno_t rc;
1312
1313 rc = exfat_root_get(&rfn, service_id);
1314 if (rc != EOK)
1315 return rc;
1316
1317 exfat_fs_close(service_id, rfn);
1318 return EOK;
1319}
1320
1321static errno_t
1322exfat_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
1323 size_t *rbytes)
1324{
1325 fs_node_t *fn;
1326 exfat_node_t *nodep;
1327 exfat_bs_t *bs;
1328 size_t bytes = 0;
1329 block_t *b;
1330 errno_t rc;
1331
1332 rc = exfat_node_get(&fn, service_id, index);
1333 if (rc != EOK)
1334 return rc;
1335 if (!fn)
1336 return ENOENT;
1337 nodep = EXFAT_NODE(fn);
1338
1339 ipc_callid_t callid;
1340 size_t len;
1341 if (!async_data_read_receive(&callid, &len)) {
1342 exfat_node_put(fn);
1343 async_answer_0(callid, EINVAL);
1344 return EINVAL;
1345 }
1346
1347 bs = block_bb_get(service_id);
1348
1349 if (nodep->type == EXFAT_FILE) {
1350 /*
1351 * Our strategy for regular file reads is to read one block at
1352 * most and make use of the possibility to return less data than
1353 * requested. This keeps the code very simple.
1354 */
1355 if (pos >= nodep->size) {
1356 /* reading beyond the EOF */
1357 bytes = 0;
1358 (void) async_data_read_finalize(callid, NULL, 0);
1359 } else {
1360 bytes = min(len, BPS(bs) - pos % BPS(bs));
1361 bytes = min(bytes, nodep->size - pos);
1362 rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs),
1363 BLOCK_FLAGS_NONE);
1364 if (rc != EOK) {
1365 exfat_node_put(fn);
1366 async_answer_0(callid, rc);
1367 return rc;
1368 }
1369 (void) async_data_read_finalize(callid,
1370 b->data + pos % BPS(bs), bytes);
1371 rc = block_put(b);
1372 if (rc != EOK) {
1373 exfat_node_put(fn);
1374 return rc;
1375 }
1376 }
1377 } else {
1378 if (nodep->type != EXFAT_DIRECTORY) {
1379 async_answer_0(callid, ENOTSUP);
1380 return ENOTSUP;
1381 }
1382
1383 aoff64_t spos = pos;
1384 char name[EXFAT_FILENAME_LEN + 1];
1385 exfat_file_dentry_t df;
1386 exfat_stream_dentry_t ds;
1387
1388 assert(nodep->size % BPS(bs) == 0);
1389 assert(BPS(bs) % sizeof(exfat_dentry_t) == 0);
1390
1391 exfat_directory_t di;
1392 rc = exfat_directory_open(nodep, &di);
1393 if (rc != EOK)
1394 goto err;
1395
1396 rc = exfat_directory_seek(&di, pos);
1397 if (rc != EOK) {
1398 (void) exfat_directory_close(&di);
1399 goto err;
1400 }
1401
1402 rc = exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN,
1403 &df, &ds);
1404 if (rc == EOK)
1405 goto hit;
1406 else if (rc == ENOENT)
1407 goto miss;
1408
1409 (void) exfat_directory_close(&di);
1410
1411err:
1412 (void) exfat_node_put(fn);
1413 async_answer_0(callid, rc);
1414 return rc;
1415
1416miss:
1417 rc = exfat_directory_close(&di);
1418 if (rc != EOK)
1419 goto err;
1420 rc = exfat_node_put(fn);
1421 async_answer_0(callid, rc != EOK ? rc : ENOENT);
1422 *rbytes = 0;
1423 return rc != EOK ? rc : ENOENT;
1424
1425hit:
1426 pos = di.pos;
1427 rc = exfat_directory_close(&di);
1428 if (rc != EOK)
1429 goto err;
1430 (void) async_data_read_finalize(callid, name,
1431 str_size(name) + 1);
1432 bytes = (pos - spos) + 1;
1433 }
1434
1435 rc = exfat_node_put(fn);
1436 *rbytes = bytes;
1437 return rc;
1438}
1439
1440static errno_t exfat_close(service_id_t service_id, fs_index_t index)
1441{
1442 return EOK;
1443}
1444
1445static errno_t exfat_sync(service_id_t service_id, fs_index_t index)
1446{
1447 fs_node_t *fn;
1448 errno_t rc = exfat_node_get(&fn, service_id, index);
1449 if (rc != EOK)
1450 return rc;
1451 if (!fn)
1452 return ENOENT;
1453
1454 exfat_node_t *nodep = EXFAT_NODE(fn);
1455
1456 nodep->dirty = true;
1457 rc = exfat_node_sync(nodep);
1458
1459 exfat_node_put(fn);
1460 return rc;
1461}
1462
1463static errno_t
1464exfat_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
1465 size_t *wbytes, aoff64_t *nsize)
1466{
1467 fs_node_t *fn;
1468 exfat_node_t *nodep;
1469 exfat_bs_t *bs;
1470 size_t bytes;
1471 block_t *b;
1472 aoff64_t boundary;
1473 int flags = BLOCK_FLAGS_NONE;
1474 errno_t rc;
1475
1476 rc = exfat_node_get(&fn, service_id, index);
1477 if (rc != EOK)
1478 return rc;
1479 if (!fn)
1480 return ENOENT;
1481 nodep = EXFAT_NODE(fn);
1482
1483 ipc_callid_t callid;
1484 size_t len;
1485 if (!async_data_write_receive(&callid, &len)) {
1486 (void) exfat_node_put(fn);
1487 async_answer_0(callid, EINVAL);
1488 return EINVAL;
1489 }
1490
1491 bs = block_bb_get(service_id);
1492
1493 /*
1494 * In all scenarios, we will attempt to write out only one block worth
1495 * of data at maximum. There might be some more efficient approaches,
1496 * but this one greatly simplifies fat_write(). Note that we can afford
1497 * to do this because the client must be ready to handle the return
1498 * value signalizing a smaller number of bytes written.
1499 */
1500 bytes = min(len, BPS(bs) - pos % BPS(bs));
1501 if (bytes == BPS(bs))
1502 flags |= BLOCK_FLAGS_NOREAD;
1503
1504 boundary = ROUND_UP(nodep->size, BPC(bs));
1505 if (pos >= boundary) {
1506 unsigned nclsts;
1507 nclsts = (ROUND_UP(pos + bytes, BPC(bs)) - boundary) / BPC(bs);
1508 rc = exfat_node_expand(service_id, nodep, nclsts);
1509 if (rc != EOK) {
1510 /* could not expand node */
1511 (void) exfat_node_put(fn);
1512 async_answer_0(callid, rc);
1513 return rc;
1514 }
1515 }
1516
1517 if (pos + bytes > nodep->size) {
1518 nodep->size = pos + bytes;
1519 nodep->dirty = true; /* need to sync node */
1520 }
1521
1522 /*
1523 * This is the easier case - we are either overwriting already
1524 * existing contents or writing behind the EOF, but still within
1525 * the limits of the last cluster. The node size may grow to the
1526 * next block size boundary.
1527 */
1528 rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs), flags);
1529 if (rc != EOK) {
1530 (void) exfat_node_put(fn);
1531 async_answer_0(callid, rc);
1532 return rc;
1533 }
1534
1535 (void) async_data_write_finalize(callid,
1536 b->data + pos % BPS(bs), bytes);
1537 b->dirty = true; /* need to sync block */
1538 rc = block_put(b);
1539 if (rc != EOK) {
1540 (void) exfat_node_put(fn);
1541 return rc;
1542 }
1543
1544 *wbytes = bytes;
1545 *nsize = nodep->size;
1546 rc = exfat_node_put(fn);
1547 return rc;
1548}
1549
1550static errno_t
1551exfat_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1552{
1553 fs_node_t *fn;
1554 exfat_node_t *nodep;
1555 exfat_bs_t *bs;
1556 errno_t rc;
1557
1558 rc = exfat_node_get(&fn, service_id, index);
1559 if (rc != EOK)
1560 return rc;
1561 if (!fn)
1562 return ENOENT;
1563 nodep = EXFAT_NODE(fn);
1564
1565 bs = block_bb_get(service_id);
1566
1567 if (nodep->size == size) {
1568 rc = EOK;
1569 } else if (nodep->size < size) {
1570 /*
1571 * The standard says we have the freedom to grow the node.
1572 * For now, we simply return an error.
1573 */
1574 rc = EINVAL;
1575 } else if (ROUND_UP(nodep->size, BPC(bs)) == ROUND_UP(size, BPC(bs))) {
1576 /*
1577 * The node will be shrunk, but no clusters will be deallocated.
1578 */
1579 nodep->size = size;
1580 nodep->dirty = true; /* need to sync node */
1581 rc = EOK;
1582 } else {
1583 rc = exfat_node_shrink(service_id, nodep, size);
1584 }
1585
1586 errno_t rc2 = exfat_node_put(fn);
1587 if (rc == EOK && rc2 != EOK)
1588 rc = rc2;
1589
1590 return rc;
1591}
1592
1593static errno_t exfat_destroy(service_id_t service_id, fs_index_t index)
1594{
1595 fs_node_t *fn;
1596 exfat_node_t *nodep;
1597 errno_t rc;
1598
1599 rc = exfat_node_get(&fn, service_id, index);
1600 if (rc != EOK)
1601 return rc;
1602 if (!fn)
1603 return ENOENT;
1604
1605 nodep = EXFAT_NODE(fn);
1606 /*
1607 * We should have exactly two references. One for the above
1608 * call to fat_node_get() and one from fat_unlink().
1609 */
1610 assert(nodep->refcnt == 2);
1611
1612 rc = exfat_destroy_node(fn);
1613 return rc;
1614}
1615
1616vfs_out_ops_t exfat_ops = {
1617 .fsprobe = exfat_fsprobe,
1618 .mounted = exfat_mounted,
1619 .unmounted = exfat_unmounted,
1620 .read = exfat_read,
1621 .write = exfat_write,
1622 .truncate = exfat_truncate,
1623 .close = exfat_close,
1624 .destroy = exfat_destroy,
1625 .sync = exfat_sync,
1626};
1627
1628/**
1629 * @}
1630 */
Note: See TracBrowser for help on using the repository browser.