2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2011 Oleg Romanenko
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
36 * @brief Implementation of VFS operations for the exFAT file system
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"
48 #include <ipc/services.h>
54 #include <byteorder.h>
55 #include <adt/hash_table.h>
59 #include <fibril_synch.h>
64 /** Mutex protecting the list of cached free FAT nodes. */
65 static FIBRIL_MUTEX_INITIALIZE(ffn_mutex
);
67 /** List of cached free FAT nodes. */
68 static LIST_INITIALIZE(ffn_list
);
71 * Forward declarations of FAT libfs operations.
74 static int exfat_root_get(fs_node_t
**, service_id_t
);
75 static int exfat_match(fs_node_t
**, fs_node_t
*, const char *);
76 static int exfat_node_get(fs_node_t
**, service_id_t
, fs_index_t
);
77 static int exfat_node_open(fs_node_t
*);
78 /* static int exfat_node_put(fs_node_t *); */
79 static int exfat_create_node(fs_node_t
**, service_id_t
, int);
80 static int exfat_destroy_node(fs_node_t
*);
81 static int exfat_link(fs_node_t
*, fs_node_t
*, const char *);
82 static int exfat_unlink(fs_node_t
*, fs_node_t
*, const char *);
83 static int exfat_has_children(bool *, fs_node_t
*);
84 static fs_index_t
exfat_index_get(fs_node_t
*);
85 static aoff64_t
exfat_size_get(fs_node_t
*);
86 static unsigned exfat_lnkcnt_get(fs_node_t
*);
87 static bool exfat_is_directory(fs_node_t
*);
88 static bool exfat_is_file(fs_node_t
*node
);
89 static service_id_t
exfat_service_get(fs_node_t
*node
);
90 static int exfat_size_block(service_id_t
, uint32_t *);
91 static int exfat_total_block_count(service_id_t
, uint64_t *);
92 static int exfat_free_block_count(service_id_t
, uint64_t *);
97 static void exfat_node_initialize(exfat_node_t
*node
)
99 fibril_mutex_initialize(&node
->lock
);
102 node
->type
= EXFAT_UNKNOW
;
103 link_initialize(&node
->ffn_link
);
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;
116 static int exfat_node_sync(exfat_node_t
*node
)
119 exfat_directory_t di
;
120 exfat_file_dentry_t df
;
121 exfat_stream_dentry_t ds
;
123 if (!(node
->type
== EXFAT_DIRECTORY
|| node
->type
== EXFAT_FILE
))
126 if (node
->type
== EXFAT_DIRECTORY
)
127 df
.attr
= EXFAT_ATTR_SUBDIR
;
131 ds
.firstc
= node
->firstc
;
132 if (node
->size
== 0 && node
->firstc
== 0) {
136 ds
.flags
|= (!node
->fragmented
<< 1);
138 ds
.valid_data_size
= node
->size
;
139 ds
.data_size
= node
->size
;
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
);
145 (void) exfat_directory_close(&di
);
149 rc
= exfat_directory_sync_file(&di
, &df
, &ds
);
151 (void) exfat_directory_close(&di
);
154 return exfat_directory_close(&di
);
157 static int exfat_node_fini_by_service_id(service_id_t service_id
)
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.
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
);
174 if (!fibril_mutex_trylock(&nodep
->idx
->lock
)) {
175 fibril_mutex_unlock(&nodep
->lock
);
176 fibril_mutex_unlock(&ffn_mutex
);
179 if (nodep
->idx
->service_id
!= service_id
) {
180 fibril_mutex_unlock(&nodep
->idx
->lock
);
181 fibril_mutex_unlock(&nodep
->lock
);
185 list_remove(&nodep
->ffn_link
);
186 fibril_mutex_unlock(&ffn_mutex
);
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.
193 fibril_mutex_unlock(&nodep
->idx
->lock
);
194 fibril_mutex_unlock(&nodep
->lock
);
197 rc
= exfat_node_sync(nodep
);
201 nodep
->idx
->nodep
= NULL
;
205 /* Need to restart because we changed the ffn_list. */
208 fibril_mutex_unlock(&ffn_mutex
);
213 static int exfat_node_get_new(exfat_node_t
**nodepp
)
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
,
225 if (!fibril_mutex_trylock(&nodep
->lock
))
227 idxp_tmp
= nodep
->idx
;
228 if (!fibril_mutex_trylock(&idxp_tmp
->lock
)) {
229 fibril_mutex_unlock(&nodep
->lock
);
232 list_remove(&nodep
->ffn_link
);
233 fibril_mutex_unlock(&ffn_mutex
);
235 rc
= exfat_node_sync(nodep
);
237 idxp_tmp
->nodep
= NULL
;
238 fibril_mutex_unlock(&nodep
->lock
);
239 fibril_mutex_unlock(&idxp_tmp
->lock
);
245 idxp_tmp
->nodep
= NULL
;
246 fibril_mutex_unlock(&nodep
->lock
);
247 fibril_mutex_unlock(&idxp_tmp
->lock
);
251 /* Try to allocate a new node structure. */
252 fibril_mutex_unlock(&ffn_mutex
);
253 fn
= (fs_node_t
*)malloc(sizeof(fs_node_t
));
256 nodep
= (exfat_node_t
*)malloc(sizeof(exfat_node_t
));
262 exfat_node_initialize(nodep
);
263 fs_node_initialize(fn
);
271 static int exfat_node_get_new_by_pos(exfat_node_t
**nodepp
,
272 service_id_t service_id
, exfat_cluster_t pfc
, unsigned pdi
)
274 exfat_idx_t
*idxp
= exfat_idx_get_by_pos(service_id
, pfc
, pdi
);
277 if (exfat_node_get_new(nodepp
) != EOK
)
279 (*nodepp
)->idx
= idxp
;
280 idxp
->nodep
= *nodepp
;
285 /** Internal version of exfat_node_get().
287 * @param idxp Locked index structure.
289 static int exfat_node_get_core(exfat_node_t
**nodepp
, exfat_idx_t
*idxp
)
292 exfat_node_t
*nodep
= NULL
;
293 exfat_directory_t di
;
299 * The node is already instantiated in memory.
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
);
307 fibril_mutex_unlock(&idxp
->nodep
->lock
);
308 *nodepp
= idxp
->nodep
;
313 * We must instantiate the node from the file system.
318 rc
= exfat_node_get_new(&nodep
);
322 exfat_directory_open_parent(&di
, idxp
->service_id
, idxp
->pfc
,
323 idxp
->parent_fragmented
);
324 rc
= exfat_directory_seek(&di
, idxp
->pdi
);
326 (void) exfat_directory_close(&di
);
327 (void) exfat_node_put(FS_NODE(nodep
));
330 rc
= exfat_directory_get(&di
, &d
);
332 (void) exfat_directory_close(&di
);
333 (void) exfat_node_put(FS_NODE(nodep
));
337 switch (exfat_classify_dentry(d
)) {
338 case EXFAT_DENTRY_FILE
:
340 (uint16_t_le2host(d
->file
.attr
) & EXFAT_ATTR_SUBDIR
) ?
341 EXFAT_DIRECTORY
: EXFAT_FILE
;
342 rc
= exfat_directory_next(&di
);
344 (void) exfat_directory_close(&di
);
345 (void) exfat_node_put(FS_NODE(nodep
));
348 rc
= exfat_directory_get(&di
, &d
);
350 (void) exfat_directory_close(&di
);
351 (void) exfat_node_put(FS_NODE(nodep
));
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;
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;
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;
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
));
386 rc
= exfat_directory_close(&di
);
388 (void) exfat_node_put(FS_NODE(nodep
));
392 /* Link the idx structure with the node structure. */
400 int exfat_node_expand(service_id_t service_id
, exfat_node_t
*nodep
,
401 exfat_cluster_t clusters
)
405 bs
= block_bb_get(service_id
);
407 if (!nodep
->fragmented
) {
408 rc
= exfat_bitmap_append_clusters(bs
, nodep
, clusters
);
412 nodep
->fragmented
= true;
413 nodep
->dirty
= true; /* need to sync node */
414 rc
= exfat_bitmap_replicate_clusters(bs
, nodep
);
420 /* If we cant linear expand the node, we should use FAT instead */
421 exfat_cluster_t mcl
, lcl
;
423 /* create an independent chain of nclsts clusters in all FATs */
424 rc
= exfat_alloc_clusters(bs
, service_id
, clusters
, &mcl
, &lcl
);
427 rc
= exfat_zero_cluster(bs
, service_id
, mcl
);
429 (void) exfat_free_clusters(bs
, service_id
, mcl
);
433 * Append the cluster chain starting in mcl to the end of the
434 * node's cluster chain.
436 rc
= exfat_append_clusters(bs
, nodep
, mcl
, lcl
);
438 (void) exfat_free_clusters(bs
, service_id
, mcl
);
445 static int exfat_node_shrink(service_id_t service_id
, exfat_node_t
*nodep
,
450 bs
= block_bb_get(service_id
);
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
);
457 assert(new_clsts
< prev_clsts
);
459 clsts
= prev_clsts
- new_clsts
;
460 rc
= exfat_bitmap_free_clusters(bs
, nodep
, clsts
);
465 * The node will be shrunk, clusters will be deallocated.
468 rc
= exfat_chop_clusters(bs
, nodep
, 0);
472 exfat_cluster_t lastc
;
473 rc
= exfat_cluster_walk(bs
, service_id
, nodep
->firstc
,
474 &lastc
, NULL
, (size
- 1) / BPC(bs
));
477 rc
= exfat_chop_clusters(bs
, nodep
, lastc
);
484 nodep
->dirty
= true; /* need to sync node */
490 * EXFAT libfs operations.
493 int exfat_root_get(fs_node_t
**rfn
, service_id_t service_id
)
495 return exfat_node_get(rfn
, service_id
, EXFAT_ROOT_IDX
);
498 int exfat_bitmap_get(fs_node_t
**rfn
, service_id_t service_id
)
500 return exfat_node_get(rfn
, service_id
, EXFAT_BITMAP_IDX
);
503 int exfat_uctable_get(fs_node_t
**rfn
, service_id_t service_id
)
505 return exfat_node_get(rfn
, service_id
, EXFAT_UCTABLE_IDX
);
509 int exfat_match(fs_node_t
**rfn
, fs_node_t
*pfn
, const char *component
)
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
;
518 fibril_mutex_lock(&parentp
->idx
->lock
);
519 service_id
= parentp
->idx
->service_id
;
520 fibril_mutex_unlock(&parentp
->idx
->lock
);
522 exfat_directory_t di
;
523 rc
= exfat_directory_open(parentp
, &di
);
527 while (exfat_directory_read_file(&di
, name
, EXFAT_FILENAME_LEN
, &df
,
529 if (str_casecmp(name
, component
) == 0) {
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
);
538 * Can happen if memory is low or if we
539 * run out of 32-bit indices.
541 rc
= exfat_directory_close(&di
);
542 return (rc
== EOK
) ? ENOMEM
: rc
;
544 rc
= exfat_node_get_core(&nodep
, idx
);
545 fibril_mutex_unlock(&idx
->lock
);
547 (void) exfat_directory_close(&di
);
550 *rfn
= FS_NODE(nodep
);
551 rc
= exfat_directory_close(&di
);
553 (void) exfat_node_put(*rfn
);
556 rc
= exfat_directory_next(&di
);
561 (void) exfat_directory_close(&di
);
566 /** Instantiate a exFAT in-core node. */
567 int exfat_node_get(fs_node_t
**rfn
, service_id_t service_id
, fs_index_t index
)
573 idxp
= exfat_idx_get_by_index(service_id
, index
);
578 /* idxp->lock held */
579 rc
= exfat_node_get_core(&nodep
, idxp
);
580 fibril_mutex_unlock(&idxp
->lock
);
582 *rfn
= FS_NODE(nodep
);
586 int exfat_node_open(fs_node_t
*fn
)
589 * Opening a file is stateless, nothing
595 int exfat_node_put(fs_node_t
*fn
)
597 exfat_node_t
*nodep
= EXFAT_NODE(fn
);
598 bool destroy
= false;
600 fibril_mutex_lock(&nodep
->lock
);
601 if (!--nodep
->refcnt
) {
603 fibril_mutex_lock(&ffn_mutex
);
604 list_append(&nodep
->ffn_link
, &ffn_list
);
605 fibril_mutex_unlock(&ffn_mutex
);
608 * The node does not have any index structure associated
609 * with itself. This can only mean that we are releasing
610 * the node after a failed attempt to allocate the index
616 fibril_mutex_unlock(&nodep
->lock
);
624 int exfat_create_node(fs_node_t
**rfn
, service_id_t service_id
, int flags
)
631 bs
= block_bb_get(service_id
);
632 rc
= exfat_node_get_new(&nodep
);
636 rc
= exfat_idx_get_new(&idxp
, service_id
);
638 (void) exfat_node_put(FS_NODE(nodep
));
644 nodep
->fragmented
= false;
645 nodep
->lnkcnt
= 0; /* not linked anywhere */
651 fibril_mutex_unlock(&idxp
->lock
);
653 if (flags
& L_DIRECTORY
) {
654 nodep
->type
= EXFAT_DIRECTORY
;
655 rc
= exfat_node_expand(service_id
, nodep
, 1);
657 (void) exfat_node_put(FS_NODE(nodep
));
661 rc
= exfat_zero_cluster(bs
, service_id
, nodep
->firstc
);
663 (void) exfat_node_put(FS_NODE(nodep
));
667 nodep
->size
= BPC(bs
);
669 nodep
->type
= EXFAT_FILE
;
672 *rfn
= FS_NODE(nodep
);
676 int exfat_destroy_node(fs_node_t
*fn
)
678 exfat_node_t
*nodep
= EXFAT_NODE(fn
);
684 * The node is not reachable from the file system. This means that the
685 * link count should be zero and that the index structure cannot be
686 * found in the position hash. Obviously, we don't need to lock the node
687 * nor its index structure.
689 assert(nodep
->lnkcnt
== 0);
692 * The node may not have any children.
694 rc
= exfat_has_children(&has_children
, fn
);
697 assert(!has_children
);
699 bs
= block_bb_get(nodep
->idx
->service_id
);
700 if (nodep
->firstc
!= 0) {
702 /* Free all clusters allocated to the node. */
703 if (nodep
->fragmented
)
704 rc
= exfat_free_clusters(bs
, nodep
->idx
->service_id
,
707 rc
= exfat_bitmap_free_clusters(bs
, nodep
,
708 ROUND_UP(nodep
->size
, BPC(bs
)) / BPC(bs
));
711 exfat_idx_destroy(nodep
->idx
);
717 int exfat_link(fs_node_t
*pfn
, fs_node_t
*cfn
, const char *name
)
719 exfat_node_t
*parentp
= EXFAT_NODE(pfn
);
720 exfat_node_t
*childp
= EXFAT_NODE(cfn
);
721 exfat_directory_t di
;
724 fibril_mutex_lock(&childp
->lock
);
725 if (childp
->lnkcnt
== 1) {
727 * We don't support multiple hard links.
729 fibril_mutex_unlock(&childp
->lock
);
732 assert(childp
->lnkcnt
== 0);
733 fibril_mutex_unlock(&childp
->lock
);
735 if (!exfat_valid_name(name
))
738 fibril_mutex_lock(&parentp
->idx
->lock
);
739 rc
= exfat_directory_open(parentp
, &di
);
743 * At this point we only establish the link between the parent and the
744 * child. The dentry, except of the name and the extension, will remain
745 * uninitialized until the corresponding node is synced. Thus the valid
746 * dentry data is kept in the child node structure.
748 rc
= exfat_directory_write_file(&di
, name
);
750 (void) exfat_directory_close(&di
);
751 fibril_mutex_unlock(&parentp
->idx
->lock
);
754 rc
= exfat_directory_close(&di
);
756 fibril_mutex_unlock(&parentp
->idx
->lock
);
760 fibril_mutex_unlock(&parentp
->idx
->lock
);
761 fibril_mutex_lock(&childp
->idx
->lock
);
763 childp
->idx
->pfc
= parentp
->firstc
;
764 childp
->idx
->parent_fragmented
= parentp
->fragmented
;
765 childp
->idx
->pdi
= di
.pos
;
766 fibril_mutex_unlock(&childp
->idx
->lock
);
768 fibril_mutex_lock(&childp
->lock
);
770 childp
->dirty
= true; /* need to sync node */
771 fibril_mutex_unlock(&childp
->lock
);
774 * Hash in the index structure into the position hash.
776 exfat_idx_hashin(childp
->idx
);
782 int exfat_unlink(fs_node_t
*pfn
, fs_node_t
*cfn
, const char *nm
)
784 exfat_node_t
*parentp
= EXFAT_NODE(pfn
);
785 exfat_node_t
*childp
= EXFAT_NODE(cfn
);
792 rc
= exfat_has_children(&has_children
, cfn
);
798 fibril_mutex_lock(&parentp
->lock
);
799 fibril_mutex_lock(&childp
->lock
);
800 assert(childp
->lnkcnt
== 1);
801 fibril_mutex_lock(&childp
->idx
->lock
);
803 exfat_directory_t di
;
804 rc
= exfat_directory_open(parentp
,&di
);
807 rc
= exfat_directory_erase_file(&di
, childp
->idx
->pdi
);
810 rc
= exfat_directory_close(&di
);
814 /* remove the index structure from the position hash */
815 exfat_idx_hashout(childp
->idx
);
816 /* clear position information */
817 childp
->idx
->pfc
= 0;
818 childp
->idx
->pdi
= 0;
819 fibril_mutex_unlock(&childp
->idx
->lock
);
821 childp
->refcnt
++; /* keep the node in memory until destroyed */
822 childp
->dirty
= true;
823 fibril_mutex_unlock(&childp
->lock
);
824 fibril_mutex_unlock(&parentp
->lock
);
829 (void) exfat_directory_close(&di
);
830 fibril_mutex_unlock(&childp
->idx
->lock
);
831 fibril_mutex_unlock(&childp
->lock
);
832 fibril_mutex_unlock(&parentp
->lock
);
837 int exfat_has_children(bool *has_children
, fs_node_t
*fn
)
839 exfat_directory_t di
;
841 exfat_node_t
*nodep
= EXFAT_NODE(fn
);
844 *has_children
= false;
846 if (nodep
->type
!= EXFAT_DIRECTORY
)
849 fibril_mutex_lock(&nodep
->idx
->lock
);
851 rc
= exfat_directory_open(nodep
, &di
);
853 fibril_mutex_unlock(&nodep
->idx
->lock
);
858 rc
= exfat_directory_get(&di
, &d
);
860 (void) exfat_directory_close(&di
);
861 fibril_mutex_unlock(&nodep
->idx
->lock
);
864 switch (exfat_classify_dentry(d
)) {
865 case EXFAT_DENTRY_SKIP
:
866 case EXFAT_DENTRY_FREE
:
868 case EXFAT_DENTRY_LAST
:
869 *has_children
= false;
872 *has_children
= true;
875 } while (exfat_directory_next(&di
) == EOK
);
878 rc
= exfat_directory_close(&di
);
879 fibril_mutex_unlock(&nodep
->idx
->lock
);
884 fs_index_t
exfat_index_get(fs_node_t
*fn
)
886 return EXFAT_NODE(fn
)->idx
->index
;
889 aoff64_t
exfat_size_get(fs_node_t
*fn
)
891 return EXFAT_NODE(fn
)->size
;
894 unsigned exfat_lnkcnt_get(fs_node_t
*fn
)
896 return EXFAT_NODE(fn
)->lnkcnt
;
899 bool exfat_is_directory(fs_node_t
*fn
)
901 return EXFAT_NODE(fn
)->type
== EXFAT_DIRECTORY
;
904 bool exfat_is_file(fs_node_t
*fn
)
906 return EXFAT_NODE(fn
)->type
== EXFAT_FILE
;
909 service_id_t
exfat_service_get(fs_node_t
*node
)
914 int exfat_size_block(service_id_t service_id
, uint32_t *size
)
917 bs
= block_bb_get(service_id
);
923 int exfat_total_block_count(service_id_t service_id
, uint64_t *count
)
926 bs
= block_bb_get(service_id
);
927 *count
= DATA_CNT(bs
);
932 int exfat_free_block_count(service_id_t service_id
, uint64_t *count
)
935 exfat_node_t
*bmap_node
;
937 uint64_t free_block_count
= 0;
938 uint64_t block_count
;
942 rc
= exfat_total_block_count(service_id
, &block_count
);
946 bs
= block_bb_get(service_id
);
948 rc
= exfat_bitmap_get(&node
, service_id
);
952 bmap_node
= (exfat_node_t
*) node
->data
;
954 unsigned const bmap_sectors
= ROUND_UP(bmap_node
->size
, BPS(bs
)) /
957 for (sector
= 0; sector
< bmap_sectors
; ++sector
) {
963 rc
= exfat_block_get(&block
, bs
, bmap_node
, sector
,
966 free_block_count
= 0;
970 bitmap
= (uint8_t *) block
->data
;
972 for (bit
= 0; bit
< BPS(bs
) * 8 && block_count
> 0;
973 ++bit
, --block_count
) {
974 if (!(bitmap
[bit
/ 8] & (1 << (bit
% 8))))
980 if (block_count
== 0) {
981 /* Reached the end of the bitmap */
987 exfat_node_put(node
);
988 *count
= free_block_count
;
992 /** libfs operations */
993 libfs_ops_t exfat_libfs_ops
= {
994 .root_get
= exfat_root_get
,
995 .match
= exfat_match
,
996 .node_get
= exfat_node_get
,
997 .node_open
= exfat_node_open
,
998 .node_put
= exfat_node_put
,
999 .create
= exfat_create_node
,
1000 .destroy
= exfat_destroy_node
,
1002 .unlink
= exfat_unlink
,
1003 .has_children
= exfat_has_children
,
1004 .index_get
= exfat_index_get
,
1005 .size_get
= exfat_size_get
,
1006 .lnkcnt_get
= exfat_lnkcnt_get
,
1007 .is_directory
= exfat_is_directory
,
1008 .is_file
= exfat_is_file
,
1009 .service_get
= exfat_service_get
,
1010 .size_block
= exfat_size_block
,
1011 .total_block_count
= exfat_total_block_count
,
1012 .free_block_count
= exfat_free_block_count
1017 * VFS_OUT operations.
1020 /* Print debug info */
1022 static void exfat_fsinfo(exfat_bs_t *bs, service_id_t service_id)
1024 printf("exFAT file system mounted\n");
1025 printf("Version: %d.%d\n", bs->version.major, bs->version.minor);
1026 printf("Volume serial: %d\n", uint32_t_le2host(bs->volume_serial));
1027 printf("Volume first sector: %lld\n", VOL_FS(bs));
1028 printf("Volume sectors: %lld\n", VOL_CNT(bs));
1029 printf("FAT first sector: %d\n", FAT_FS(bs));
1030 printf("FAT sectors: %d\n", FAT_CNT(bs));
1031 printf("Data first sector: %d\n", DATA_FS(bs));
1032 printf("Data sectors: %d\n", DATA_CNT(bs));
1033 printf("Root dir first cluster: %d\n", ROOT_FC(bs));
1034 printf("Bytes per sector: %d\n", BPS(bs));
1035 printf("Sectors per cluster: %d\n", SPC(bs));
1036 printf("KBytes per cluster: %d\n", SPC(bs)*BPS(bs)/1024);
1039 exfat_cluster_t clst;
1040 for (i=0; i<=7; i++) {
1041 rc = exfat_get_cluster(bs, service_id, i, &clst);
1044 printf("Clst %d: %x", i, clst);
1046 printf(", Bitmap: %d\n", bitmap_is_free(bs, service_id, i)!=EOK);
1053 static int exfat_fsprobe(service_id_t service_id
, vfs_fs_probe_info_t
*info
)
1058 /* initialize libblock */
1059 rc
= block_init(service_id
, BS_SIZE
);
1063 /* prepare the boot block */
1064 rc
= block_bb_read(service_id
, BS_BLOCK
);
1066 block_fini(service_id
);
1070 /* get the buffer with the boot sector */
1071 bs
= block_bb_get(service_id
);
1073 /* Do some simple sanity checks on the file system. */
1074 rc
= exfat_sanity_check(bs
);
1076 (void) block_cache_fini(service_id
);
1077 block_fini(service_id
);
1082 exfat_mounted(service_id_t service_id
, const char *opts
, fs_index_t
*index
,
1083 aoff64_t
*size
, unsigned *linkcnt
)
1086 exfat_node_t
*rootp
= NULL
, *bitmapp
= NULL
, *uctablep
= NULL
;
1087 enum cache_mode cmode
;
1090 /* Check for option enabling write through. */
1091 if (str_cmp(opts
, "wtcache") == 0)
1092 cmode
= CACHE_MODE_WT
;
1094 cmode
= CACHE_MODE_WB
;
1096 /* initialize libblock */
1097 rc
= block_init(service_id
, BS_SIZE
);
1101 /* prepare the boot block */
1102 rc
= block_bb_read(service_id
, BS_BLOCK
);
1104 block_fini(service_id
);
1108 /* get the buffer with the boot sector */
1109 bs
= block_bb_get(service_id
);
1111 /* Do some simple sanity checks on the file system. */
1112 rc
= exfat_sanity_check(bs
);
1114 (void) block_cache_fini(service_id
);
1115 block_fini(service_id
);
1119 /* Initialize the block cache */
1120 rc
= block_cache_init(service_id
, BPS(bs
), 0 /* XXX */, cmode
);
1122 block_fini(service_id
);
1126 rc
= exfat_idx_init_by_service_id(service_id
);
1128 (void) block_cache_fini(service_id
);
1129 block_fini(service_id
);
1133 /* Initialize the root node. */
1134 rc
= exfat_node_get_new_by_pos(&rootp
, service_id
, EXFAT_ROOT_PAR
,
1137 (void) block_cache_fini(service_id
);
1138 block_fini(service_id
);
1139 exfat_idx_fini_by_service_id(service_id
);
1142 assert(rootp
->idx
->index
== EXFAT_ROOT_IDX
);
1144 rootp
->type
= EXFAT_DIRECTORY
;
1145 rootp
->firstc
= ROOT_FC(bs
);
1146 rootp
->fragmented
= true;
1148 rootp
->lnkcnt
= 0; /* FS root is not linked */
1151 rc
= exfat_clusters_get(&clusters
, bs
, service_id
, rootp
->firstc
);
1154 (void) block_cache_fini(service_id
);
1155 block_fini(service_id
);
1156 exfat_idx_fini_by_service_id(service_id
);
1159 rootp
->size
= BPS(bs
) * SPC(bs
) * clusters
;
1160 fibril_mutex_unlock(&rootp
->idx
->lock
);
1162 /* Open root directory and looking for Bitmap and UC-Table */
1163 exfat_directory_t di
;
1165 rc
= exfat_directory_open(rootp
, &di
);
1168 (void) block_cache_fini(service_id
);
1169 block_fini(service_id
);
1170 exfat_idx_fini_by_service_id(service_id
);
1174 /* Initialize the bitmap node. */
1175 rc
= exfat_directory_find(&di
, EXFAT_DENTRY_BITMAP
, &de
);
1178 (void) block_cache_fini(service_id
);
1179 block_fini(service_id
);
1180 exfat_idx_fini_by_service_id(service_id
);
1184 rc
= exfat_node_get_new_by_pos(&bitmapp
, service_id
, rootp
->firstc
,
1188 (void) block_cache_fini(service_id
);
1189 block_fini(service_id
);
1190 exfat_idx_fini_by_service_id(service_id
);
1193 assert(bitmapp
->idx
->index
== EXFAT_BITMAP_IDX
);
1194 fibril_mutex_unlock(&bitmapp
->idx
->lock
);
1196 bitmapp
->type
= EXFAT_BITMAP
;
1197 bitmapp
->firstc
= uint32_t_le2host(de
->bitmap
.firstc
);
1198 bitmapp
->fragmented
= true;
1199 bitmapp
->idx
->parent_fragmented
= true;
1200 bitmapp
->refcnt
= 1;
1201 bitmapp
->lnkcnt
= 0;
1202 bitmapp
->size
= uint64_t_le2host(de
->bitmap
.size
);
1204 /* Initialize the uctable node. */
1205 rc
= exfat_directory_seek(&di
, 0);
1209 (void) block_cache_fini(service_id
);
1210 block_fini(service_id
);
1211 exfat_idx_fini_by_service_id(service_id
);
1215 rc
= exfat_directory_find(&di
, EXFAT_DENTRY_UCTABLE
, &de
);
1219 (void) block_cache_fini(service_id
);
1220 block_fini(service_id
);
1221 exfat_idx_fini_by_service_id(service_id
);
1225 rc
= exfat_node_get_new_by_pos(&uctablep
, service_id
, rootp
->firstc
,
1230 (void) block_cache_fini(service_id
);
1231 block_fini(service_id
);
1232 exfat_idx_fini_by_service_id(service_id
);
1235 assert(uctablep
->idx
->index
== EXFAT_UCTABLE_IDX
);
1236 fibril_mutex_unlock(&uctablep
->idx
->lock
);
1238 uctablep
->type
= EXFAT_UCTABLE
;
1239 uctablep
->firstc
= uint32_t_le2host(de
->uctable
.firstc
);
1240 uctablep
->fragmented
= true;
1241 uctablep
->idx
->parent_fragmented
= true;
1242 uctablep
->refcnt
= 1;
1243 uctablep
->lnkcnt
= 0;
1244 uctablep
->size
= uint64_t_le2host(de
->uctable
.size
);
1246 rc
= exfat_directory_close(&di
);
1251 (void) block_cache_fini(service_id
);
1252 block_fini(service_id
);
1253 exfat_idx_fini_by_service_id(service_id
);
1257 /* exfat_fsinfo(bs, service_id); */
1259 *index
= rootp
->idx
->index
;
1260 *size
= rootp
->size
;
1261 *linkcnt
= rootp
->lnkcnt
;
1266 static int exfat_unmounted(service_id_t service_id
)
1269 exfat_node_t
*nodep
;
1272 rc
= exfat_root_get(&fn
, service_id
);
1275 nodep
= EXFAT_NODE(fn
);
1278 * We expect exactly two references on the root node. One for the
1279 * fat_root_get() above and one created in fat_mounted().
1281 if (nodep
->refcnt
!= 2) {
1282 (void) exfat_node_put(fn
);
1287 * Put the root node and force it to the FAT free node list.
1289 (void) exfat_node_put(fn
);
1290 (void) exfat_node_put(fn
);
1293 * Perform cleanup of the node structures, index structures and
1294 * associated data. Write back this file system's dirty blocks and
1295 * stop using libblock for this instance.
1297 (void) exfat_node_fini_by_service_id(service_id
);
1298 exfat_idx_fini_by_service_id(service_id
);
1299 (void) block_cache_fini(service_id
);
1300 block_fini(service_id
);
1306 exfat_read(service_id_t service_id
, fs_index_t index
, aoff64_t pos
,
1310 exfat_node_t
*nodep
;
1316 rc
= exfat_node_get(&fn
, service_id
, index
);
1321 nodep
= EXFAT_NODE(fn
);
1323 ipc_callid_t callid
;
1325 if (!async_data_read_receive(&callid
, &len
)) {
1327 async_answer_0(callid
, EINVAL
);
1331 bs
= block_bb_get(service_id
);
1333 if (nodep
->type
== EXFAT_FILE
) {
1335 * Our strategy for regular file reads is to read one block at
1336 * most and make use of the possibility to return less data than
1337 * requested. This keeps the code very simple.
1339 if (pos
>= nodep
->size
) {
1340 /* reading beyond the EOF */
1342 (void) async_data_read_finalize(callid
, NULL
, 0);
1344 bytes
= min(len
, BPS(bs
) - pos
% BPS(bs
));
1345 bytes
= min(bytes
, nodep
->size
- pos
);
1346 rc
= exfat_block_get(&b
, bs
, nodep
, pos
/ BPS(bs
),
1350 async_answer_0(callid
, rc
);
1353 (void) async_data_read_finalize(callid
,
1354 b
->data
+ pos
% BPS(bs
), bytes
);
1362 if (nodep
->type
!= EXFAT_DIRECTORY
) {
1363 async_answer_0(callid
, ENOTSUP
);
1367 aoff64_t spos
= pos
;
1368 char name
[EXFAT_FILENAME_LEN
+ 1];
1369 exfat_file_dentry_t df
;
1370 exfat_stream_dentry_t ds
;
1372 assert(nodep
->size
% BPS(bs
) == 0);
1373 assert(BPS(bs
) % sizeof(exfat_dentry_t
) == 0);
1375 exfat_directory_t di
;
1376 rc
= exfat_directory_open(nodep
, &di
);
1380 rc
= exfat_directory_seek(&di
, pos
);
1382 (void) exfat_directory_close(&di
);
1386 rc
= exfat_directory_read_file(&di
, name
, EXFAT_FILENAME_LEN
,
1390 else if (rc
== ENOENT
)
1393 (void) exfat_directory_close(&di
);
1396 (void) exfat_node_put(fn
);
1397 async_answer_0(callid
, rc
);
1401 rc
= exfat_directory_close(&di
);
1404 rc
= exfat_node_put(fn
);
1405 async_answer_0(callid
, rc
!= EOK
? rc
: ENOENT
);
1407 return rc
!= EOK
? rc
: ENOENT
;
1411 rc
= exfat_directory_close(&di
);
1414 (void) async_data_read_finalize(callid
, name
,
1415 str_size(name
) + 1);
1416 bytes
= (pos
- spos
) + 1;
1419 rc
= exfat_node_put(fn
);
1424 static int exfat_close(service_id_t service_id
, fs_index_t index
)
1429 static int exfat_sync(service_id_t service_id
, fs_index_t index
)
1432 int rc
= exfat_node_get(&fn
, service_id
, index
);
1438 exfat_node_t
*nodep
= EXFAT_NODE(fn
);
1440 nodep
->dirty
= true;
1441 rc
= exfat_node_sync(nodep
);
1448 exfat_write(service_id_t service_id
, fs_index_t index
, aoff64_t pos
,
1449 size_t *wbytes
, aoff64_t
*nsize
)
1452 exfat_node_t
*nodep
;
1457 int flags
= BLOCK_FLAGS_NONE
;
1460 rc
= exfat_node_get(&fn
, service_id
, index
);
1465 nodep
= EXFAT_NODE(fn
);
1467 ipc_callid_t callid
;
1469 if (!async_data_write_receive(&callid
, &len
)) {
1470 (void) exfat_node_put(fn
);
1471 async_answer_0(callid
, EINVAL
);
1475 bs
= block_bb_get(service_id
);
1478 * In all scenarios, we will attempt to write out only one block worth
1479 * of data at maximum. There might be some more efficient approaches,
1480 * but this one greatly simplifies fat_write(). Note that we can afford
1481 * to do this because the client must be ready to handle the return
1482 * value signalizing a smaller number of bytes written.
1484 bytes
= min(len
, BPS(bs
) - pos
% BPS(bs
));
1485 if (bytes
== BPS(bs
))
1486 flags
|= BLOCK_FLAGS_NOREAD
;
1488 boundary
= ROUND_UP(nodep
->size
, BPC(bs
));
1489 if (pos
>= boundary
) {
1491 nclsts
= (ROUND_UP(pos
+ bytes
, BPC(bs
)) - boundary
) / BPC(bs
);
1492 rc
= exfat_node_expand(service_id
, nodep
, nclsts
);
1494 /* could not expand node */
1495 (void) exfat_node_put(fn
);
1496 async_answer_0(callid
, rc
);
1501 if (pos
+ bytes
> nodep
->size
) {
1502 nodep
->size
= pos
+ bytes
;
1503 nodep
->dirty
= true; /* need to sync node */
1507 * This is the easier case - we are either overwriting already
1508 * existing contents or writing behind the EOF, but still within
1509 * the limits of the last cluster. The node size may grow to the
1510 * next block size boundary.
1512 rc
= exfat_block_get(&b
, bs
, nodep
, pos
/ BPS(bs
), flags
);
1514 (void) exfat_node_put(fn
);
1515 async_answer_0(callid
, rc
);
1519 (void) async_data_write_finalize(callid
,
1520 b
->data
+ pos
% BPS(bs
), bytes
);
1521 b
->dirty
= true; /* need to sync block */
1524 (void) exfat_node_put(fn
);
1529 *nsize
= nodep
->size
;
1530 rc
= exfat_node_put(fn
);
1535 exfat_truncate(service_id_t service_id
, fs_index_t index
, aoff64_t size
)
1538 exfat_node_t
*nodep
;
1542 rc
= exfat_node_get(&fn
, service_id
, index
);
1547 nodep
= EXFAT_NODE(fn
);
1549 bs
= block_bb_get(service_id
);
1551 if (nodep
->size
== size
) {
1553 } else if (nodep
->size
< size
) {
1555 * The standard says we have the freedom to grow the node.
1556 * For now, we simply return an error.
1559 } else if (ROUND_UP(nodep
->size
, BPC(bs
)) == ROUND_UP(size
, BPC(bs
))) {
1561 * The node will be shrunk, but no clusters will be deallocated.
1564 nodep
->dirty
= true; /* need to sync node */
1567 rc
= exfat_node_shrink(service_id
, nodep
, size
);
1570 int rc2
= exfat_node_put(fn
);
1571 if (rc
== EOK
&& rc2
!= EOK
)
1577 static int exfat_destroy(service_id_t service_id
, fs_index_t index
)
1580 exfat_node_t
*nodep
;
1583 rc
= exfat_node_get(&fn
, service_id
, index
);
1589 nodep
= EXFAT_NODE(fn
);
1591 * We should have exactly two references. One for the above
1592 * call to fat_node_get() and one from fat_unlink().
1594 assert(nodep
->refcnt
== 2);
1596 rc
= exfat_destroy_node(fn
);
1600 vfs_out_ops_t exfat_ops
= {
1601 .fsprobe
= exfat_fsprobe
,
1602 .mounted
= exfat_mounted
,
1603 .unmounted
= exfat_unmounted
,
1605 .write
= exfat_write
,
1606 .truncate
= exfat_truncate
,
1607 .close
= exfat_close
,
1608 .destroy
= exfat_destroy
,