source: mainline/uspace/srv/fs/exfat/exfat_ops.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 38.1 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 exfat
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 void exfat_fsinfo(exfat_bs_t *, service_id_t);
75static errno_t exfat_root_get(fs_node_t **, service_id_t);
76static errno_t exfat_match(fs_node_t **, fs_node_t *, const char *);
77static errno_t exfat_node_get(fs_node_t **, service_id_t, fs_index_t);
78static errno_t exfat_node_open(fs_node_t *);
79/* static errno_t exfat_node_put(fs_node_t *); */
80static errno_t exfat_create_node(fs_node_t **, service_id_t, int);
81static errno_t exfat_destroy_node(fs_node_t *);
82static errno_t exfat_link(fs_node_t *, fs_node_t *, const char *);
83static errno_t exfat_unlink(fs_node_t *, fs_node_t *, const char *);
84static errno_t exfat_has_children(bool *, fs_node_t *);
85static fs_index_t exfat_index_get(fs_node_t *);
86static aoff64_t exfat_size_get(fs_node_t *);
87static unsigned exfat_lnkcnt_get(fs_node_t *);
88static bool exfat_is_directory(fs_node_t *);
89static bool exfat_is_file(fs_node_t *node);
90static service_id_t exfat_service_get(fs_node_t *node);
91static errno_t exfat_size_block(service_id_t, uint32_t *);
92static errno_t exfat_total_block_count(service_id_t, uint64_t *);
93static errno_t exfat_free_block_count(service_id_t, uint64_t *);
94
95/*
96 * Helper functions.
97 */
98static void exfat_node_initialize(exfat_node_t *node)
99{
100 fibril_mutex_initialize(&node->lock);
101 node->bp = NULL;
102 node->idx = NULL;
103 node->type = EXFAT_UNKNOW;
104 link_initialize(&node->ffn_link);
105 node->size = 0;
106 node->lnkcnt = 0;
107 node->refcnt = 0;
108 node->dirty = false;
109 node->fragmented = false;
110 node->lastc_cached_valid = false;
111 node->lastc_cached_value = 0;
112 node->currc_cached_valid = false;
113 node->currc_cached_bn = 0;
114 node->currc_cached_value = 0;
115}
116
117static errno_t exfat_node_sync(exfat_node_t *node)
118{
119 errno_t rc;
120 exfat_directory_t di;
121 exfat_file_dentry_t df;
122 exfat_stream_dentry_t ds;
123
124 if (!(node->type == EXFAT_DIRECTORY || node->type == EXFAT_FILE))
125 return EOK;
126
127 if (node->type == EXFAT_DIRECTORY)
128 df.attr = EXFAT_ATTR_SUBDIR;
129 else
130 df.attr = 0;
131
132 ds.firstc = node->firstc;
133 if (node->size == 0 && node->firstc == 0) {
134 ds.flags = 0;
135 } else {
136 ds.flags = 1;
137 ds.flags |= (!node->fragmented << 1);
138 }
139 ds.valid_data_size = node->size;
140 ds.data_size = node->size;
141
142 exfat_directory_open_parent(&di, node->idx->service_id, node->idx->pfc,
143 node->idx->parent_fragmented);
144 rc = exfat_directory_seek(&di, node->idx->pdi);
145 if (rc != EOK) {
146 (void) exfat_directory_close(&di);
147 return rc;
148 }
149
150 rc = exfat_directory_sync_file(&di, &df, &ds);
151 if (rc != EOK) {
152 (void) exfat_directory_close(&di);
153 return rc;
154 }
155 return exfat_directory_close(&di);
156}
157
158static errno_t exfat_node_fini_by_service_id(service_id_t service_id)
159{
160 errno_t rc;
161
162 /*
163 * We are called from fat_unmounted() and assume that there are already
164 * no nodes belonging to this instance with non-zero refcount. Therefore
165 * it is sufficient to clean up only the FAT free node list.
166 */
167
168restart:
169 fibril_mutex_lock(&ffn_mutex);
170 list_foreach(ffn_list, ffn_link, exfat_node_t, nodep) {
171 if (!fibril_mutex_trylock(&nodep->lock)) {
172 fibril_mutex_unlock(&ffn_mutex);
173 goto restart;
174 }
175 if (!fibril_mutex_trylock(&nodep->idx->lock)) {
176 fibril_mutex_unlock(&nodep->lock);
177 fibril_mutex_unlock(&ffn_mutex);
178 goto restart;
179 }
180 if (nodep->idx->service_id != service_id) {
181 fibril_mutex_unlock(&nodep->idx->lock);
182 fibril_mutex_unlock(&nodep->lock);
183 continue;
184 }
185
186 list_remove(&nodep->ffn_link);
187 fibril_mutex_unlock(&ffn_mutex);
188
189 /*
190 * We can unlock the node and its index structure because we are
191 * the last player on this playground and VFS is preventing new
192 * players from entering.
193 */
194 fibril_mutex_unlock(&nodep->idx->lock);
195 fibril_mutex_unlock(&nodep->lock);
196
197 if (nodep->dirty) {
198 rc = exfat_node_sync(nodep);
199 if (rc != EOK)
200 return rc;
201 }
202 nodep->idx->nodep = NULL;
203 free(nodep->bp);
204 free(nodep);
205
206 /* Need to restart because we changed the ffn_list. */
207 goto restart;
208 }
209 fibril_mutex_unlock(&ffn_mutex);
210
211 return EOK;
212}
213
214static errno_t exfat_node_get_new(exfat_node_t **nodepp)
215{
216 fs_node_t *fn;
217 exfat_node_t *nodep;
218 errno_t rc;
219
220 fibril_mutex_lock(&ffn_mutex);
221 if (!list_empty(&ffn_list)) {
222 /* Try to use a cached free node structure. */
223 exfat_idx_t *idxp_tmp;
224 nodep = list_get_instance(list_first(&ffn_list), exfat_node_t,
225 ffn_link);
226 if (!fibril_mutex_trylock(&nodep->lock))
227 goto skip_cache;
228 idxp_tmp = nodep->idx;
229 if (!fibril_mutex_trylock(&idxp_tmp->lock)) {
230 fibril_mutex_unlock(&nodep->lock);
231 goto skip_cache;
232 }
233 list_remove(&nodep->ffn_link);
234 fibril_mutex_unlock(&ffn_mutex);
235 if (nodep->dirty) {
236 rc = exfat_node_sync(nodep);
237 if (rc != EOK) {
238 idxp_tmp->nodep = NULL;
239 fibril_mutex_unlock(&nodep->lock);
240 fibril_mutex_unlock(&idxp_tmp->lock);
241 free(nodep->bp);
242 free(nodep);
243 return rc;
244 }
245 }
246 idxp_tmp->nodep = NULL;
247 fibril_mutex_unlock(&nodep->lock);
248 fibril_mutex_unlock(&idxp_tmp->lock);
249 fn = FS_NODE(nodep);
250 } else {
251 skip_cache:
252 /* Try to allocate a new node structure. */
253 fibril_mutex_unlock(&ffn_mutex);
254 fn = (fs_node_t *)malloc(sizeof(fs_node_t));
255 if (!fn)
256 return ENOMEM;
257 nodep = (exfat_node_t *)malloc(sizeof(exfat_node_t));
258 if (!nodep) {
259 free(fn);
260 return ENOMEM;
261 }
262 }
263 exfat_node_initialize(nodep);
264 fs_node_initialize(fn);
265 fn->data = nodep;
266 nodep->bp = fn;
267
268 *nodepp = nodep;
269 return EOK;
270}
271
272static errno_t exfat_node_get_new_by_pos(exfat_node_t **nodepp,
273 service_id_t service_id, exfat_cluster_t pfc, unsigned pdi)
274{
275 exfat_idx_t *idxp = exfat_idx_get_by_pos(service_id, pfc, pdi);
276 if (!idxp)
277 return ENOMEM;
278 if (exfat_node_get_new(nodepp) != EOK)
279 return ENOMEM;
280 (*nodepp)->idx = idxp;
281 idxp->nodep = *nodepp;
282 return EOK;
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 * EXFAT libfs operations.
490 */
491
492errno_t exfat_root_get(fs_node_t **rfn, service_id_t service_id)
493{
494 return exfat_node_get(rfn, service_id, EXFAT_ROOT_IDX);
495}
496
497errno_t exfat_bitmap_get(fs_node_t **rfn, service_id_t service_id)
498{
499 return exfat_node_get(rfn, service_id, EXFAT_BITMAP_IDX);
500}
501
502errno_t exfat_uctable_get(fs_node_t **rfn, service_id_t service_id)
503{
504 return exfat_node_get(rfn, service_id, EXFAT_UCTABLE_IDX);
505}
506
507errno_t exfat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
508{
509 exfat_node_t *parentp = EXFAT_NODE(pfn);
510 char name[EXFAT_FILENAME_LEN + 1];
511 exfat_file_dentry_t df;
512 exfat_stream_dentry_t ds;
513 service_id_t service_id;
514 errno_t rc;
515
516 fibril_mutex_lock(&parentp->idx->lock);
517 service_id = parentp->idx->service_id;
518 fibril_mutex_unlock(&parentp->idx->lock);
519
520 exfat_directory_t di;
521 rc = exfat_directory_open(parentp, &di);
522 if (rc != EOK)
523 return rc;
524
525 while (exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN, &df,
526 &ds) == EOK) {
527 if (str_casecmp(name, component) == 0) {
528 /* hit */
529 exfat_node_t *nodep;
530 aoff64_t o = di.pos %
531 (BPS(di.bs) / sizeof(exfat_dentry_t));
532 exfat_idx_t *idx = exfat_idx_get_by_pos(service_id,
533 parentp->firstc, di.bnum * DPS(di.bs) + o);
534 if (!idx) {
535 /*
536 * Can happen if memory is low or if we
537 * run out of 32-bit indices.
538 */
539 rc = exfat_directory_close(&di);
540 return (rc == EOK) ? ENOMEM : rc;
541 }
542 rc = exfat_node_get_core(&nodep, idx);
543 fibril_mutex_unlock(&idx->lock);
544 if (rc != EOK) {
545 (void) exfat_directory_close(&di);
546 return rc;
547 }
548 *rfn = FS_NODE(nodep);
549 rc = exfat_directory_close(&di);
550 if (rc != EOK)
551 (void) exfat_node_put(*rfn);
552 return rc;
553 } else {
554 rc = exfat_directory_next(&di);
555 if (rc != EOK)
556 break;
557 }
558 }
559 (void) exfat_directory_close(&di);
560 *rfn = NULL;
561 return EOK;
562}
563
564/** Instantiate a exFAT in-core node. */
565errno_t exfat_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
566{
567 exfat_node_t *nodep;
568 exfat_idx_t *idxp;
569 errno_t rc;
570
571 idxp = exfat_idx_get_by_index(service_id, index);
572 if (!idxp) {
573 *rfn = NULL;
574 return EOK;
575 }
576 /* idxp->lock held */
577 rc = exfat_node_get_core(&nodep, idxp);
578 fibril_mutex_unlock(&idxp->lock);
579 if (rc == EOK)
580 *rfn = FS_NODE(nodep);
581 return rc;
582}
583
584errno_t exfat_node_open(fs_node_t *fn)
585{
586 /*
587 * Opening a file is stateless, nothing
588 * to be done here.
589 */
590 return EOK;
591}
592
593errno_t exfat_node_put(fs_node_t *fn)
594{
595 if (fn == NULL)
596 return EOK;
597
598 exfat_node_t *nodep = EXFAT_NODE(fn);
599 bool destroy = false;
600
601 fibril_mutex_lock(&nodep->lock);
602 if (!--nodep->refcnt) {
603 if (nodep->idx) {
604 fibril_mutex_lock(&ffn_mutex);
605 list_append(&nodep->ffn_link, &ffn_list);
606 fibril_mutex_unlock(&ffn_mutex);
607 } else {
608 /*
609 * The node does not have any index structure associated
610 * with itself. This can only mean that we are releasing
611 * the node after a failed attempt to allocate the index
612 * structure for it.
613 */
614 destroy = true;
615 }
616 }
617 fibril_mutex_unlock(&nodep->lock);
618 if (destroy) {
619 free(nodep->bp);
620 free(nodep);
621 }
622 return EOK;
623}
624
625errno_t exfat_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
626{
627 exfat_idx_t *idxp;
628 exfat_node_t *nodep;
629 exfat_bs_t *bs;
630 errno_t rc;
631
632 bs = block_bb_get(service_id);
633 rc = exfat_node_get_new(&nodep);
634 if (rc != EOK)
635 return rc;
636
637 rc = exfat_idx_get_new(&idxp, service_id);
638 if (rc != EOK) {
639 (void) exfat_node_put(FS_NODE(nodep));
640 return rc;
641 }
642
643 nodep->firstc = 0;
644 nodep->size = 0;
645 nodep->fragmented = false;
646 nodep->lnkcnt = 0; /* not linked anywhere */
647 nodep->refcnt = 1;
648 nodep->dirty = true;
649
650 nodep->idx = idxp;
651 idxp->nodep = nodep;
652 fibril_mutex_unlock(&idxp->lock);
653
654 if (flags & L_DIRECTORY) {
655 nodep->type = EXFAT_DIRECTORY;
656 rc = exfat_node_expand(service_id, nodep, 1);
657 if (rc != EOK) {
658 (void) exfat_node_put(FS_NODE(nodep));
659 return rc;
660 }
661
662 rc = exfat_zero_cluster(bs, service_id, nodep->firstc);
663 if (rc != EOK) {
664 (void) exfat_node_put(FS_NODE(nodep));
665 return rc;
666 }
667
668 nodep->size = BPC(bs);
669 } else {
670 nodep->type = EXFAT_FILE;
671 }
672
673 *rfn = FS_NODE(nodep);
674 return EOK;
675}
676
677errno_t exfat_destroy_node(fs_node_t *fn)
678{
679 exfat_node_t *nodep = EXFAT_NODE(fn);
680 exfat_bs_t *bs;
681 bool has_children;
682 errno_t rc;
683
684 /*
685 * The node is not reachable from the file system. This means that the
686 * link count should be zero and that the index structure cannot be
687 * found in the position hash. Obviously, we don't need to lock the node
688 * nor its index structure.
689 */
690 assert(nodep->lnkcnt == 0);
691
692 /*
693 * The node may not have any children.
694 */
695 rc = exfat_has_children(&has_children, fn);
696 if (rc != EOK)
697 return rc;
698 assert(!has_children);
699
700 bs = block_bb_get(nodep->idx->service_id);
701 if (nodep->firstc != 0) {
702 assert(nodep->size);
703 /* Free all clusters allocated to the node. */
704 if (nodep->fragmented)
705 rc = exfat_free_clusters(bs, nodep->idx->service_id,
706 nodep->firstc);
707 else
708 rc = exfat_bitmap_free_clusters(bs, nodep,
709 ROUND_UP(nodep->size, BPC(bs)) / BPC(bs));
710 }
711
712 exfat_idx_destroy(nodep->idx);
713 free(nodep->bp);
714 free(nodep);
715 return rc;
716}
717
718errno_t exfat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
719{
720 exfat_node_t *parentp = EXFAT_NODE(pfn);
721 exfat_node_t *childp = EXFAT_NODE(cfn);
722 exfat_directory_t di;
723 errno_t rc;
724
725 fibril_mutex_lock(&childp->lock);
726 if (childp->lnkcnt == 1) {
727 /*
728 * We don't support multiple hard links.
729 */
730 fibril_mutex_unlock(&childp->lock);
731 return EMLINK;
732 }
733 assert(childp->lnkcnt == 0);
734 fibril_mutex_unlock(&childp->lock);
735
736 if (!exfat_valid_name(name))
737 return ENOTSUP;
738
739 fibril_mutex_lock(&parentp->idx->lock);
740 rc = exfat_directory_open(parentp, &di);
741 if (rc != EOK)
742 return rc;
743 /*
744 * At this point we only establish the link between the parent and the
745 * child. The dentry, except of the name and the extension, will remain
746 * uninitialized until the corresponding node is synced. Thus the valid
747 * dentry data is kept in the child node structure.
748 */
749 rc = exfat_directory_write_file(&di, name);
750 if (rc != EOK) {
751 (void) exfat_directory_close(&di);
752 fibril_mutex_unlock(&parentp->idx->lock);
753 return rc;
754 }
755 rc = exfat_directory_close(&di);
756 if (rc != EOK) {
757 fibril_mutex_unlock(&parentp->idx->lock);
758 return rc;
759 }
760
761 fibril_mutex_unlock(&parentp->idx->lock);
762 fibril_mutex_lock(&childp->idx->lock);
763
764 childp->idx->pfc = parentp->firstc;
765 childp->idx->parent_fragmented = parentp->fragmented;
766 childp->idx->pdi = di.pos;
767 fibril_mutex_unlock(&childp->idx->lock);
768
769 fibril_mutex_lock(&childp->lock);
770 childp->lnkcnt = 1;
771 childp->dirty = true; /* need to sync node */
772 fibril_mutex_unlock(&childp->lock);
773
774 /*
775 * Hash in the index structure into the position hash.
776 */
777 exfat_idx_hashin(childp->idx);
778
779 return EOK;
780
781}
782
783errno_t exfat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
784{
785 exfat_node_t *parentp = EXFAT_NODE(pfn);
786 exfat_node_t *childp = EXFAT_NODE(cfn);
787 bool has_children;
788 errno_t rc;
789
790 if (!parentp)
791 return EBUSY;
792
793 rc = exfat_has_children(&has_children, cfn);
794 if (rc != EOK)
795 return rc;
796 if (has_children)
797 return ENOTEMPTY;
798
799 fibril_mutex_lock(&parentp->lock);
800 fibril_mutex_lock(&childp->lock);
801 assert(childp->lnkcnt == 1);
802 fibril_mutex_lock(&childp->idx->lock);
803
804 exfat_directory_t di;
805 rc = exfat_directory_open(parentp, &di);
806 if (rc != EOK)
807 goto error;
808 rc = exfat_directory_erase_file(&di, childp->idx->pdi);
809 if (rc != EOK)
810 goto error;
811 rc = exfat_directory_close(&di);
812 if (rc != EOK)
813 goto error;
814
815 /* remove the index structure from the position hash */
816 exfat_idx_hashout(childp->idx);
817 /* clear position information */
818 childp->idx->pfc = 0;
819 childp->idx->pdi = 0;
820 fibril_mutex_unlock(&childp->idx->lock);
821 childp->lnkcnt = 0;
822 childp->refcnt++; /* keep the node in memory until destroyed */
823 childp->dirty = true;
824 fibril_mutex_unlock(&childp->lock);
825 fibril_mutex_unlock(&parentp->lock);
826
827 return EOK;
828
829error:
830 (void) exfat_directory_close(&di);
831 fibril_mutex_unlock(&childp->idx->lock);
832 fibril_mutex_unlock(&childp->lock);
833 fibril_mutex_unlock(&parentp->lock);
834 return rc;
835
836}
837
838errno_t exfat_has_children(bool *has_children, fs_node_t *fn)
839{
840 exfat_directory_t di;
841 exfat_dentry_t *d;
842 exfat_node_t *nodep = EXFAT_NODE(fn);
843 errno_t rc;
844
845 *has_children = false;
846
847 if (nodep->type != EXFAT_DIRECTORY)
848 return EOK;
849
850 fibril_mutex_lock(&nodep->idx->lock);
851
852 rc = exfat_directory_open(nodep, &di);
853 if (rc != EOK) {
854 fibril_mutex_unlock(&nodep->idx->lock);
855 return rc;
856 }
857
858 do {
859 rc = exfat_directory_get(&di, &d);
860 if (rc != EOK) {
861 (void) exfat_directory_close(&di);
862 fibril_mutex_unlock(&nodep->idx->lock);
863 return rc;
864 }
865 switch (exfat_classify_dentry(d)) {
866 case EXFAT_DENTRY_SKIP:
867 case EXFAT_DENTRY_FREE:
868 continue;
869 case EXFAT_DENTRY_LAST:
870 *has_children = false;
871 goto exit;
872 default:
873 *has_children = true;
874 goto exit;
875 }
876 } while (exfat_directory_next(&di) == EOK);
877
878exit:
879 rc = exfat_directory_close(&di);
880 fibril_mutex_unlock(&nodep->idx->lock);
881 return rc;
882}
883
884fs_index_t exfat_index_get(fs_node_t *fn)
885{
886 return EXFAT_NODE(fn)->idx->index;
887}
888
889aoff64_t exfat_size_get(fs_node_t *fn)
890{
891 return EXFAT_NODE(fn)->size;
892}
893
894unsigned exfat_lnkcnt_get(fs_node_t *fn)
895{
896 return EXFAT_NODE(fn)->lnkcnt;
897}
898
899bool exfat_is_directory(fs_node_t *fn)
900{
901 return EXFAT_NODE(fn)->type == EXFAT_DIRECTORY;
902}
903
904bool exfat_is_file(fs_node_t *fn)
905{
906 return EXFAT_NODE(fn)->type == EXFAT_FILE;
907}
908
909service_id_t exfat_service_get(fs_node_t *node)
910{
911 return 0;
912}
913
914errno_t exfat_size_block(service_id_t service_id, uint32_t *size)
915{
916 exfat_bs_t *bs;
917 bs = block_bb_get(service_id);
918 *size = BPC(bs);
919
920 return EOK;
921}
922
923errno_t exfat_total_block_count(service_id_t service_id, uint64_t *count)
924{
925 exfat_bs_t *bs;
926 bs = block_bb_get(service_id);
927 *count = DATA_CNT(bs);
928
929 return EOK;
930}
931
932errno_t exfat_free_block_count(service_id_t service_id, uint64_t *count)
933{
934 fs_node_t *node = NULL;
935 exfat_node_t *bmap_node;
936 exfat_bs_t *bs;
937 uint64_t free_block_count = 0;
938 uint64_t block_count;
939 unsigned sector;
940 errno_t rc;
941
942 rc = exfat_total_block_count(service_id, &block_count);
943 if (rc != EOK)
944 goto exit;
945
946 bs = block_bb_get(service_id);
947 rc = exfat_bitmap_get(&node, service_id);
948 if (rc != EOK)
949 goto exit;
950
951 bmap_node = (exfat_node_t *) node->data;
952
953 unsigned const bmap_sectors = ROUND_UP(bmap_node->size, BPS(bs)) /
954 BPS(bs);
955
956 for (sector = 0; sector < bmap_sectors; ++sector) {
957
958 block_t *block;
959 uint8_t *bitmap;
960 unsigned bit;
961
962 rc = exfat_block_get(&block, bs, bmap_node, sector,
963 BLOCK_FLAGS_NONE);
964 if (rc != EOK) {
965 free_block_count = 0;
966 goto exit;
967 }
968
969 bitmap = (uint8_t *) block->data;
970
971 for (bit = 0; bit < BPS(bs) * 8 && block_count > 0;
972 ++bit, --block_count) {
973 if (!(bitmap[bit / 8] & (1 << (bit % 8))))
974 ++free_block_count;
975 }
976
977 block_put(block);
978
979 if (block_count == 0) {
980 /* Reached the end of the bitmap */
981 goto exit;
982 }
983 }
984
985exit:
986 exfat_node_put(node);
987 *count = free_block_count;
988 return rc;
989}
990
991/** libfs operations */
992libfs_ops_t exfat_libfs_ops = {
993 .root_get = exfat_root_get,
994 .match = exfat_match,
995 .node_get = exfat_node_get,
996 .node_open = exfat_node_open,
997 .node_put = exfat_node_put,
998 .create = exfat_create_node,
999 .destroy = exfat_destroy_node,
1000 .link = exfat_link,
1001 .unlink = exfat_unlink,
1002 .has_children = exfat_has_children,
1003 .index_get = exfat_index_get,
1004 .size_get = exfat_size_get,
1005 .lnkcnt_get = exfat_lnkcnt_get,
1006 .is_directory = exfat_is_directory,
1007 .is_file = exfat_is_file,
1008 .service_get = exfat_service_get,
1009 .size_block = exfat_size_block,
1010 .total_block_count = exfat_total_block_count,
1011 .free_block_count = exfat_free_block_count
1012};
1013
1014static errno_t exfat_fs_open(service_id_t service_id, enum cache_mode cmode,
1015 fs_node_t **rrfn, exfat_idx_t **rridxp, vfs_fs_probe_info_t *info)
1016{
1017 errno_t rc;
1018 exfat_node_t *rootp = NULL, *bitmapp = NULL, *uctablep = NULL;
1019 exfat_bs_t *bs;
1020
1021 /* initialize libblock */
1022 rc = block_init(service_id, BS_SIZE);
1023 if (rc != EOK)
1024 return rc;
1025
1026 /* prepare the boot block */
1027 rc = block_bb_read(service_id, BS_BLOCK);
1028 if (rc != EOK) {
1029 block_fini(service_id);
1030 return rc;
1031 }
1032
1033 /* get the buffer with the boot sector */
1034 bs = block_bb_get(service_id);
1035
1036 /* Do some simple sanity checks on the file system. */
1037 rc = exfat_sanity_check(bs);
1038 if (rc != EOK) {
1039 (void) block_cache_fini(service_id);
1040 block_fini(service_id);
1041 return rc;
1042 }
1043
1044 /* Initialize the block cache */
1045 rc = block_cache_init(service_id, BPS(bs), 0 /* XXX */, cmode);
1046 if (rc != EOK) {
1047 block_fini(service_id);
1048 return rc;
1049 }
1050
1051 rc = exfat_idx_init_by_service_id(service_id);
1052 if (rc != EOK) {
1053 (void) block_cache_fini(service_id);
1054 block_fini(service_id);
1055 return rc;
1056 }
1057
1058 /* Initialize the root node. */
1059 rc = exfat_node_get_new_by_pos(&rootp, service_id, EXFAT_ROOT_PAR,
1060 EXFAT_ROOT_POS);
1061 if (rc != EOK) {
1062 (void) block_cache_fini(service_id);
1063 block_fini(service_id);
1064 exfat_idx_fini_by_service_id(service_id);
1065 return ENOMEM;
1066 }
1067 assert(rootp->idx->index == EXFAT_ROOT_IDX);
1068
1069 rootp->type = EXFAT_DIRECTORY;
1070 rootp->firstc = ROOT_FC(bs);
1071 rootp->fragmented = true;
1072 rootp->refcnt = 1;
1073 rootp->lnkcnt = 0; /* FS root is not linked */
1074
1075 uint32_t clusters;
1076 rc = exfat_clusters_get(&clusters, bs, service_id, rootp->firstc);
1077 if (rc != EOK) {
1078 free(rootp);
1079 (void) block_cache_fini(service_id);
1080 block_fini(service_id);
1081 exfat_idx_fini_by_service_id(service_id);
1082 return ENOTSUP;
1083 }
1084 rootp->size = BPS(bs) * SPC(bs) * clusters;
1085 fibril_mutex_unlock(&rootp->idx->lock);
1086
1087 /* Open root directory and looking for Bitmap and UC-Table */
1088 exfat_directory_t di;
1089 exfat_dentry_t *de;
1090 rc = exfat_directory_open(rootp, &di);
1091 if (rc != EOK) {
1092 free(rootp);
1093 (void) block_cache_fini(service_id);
1094 block_fini(service_id);
1095 exfat_idx_fini_by_service_id(service_id);
1096 return ENOTSUP;
1097 }
1098
1099 /* Initialize the bitmap node. */
1100 rc = exfat_directory_find(&di, EXFAT_DENTRY_BITMAP, &de);
1101 if (rc != EOK) {
1102 free(rootp);
1103 (void) block_cache_fini(service_id);
1104 block_fini(service_id);
1105 exfat_idx_fini_by_service_id(service_id);
1106 return ENOTSUP;
1107 }
1108
1109 rc = exfat_node_get_new_by_pos(&bitmapp, service_id, rootp->firstc,
1110 di.pos);
1111 if (rc != EOK) {
1112 free(rootp);
1113 (void) block_cache_fini(service_id);
1114 block_fini(service_id);
1115 exfat_idx_fini_by_service_id(service_id);
1116 return ENOMEM;
1117 }
1118 assert(bitmapp->idx->index == EXFAT_BITMAP_IDX);
1119 fibril_mutex_unlock(&bitmapp->idx->lock);
1120
1121 bitmapp->type = EXFAT_BITMAP;
1122 bitmapp->firstc = uint32_t_le2host(de->bitmap.firstc);
1123 bitmapp->fragmented = true;
1124 bitmapp->idx->parent_fragmented = true;
1125 bitmapp->refcnt = 1;
1126 bitmapp->lnkcnt = 0;
1127 bitmapp->size = uint64_t_le2host(de->bitmap.size);
1128
1129 /* Initialize the uctable node. */
1130 rc = exfat_directory_seek(&di, 0);
1131 if (rc != EOK) {
1132 free(rootp);
1133 free(bitmapp);
1134 (void) block_cache_fini(service_id);
1135 block_fini(service_id);
1136 exfat_idx_fini_by_service_id(service_id);
1137 return ENOTSUP;
1138 }
1139
1140 rc = exfat_directory_find(&di, EXFAT_DENTRY_UCTABLE, &de);
1141 if (rc != EOK) {
1142 free(rootp);
1143 free(bitmapp);
1144 (void) block_cache_fini(service_id);
1145 block_fini(service_id);
1146 exfat_idx_fini_by_service_id(service_id);
1147 return ENOTSUP;
1148 }
1149
1150 rc = exfat_node_get_new_by_pos(&uctablep, service_id, rootp->firstc,
1151 di.pos);
1152 if (rc != EOK) {
1153 free(rootp);
1154 free(bitmapp);
1155 (void) block_cache_fini(service_id);
1156 block_fini(service_id);
1157 exfat_idx_fini_by_service_id(service_id);
1158 return ENOMEM;
1159 }
1160 assert(uctablep->idx->index == EXFAT_UCTABLE_IDX);
1161 fibril_mutex_unlock(&uctablep->idx->lock);
1162
1163 uctablep->type = EXFAT_UCTABLE;
1164 uctablep->firstc = uint32_t_le2host(de->uctable.firstc);
1165 uctablep->fragmented = true;
1166 uctablep->idx->parent_fragmented = true;
1167 uctablep->refcnt = 1;
1168 uctablep->lnkcnt = 0;
1169 uctablep->size = uint64_t_le2host(de->uctable.size);
1170
1171 if (info != NULL) {
1172 /* Read volume label. */
1173 rc = exfat_directory_read_vollabel(&di, info->label,
1174 FS_LABEL_MAXLEN + 1);
1175 if (rc != EOK) {
1176 free(rootp);
1177 free(bitmapp);
1178 free(uctablep);
1179 (void) block_cache_fini(service_id);
1180 block_fini(service_id);
1181 exfat_idx_fini_by_service_id(service_id);
1182 return ENOTSUP;
1183 }
1184 }
1185
1186 rc = exfat_directory_close(&di);
1187 if (rc != EOK) {
1188 free(rootp);
1189 free(bitmapp);
1190 free(uctablep);
1191 (void) block_cache_fini(service_id);
1192 block_fini(service_id);
1193 exfat_idx_fini_by_service_id(service_id);
1194 return ENOMEM;
1195 }
1196
1197 if (0)
1198 exfat_fsinfo(bs, service_id);
1199
1200 *rrfn = FS_NODE(rootp);
1201 *rridxp = rootp->idx;
1202
1203 if (info != NULL) {
1204#if 0
1205 str_cpy(info->label, FS_LABEL_MAXLEN + 1, label);
1206#endif
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 * VFS_OUT operations.
1233 */
1234
1235/* Print debug info */
1236static void exfat_fsinfo(exfat_bs_t *bs, service_id_t service_id)
1237{
1238 printf("exFAT file system mounted\n");
1239 printf("Version: %d.%d\n", bs->version.major, bs->version.minor);
1240 printf("Volume serial: %d\n", uint32_t_le2host(bs->volume_serial));
1241 printf("Volume first sector: %" PRId64 "\n", VOL_FS(bs));
1242 printf("Volume sectors: %" PRId64 "\n", VOL_CNT(bs));
1243 printf("FAT first sector: %d\n", FAT_FS(bs));
1244 printf("FAT sectors: %d\n", FAT_CNT(bs));
1245 printf("Data first sector: %d\n", DATA_FS(bs));
1246 printf("Data sectors: %d\n", DATA_CNT(bs));
1247 printf("Root dir first cluster: %d\n", ROOT_FC(bs));
1248 printf("Bytes per sector: %d\n", BPS(bs));
1249 printf("Sectors per cluster: %d\n", SPC(bs));
1250 printf("KBytes per cluster: %d\n", SPC(bs) * BPS(bs) / 1024);
1251
1252 int i, rc;
1253 exfat_cluster_t clst;
1254 for (i = 0; i <= 7; i++) {
1255 rc = exfat_get_cluster(bs, service_id, i, &clst);
1256 if (rc != EOK)
1257 return;
1258 printf("Clst %d: %x", i, clst);
1259 if (i >= 2) {
1260 printf(", Bitmap: %d\n", exfat_bitmap_is_free(bs,
1261 service_id, i) != EOK);
1262 } else {
1263 printf("\n");
1264 }
1265 }
1266}
1267
1268static errno_t exfat_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
1269{
1270 errno_t rc;
1271 exfat_idx_t *ridxp;
1272 fs_node_t *rfn;
1273
1274 rc = exfat_fs_open(service_id, CACHE_MODE_WT, &rfn, &ridxp, info);
1275 if (rc != EOK)
1276 return rc;
1277
1278 exfat_fs_close(service_id, rfn);
1279 return EOK;
1280}
1281
1282static errno_t
1283exfat_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
1284 aoff64_t *size)
1285{
1286 errno_t rc;
1287 enum cache_mode cmode;
1288 exfat_idx_t *ridxp;
1289 fs_node_t *rfn;
1290
1291 /* Check for option enabling write through. */
1292 if (str_cmp(opts, "wtcache") == 0)
1293 cmode = CACHE_MODE_WT;
1294 else
1295 cmode = CACHE_MODE_WB;
1296
1297 rc = exfat_fs_open(service_id, cmode, &rfn, &ridxp, NULL);
1298 if (rc != EOK)
1299 return rc;
1300
1301 *index = ridxp->index;
1302 *size = EXFAT_NODE(rfn)->size;
1303
1304 return EOK;
1305}
1306
1307static errno_t exfat_unmounted(service_id_t service_id)
1308{
1309 fs_node_t *rfn;
1310 errno_t rc;
1311
1312 rc = exfat_root_get(&rfn, service_id);
1313 if (rc != EOK)
1314 return rc;
1315
1316 exfat_fs_close(service_id, rfn);
1317 return EOK;
1318}
1319
1320static errno_t
1321exfat_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
1322 size_t *rbytes)
1323{
1324 fs_node_t *fn;
1325 exfat_node_t *nodep;
1326 exfat_bs_t *bs;
1327 size_t bytes = 0;
1328 block_t *b;
1329 errno_t rc;
1330
1331 rc = exfat_node_get(&fn, service_id, index);
1332 if (rc != EOK)
1333 return rc;
1334 if (!fn)
1335 return ENOENT;
1336 nodep = EXFAT_NODE(fn);
1337
1338 ipc_call_t call;
1339 size_t len;
1340 if (!async_data_read_receive(&call, &len)) {
1341 exfat_node_put(fn);
1342 async_answer_0(&call, EINVAL);
1343 return EINVAL;
1344 }
1345
1346 bs = block_bb_get(service_id);
1347
1348 if (nodep->type == EXFAT_FILE) {
1349 /*
1350 * Our strategy for regular file reads is to read one block at
1351 * most and make use of the possibility to return less data than
1352 * requested. This keeps the code very simple.
1353 */
1354 if (pos >= nodep->size) {
1355 /* reading beyond the EOF */
1356 bytes = 0;
1357 (void) async_data_read_finalize(&call, NULL, 0);
1358 } else {
1359 bytes = min(len, BPS(bs) - pos % BPS(bs));
1360 bytes = min(bytes, nodep->size - pos);
1361 rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs),
1362 BLOCK_FLAGS_NONE);
1363 if (rc != EOK) {
1364 exfat_node_put(fn);
1365 async_answer_0(&call, rc);
1366 return rc;
1367 }
1368 (void) async_data_read_finalize(&call,
1369 b->data + pos % BPS(bs), bytes);
1370 rc = block_put(b);
1371 if (rc != EOK) {
1372 exfat_node_put(fn);
1373 return rc;
1374 }
1375 }
1376 } else {
1377 if (nodep->type != EXFAT_DIRECTORY) {
1378 async_answer_0(&call, ENOTSUP);
1379 return ENOTSUP;
1380 }
1381
1382 aoff64_t spos = pos;
1383 char name[EXFAT_FILENAME_LEN + 1];
1384 exfat_file_dentry_t df;
1385 exfat_stream_dentry_t ds;
1386
1387 assert(nodep->size % BPS(bs) == 0);
1388 assert(BPS(bs) % sizeof(exfat_dentry_t) == 0);
1389
1390 exfat_directory_t di;
1391 rc = exfat_directory_open(nodep, &di);
1392 if (rc != EOK)
1393 goto err;
1394
1395 rc = exfat_directory_seek(&di, pos);
1396 if (rc != EOK) {
1397 (void) exfat_directory_close(&di);
1398 goto err;
1399 }
1400
1401 rc = exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN,
1402 &df, &ds);
1403 if (rc == EOK)
1404 goto hit;
1405 else if (rc == ENOENT)
1406 goto miss;
1407
1408 (void) exfat_directory_close(&di);
1409
1410 err:
1411 (void) exfat_node_put(fn);
1412 async_answer_0(&call, rc);
1413 return rc;
1414
1415 miss:
1416 rc = exfat_directory_close(&di);
1417 if (rc != EOK)
1418 goto err;
1419 rc = exfat_node_put(fn);
1420 async_answer_0(&call, rc != EOK ? rc : ENOENT);
1421 *rbytes = 0;
1422 return rc != EOK ? rc : ENOENT;
1423
1424 hit:
1425 pos = di.pos;
1426 rc = exfat_directory_close(&di);
1427 if (rc != EOK)
1428 goto err;
1429 (void) async_data_read_finalize(&call, name,
1430 str_bytes(name) + 1);
1431 bytes = (pos - spos) + 1;
1432 }
1433
1434 rc = exfat_node_put(fn);
1435 *rbytes = bytes;
1436 return rc;
1437}
1438
1439static errno_t exfat_close(service_id_t service_id, fs_index_t index)
1440{
1441 return EOK;
1442}
1443
1444static errno_t exfat_sync(service_id_t service_id, fs_index_t index)
1445{
1446 fs_node_t *fn;
1447 errno_t rc = exfat_node_get(&fn, service_id, index);
1448 if (rc != EOK)
1449 return rc;
1450 if (!fn)
1451 return ENOENT;
1452
1453 exfat_node_t *nodep = EXFAT_NODE(fn);
1454
1455 nodep->dirty = true;
1456 rc = exfat_node_sync(nodep);
1457
1458 exfat_node_put(fn);
1459 return rc;
1460}
1461
1462static errno_t
1463exfat_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
1464 size_t *wbytes, aoff64_t *nsize)
1465{
1466 fs_node_t *fn;
1467 exfat_node_t *nodep;
1468 exfat_bs_t *bs;
1469 size_t bytes;
1470 block_t *b;
1471 aoff64_t boundary;
1472 int flags = BLOCK_FLAGS_NONE;
1473 errno_t rc;
1474
1475 rc = exfat_node_get(&fn, service_id, index);
1476 if (rc != EOK)
1477 return rc;
1478 if (!fn)
1479 return ENOENT;
1480 nodep = EXFAT_NODE(fn);
1481
1482 ipc_call_t call;
1483 size_t len;
1484 if (!async_data_write_receive(&call, &len)) {
1485 (void) exfat_node_put(fn);
1486 async_answer_0(&call, EINVAL);
1487 return EINVAL;
1488 }
1489
1490 bs = block_bb_get(service_id);
1491
1492 /*
1493 * In all scenarios, we will attempt to write out only one block worth
1494 * of data at maximum. There might be some more efficient approaches,
1495 * but this one greatly simplifies fat_write(). Note that we can afford
1496 * to do this because the client must be ready to handle the return
1497 * value signalizing a smaller number of bytes written.
1498 */
1499 bytes = min(len, BPS(bs) - pos % BPS(bs));
1500 if (bytes == BPS(bs))
1501 flags |= BLOCK_FLAGS_NOREAD;
1502
1503 boundary = ROUND_UP(nodep->size, BPC(bs));
1504 if (pos >= boundary) {
1505 unsigned nclsts;
1506 nclsts = (ROUND_UP(pos + bytes, BPC(bs)) - boundary) / BPC(bs);
1507 rc = exfat_node_expand(service_id, nodep, nclsts);
1508 if (rc != EOK) {
1509 /* could not expand node */
1510 (void) exfat_node_put(fn);
1511 async_answer_0(&call, rc);
1512 return rc;
1513 }
1514 }
1515
1516 if (pos + bytes > nodep->size) {
1517 nodep->size = pos + bytes;
1518 nodep->dirty = true; /* need to sync node */
1519 }
1520
1521 /*
1522 * This is the easier case - we are either overwriting already
1523 * existing contents or writing behind the EOF, but still within
1524 * the limits of the last cluster. The node size may grow to the
1525 * next block size boundary.
1526 */
1527 rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs), flags);
1528 if (rc != EOK) {
1529 (void) exfat_node_put(fn);
1530 async_answer_0(&call, rc);
1531 return rc;
1532 }
1533
1534 (void) async_data_write_finalize(&call,
1535 b->data + pos % BPS(bs), bytes);
1536 b->dirty = true; /* need to sync block */
1537 rc = block_put(b);
1538 if (rc != EOK) {
1539 (void) exfat_node_put(fn);
1540 return rc;
1541 }
1542
1543 *wbytes = bytes;
1544 *nsize = nodep->size;
1545 rc = exfat_node_put(fn);
1546 return rc;
1547}
1548
1549static errno_t
1550exfat_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1551{
1552 fs_node_t *fn;
1553 exfat_node_t *nodep;
1554 exfat_bs_t *bs;
1555 errno_t rc;
1556
1557 rc = exfat_node_get(&fn, service_id, index);
1558 if (rc != EOK)
1559 return rc;
1560 if (!fn)
1561 return ENOENT;
1562 nodep = EXFAT_NODE(fn);
1563
1564 bs = block_bb_get(service_id);
1565
1566 if (nodep->size == size) {
1567 rc = EOK;
1568 } else if (nodep->size < size) {
1569 /*
1570 * The standard says we have the freedom to grow the node.
1571 * For now, we simply return an error.
1572 */
1573 rc = EINVAL;
1574 } else if (ROUND_UP(nodep->size, BPC(bs)) == ROUND_UP(size, BPC(bs))) {
1575 /*
1576 * The node will be shrunk, but no clusters will be deallocated.
1577 */
1578 nodep->size = size;
1579 nodep->dirty = true; /* need to sync node */
1580 rc = EOK;
1581 } else {
1582 rc = exfat_node_shrink(service_id, nodep, size);
1583 }
1584
1585 errno_t rc2 = exfat_node_put(fn);
1586 if (rc == EOK && rc2 != EOK)
1587 rc = rc2;
1588
1589 return rc;
1590}
1591
1592static errno_t exfat_destroy(service_id_t service_id, fs_index_t index)
1593{
1594 fs_node_t *fn;
1595 exfat_node_t *nodep;
1596 errno_t rc;
1597
1598 rc = exfat_node_get(&fn, service_id, index);
1599 if (rc != EOK)
1600 return rc;
1601 if (!fn)
1602 return ENOENT;
1603
1604 nodep = EXFAT_NODE(fn);
1605 /*
1606 * We should have exactly two references. One for the above
1607 * call to fat_node_get() and one from fat_unlink().
1608 */
1609 assert(nodep->refcnt == 2);
1610
1611 rc = exfat_destroy_node(fn);
1612 return rc;
1613}
1614
1615vfs_out_ops_t exfat_ops = {
1616 .fsprobe = exfat_fsprobe,
1617 .mounted = exfat_mounted,
1618 .unmounted = exfat_unmounted,
1619 .read = exfat_read,
1620 .write = exfat_write,
1621 .truncate = exfat_truncate,
1622 .close = exfat_close,
1623 .destroy = exfat_destroy,
1624 .sync = exfat_sync,
1625};
1626
1627/**
1628 * @}
1629 */
Note: See TracBrowser for help on using the repository browser.