2 * Copyright (c) 2011 Martin Sucha
3 * Copyright (c) 2012 Frantisek Princ
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.
35 * @brief VFS operations for ext4 filesystem.
39 #include <fibril_synch.h>
44 #include <adt/hash_table.h>
48 #include "../../vfs/vfs.h"
50 #define EXT4FS_NODE(node) \
51 ((node) ? (ext4fs_node_t *) (node)->data : NULL)
54 * Type for holding an instance of mounted partition.
56 typedef struct ext4fs_instance
{
58 service_id_t service_id
;
59 ext4_filesystem_t
*filesystem
;
60 unsigned int open_nodes_count
;
64 * Type for wrapping common fs_node and add some useful pointers.
66 typedef struct ext4fs_node
{
67 ext4fs_instance_t
*instance
;
68 ext4_inode_ref_t
*inode_ref
;
71 unsigned int references
;
74 /* Forward declarations of auxiliary functions */
76 static int ext4fs_read_directory(ipc_callid_t
, aoff64_t
, size_t,
77 ext4fs_instance_t
*, ext4_inode_ref_t
*, size_t *);
78 static int ext4fs_read_file(ipc_callid_t
, aoff64_t
, size_t, ext4fs_instance_t
*,
79 ext4_inode_ref_t
*, size_t *);
80 static bool ext4fs_is_dots(const uint8_t *, size_t);
81 static int ext4fs_instance_get(service_id_t
, ext4fs_instance_t
**);
82 static int ext4fs_node_get_core(fs_node_t
**, ext4fs_instance_t
*, fs_index_t
);
83 static int ext4fs_node_put_core(ext4fs_node_t
*);
85 /* Forward declarations of ext4 libfs operations. */
87 static int ext4fs_root_get(fs_node_t
**, service_id_t
);
88 static int ext4fs_match(fs_node_t
**, fs_node_t
*, const char *);
89 static int ext4fs_node_get(fs_node_t
**, service_id_t
, fs_index_t
);
90 static int ext4fs_node_open(fs_node_t
*);
91 static int ext4fs_node_put(fs_node_t
*);
92 static int ext4fs_create_node(fs_node_t
**, service_id_t
, int);
93 static int ext4fs_destroy_node(fs_node_t
*);
94 static int ext4fs_link(fs_node_t
*, fs_node_t
*, const char *);
95 static int ext4fs_unlink(fs_node_t
*, fs_node_t
*, const char *);
96 static int ext4fs_has_children(bool *, fs_node_t
*);
97 static fs_index_t
ext4fs_index_get(fs_node_t
*);
98 static aoff64_t
ext4fs_size_get(fs_node_t
*);
99 static unsigned ext4fs_lnkcnt_get(fs_node_t
*);
100 static bool ext4fs_is_directory(fs_node_t
*);
101 static bool ext4fs_is_file(fs_node_t
*node
);
102 static service_id_t
ext4fs_service_get(fs_node_t
*node
);
103 static int ext4fs_size_block(service_id_t
, uint32_t *);
104 static int ext4fs_total_block_count(service_id_t
, uint64_t *);
105 static int ext4fs_free_block_count(service_id_t
, uint64_t *);
107 /* Static variables */
109 static LIST_INITIALIZE(instance_list
);
110 static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex
);
111 static hash_table_t open_nodes
;
112 static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock
);
114 /* Hash table interface for open nodes hash table */
117 service_id_t service_id
;
121 static size_t open_nodes_key_hash(void *key_arg
)
123 node_key_t
*key
= (node_key_t
*)key_arg
;
124 return hash_combine(key
->service_id
, key
->index
);
127 static size_t open_nodes_hash(const ht_link_t
*item
)
129 ext4fs_node_t
*enode
= hash_table_get_inst(item
, ext4fs_node_t
, link
);
130 return hash_combine(enode
->instance
->service_id
, enode
->inode_ref
->index
);
133 static bool open_nodes_key_equal(void *key_arg
, const ht_link_t
*item
)
135 node_key_t
*key
= (node_key_t
*)key_arg
;
136 ext4fs_node_t
*enode
= hash_table_get_inst(item
, ext4fs_node_t
, link
);
138 return key
->service_id
== enode
->instance
->service_id
139 && key
->index
== enode
->inode_ref
->index
;
142 static hash_table_ops_t open_nodes_ops
= {
143 .hash
= open_nodes_hash
,
144 .key_hash
= open_nodes_key_hash
,
145 .key_equal
= open_nodes_key_equal
,
147 .remove_callback
= NULL
,
150 /** Basic initialization of the driver.
152 * This is only needed to create the hash table
153 * for storing open nodes.
158 int ext4fs_global_init(void)
160 if (!hash_table_create(&open_nodes
, 0, 0, &open_nodes_ops
))
166 /* Finalization of the driver.
168 * This is only needed to destroy the hash table.
172 int ext4fs_global_fini(void)
174 hash_table_destroy(&open_nodes
);
179 * Ext4 libfs operations.
182 /** Get instance from internal table by service_id.
184 * @param service_id Device identifier
185 * @param inst Output instance if successful operation
190 int ext4fs_instance_get(service_id_t service_id
, ext4fs_instance_t
**inst
)
192 fibril_mutex_lock(&instance_list_mutex
);
194 if (list_empty(&instance_list
)) {
195 fibril_mutex_unlock(&instance_list_mutex
);
199 list_foreach(instance_list
, link
, ext4fs_instance_t
, tmp
) {
200 if (tmp
->service_id
== service_id
) {
202 fibril_mutex_unlock(&instance_list_mutex
);
207 fibril_mutex_unlock(&instance_list_mutex
);
211 /** Get root node of filesystem specified by service_id.
213 * @param rfn Output pointer to loaded node
214 * @param service_id Device to load root node from
219 int ext4fs_root_get(fs_node_t
**rfn
, service_id_t service_id
)
221 return ext4fs_node_get(rfn
, service_id
, EXT4_INODE_ROOT_INDEX
);
224 /** Check if specified name (component) matches with any directory entry.
226 * If match is found, load and return matching node.
228 * @param rfn Output pointer to node if operation successful
229 * @param pfn Parent directory node
230 * @param component Name to check directory for
235 int ext4fs_match(fs_node_t
**rfn
, fs_node_t
*pfn
, const char *component
)
237 ext4fs_node_t
*eparent
= EXT4FS_NODE(pfn
);
238 ext4_filesystem_t
*fs
= eparent
->instance
->filesystem
;
240 if (!ext4_inode_is_type(fs
->superblock
, eparent
->inode_ref
->inode
,
241 EXT4_INODE_MODE_DIRECTORY
))
244 /* Try to find entry */
245 ext4_directory_search_result_t result
;
246 int rc
= ext4_directory_find_entry(&result
, eparent
->inode_ref
,
257 /* Load node from search result */
258 uint32_t inode
= ext4_directory_entry_ll_get_inode(result
.dentry
);
259 rc
= ext4fs_node_get_core(rfn
, eparent
->instance
, inode
);
266 /* Destroy search result structure */
267 int const rc2
= ext4_directory_destroy_result(&result
);
268 return rc
== EOK
? rc2
: rc
;
271 /** Get node specified by index
273 * It's wrapper for node_put_core operation
275 * @param rfn Output pointer to loaded node if operation successful
276 * @param service_id Device identifier
277 * @param index Node index (here i-node number)
282 int ext4fs_node_get(fs_node_t
**rfn
, service_id_t service_id
, fs_index_t index
)
284 ext4fs_instance_t
*inst
;
285 int rc
= ext4fs_instance_get(service_id
, &inst
);
289 return ext4fs_node_get_core(rfn
, inst
, index
);
292 /** Main function for getting node from the filesystem.
294 * @param rfn Output point to loaded node if operation successful
295 * @param inst Instance of filesystem
296 * @param index Index of node (i-node number)
301 int ext4fs_node_get_core(fs_node_t
**rfn
, ext4fs_instance_t
*inst
,
304 fibril_mutex_lock(&open_nodes_lock
);
306 /* Check if the node is not already open */
308 .service_id
= inst
->service_id
,
312 ht_link_t
*already_open
= hash_table_find(&open_nodes
, &key
);
313 ext4fs_node_t
*enode
= NULL
;
315 enode
= hash_table_get_inst(already_open
, ext4fs_node_t
, link
);
316 *rfn
= enode
->fs_node
;
319 fibril_mutex_unlock(&open_nodes_lock
);
323 /* Prepare new enode */
324 enode
= malloc(sizeof(ext4fs_node_t
));
326 fibril_mutex_unlock(&open_nodes_lock
);
330 /* Prepare new fs_node and initialize */
331 fs_node_t
*fs_node
= malloc(sizeof(fs_node_t
));
332 if (fs_node
== NULL
) {
334 fibril_mutex_unlock(&open_nodes_lock
);
338 fs_node_initialize(fs_node
);
340 /* Load i-node from filesystem */
341 ext4_inode_ref_t
*inode_ref
;
342 int rc
= ext4_filesystem_get_inode_ref(inst
->filesystem
, index
,
347 fibril_mutex_unlock(&open_nodes_lock
);
351 /* Initialize enode */
352 enode
->inode_ref
= inode_ref
;
353 enode
->instance
= inst
;
354 enode
->references
= 1;
355 enode
->fs_node
= fs_node
;
357 fs_node
->data
= enode
;
360 hash_table_insert(&open_nodes
, &enode
->link
);
361 inst
->open_nodes_count
++;
363 fibril_mutex_unlock(&open_nodes_lock
);
368 /** Put previously loaded node.
370 * @param enode Node to put back
375 int ext4fs_node_put_core(ext4fs_node_t
*enode
)
377 hash_table_remove_item(&open_nodes
, &enode
->link
);
378 assert(enode
->instance
->open_nodes_count
> 0);
379 enode
->instance
->open_nodes_count
--;
381 /* Put inode back in filesystem */
382 int rc
= ext4_filesystem_put_inode_ref(enode
->inode_ref
);
386 /* Destroy data structure */
387 free(enode
->fs_node
);
395 * This operation is stateless in this driver.
397 * @param fn Node to open
402 int ext4fs_node_open(fs_node_t
*fn
)
404 /* Stateless operation */
408 /** Put previously loaded node.
410 * A wrapper for node_put_core operation
412 * @param fn Node to put back
416 int ext4fs_node_put(fs_node_t
*fn
)
418 fibril_mutex_lock(&open_nodes_lock
);
420 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
421 assert(enode
->references
> 0);
423 if (enode
->references
== 0) {
424 int rc
= ext4fs_node_put_core(enode
);
426 fibril_mutex_unlock(&open_nodes_lock
);
431 fibril_mutex_unlock(&open_nodes_lock
);
436 /** Create new node in filesystem.
438 * @param rfn Output pointer to newly created node if successful
439 * @param service_id Device identifier, where the filesystem is
440 * @param flags Flags for specification of new node parameters
445 int ext4fs_create_node(fs_node_t
**rfn
, service_id_t service_id
, int flags
)
448 ext4fs_node_t
*enode
;
449 enode
= malloc(sizeof(ext4fs_node_t
));
453 /* Allocate fs_node */
455 fs_node
= malloc(sizeof(fs_node_t
));
456 if (fs_node
== NULL
) {
462 ext4fs_instance_t
*inst
;
463 int rc
= ext4fs_instance_get(service_id
, &inst
);
470 /* Allocate new i-node in filesystem */
471 ext4_inode_ref_t
*inode_ref
;
472 rc
= ext4_filesystem_alloc_inode(inst
->filesystem
, &inode_ref
, flags
);
479 /* Do some interconnections in references */
480 enode
->inode_ref
= inode_ref
;
481 enode
->instance
= inst
;
482 enode
->references
= 1;
484 fibril_mutex_lock(&open_nodes_lock
);
485 hash_table_insert(&open_nodes
, &enode
->link
);
486 fibril_mutex_unlock(&open_nodes_lock
);
487 inst
->open_nodes_count
++;
489 enode
->inode_ref
->dirty
= true;
491 fs_node_initialize(fs_node
);
492 fs_node
->data
= enode
;
493 enode
->fs_node
= fs_node
;
499 /** Destroy existing node.
501 * @param fs Node to destroy
506 int ext4fs_destroy_node(fs_node_t
*fn
)
508 /* If directory, check for children */
510 int rc
= ext4fs_has_children(&has_children
, fn
);
521 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
522 ext4_inode_ref_t
*inode_ref
= enode
->inode_ref
;
524 /* Release data blocks */
525 rc
= ext4_filesystem_truncate_inode(inode_ref
, 0);
532 * TODO: Sset real deletion time when it will be supported.
533 * Temporary set fake deletion time.
535 ext4_inode_set_deletion_time(inode_ref
->inode
, 0xdeadbeef);
536 inode_ref
->dirty
= true;
539 rc
= ext4_filesystem_free_inode(inode_ref
);
545 return ext4fs_node_put(fn
);
548 /** Link the specfied node to directory.
550 * @param pfn Parent node to link in
551 * @param cfn Node to be linked
552 * @param name Name which will be assigned to directory entry
557 int ext4fs_link(fs_node_t
*pfn
, fs_node_t
*cfn
, const char *name
)
559 /* Check maximum name length */
560 if (str_size(name
) > EXT4_DIRECTORY_FILENAME_LEN
)
563 ext4fs_node_t
*parent
= EXT4FS_NODE(pfn
);
564 ext4fs_node_t
*child
= EXT4FS_NODE(cfn
);
565 ext4_filesystem_t
*fs
= parent
->instance
->filesystem
;
567 /* Add entry to parent directory */
568 int rc
= ext4_directory_add_entry(parent
->inode_ref
, name
,
573 /* Fill new dir -> add '.' and '..' entries */
574 if (ext4_inode_is_type(fs
->superblock
, child
->inode_ref
->inode
,
575 EXT4_INODE_MODE_DIRECTORY
)) {
576 rc
= ext4_directory_add_entry(child
->inode_ref
, ".",
579 ext4_directory_remove_entry(parent
->inode_ref
, name
);
583 rc
= ext4_directory_add_entry(child
->inode_ref
, "..",
586 ext4_directory_remove_entry(parent
->inode_ref
, name
);
587 ext4_directory_remove_entry(child
->inode_ref
, ".");
591 /* Initialize directory index if supported */
592 if (ext4_superblock_has_feature_compatible(fs
->superblock
,
593 EXT4_FEATURE_COMPAT_DIR_INDEX
)) {
594 rc
= ext4_directory_dx_init(child
->inode_ref
);
598 ext4_inode_set_flag(child
->inode_ref
->inode
,
599 EXT4_INODE_FLAG_INDEX
);
600 child
->inode_ref
->dirty
= true;
603 uint16_t parent_links
=
604 ext4_inode_get_links_count(parent
->inode_ref
->inode
);
606 ext4_inode_set_links_count(parent
->inode_ref
->inode
, parent_links
);
608 parent
->inode_ref
->dirty
= true;
611 uint16_t child_links
=
612 ext4_inode_get_links_count(child
->inode_ref
->inode
);
614 ext4_inode_set_links_count(child
->inode_ref
->inode
, child_links
);
616 child
->inode_ref
->dirty
= true;
621 /** Unlink node from specified directory.
623 * @param pfn Parent node to delete node from
624 * @param cfn Child node to be unlinked from directory
625 * @param name Name of entry that will be removed
630 int ext4fs_unlink(fs_node_t
*pfn
, fs_node_t
*cfn
, const char *name
)
633 int rc
= ext4fs_has_children(&has_children
, cfn
);
637 /* Cannot unlink non-empty node */
641 /* Remove entry from parent directory */
642 ext4_inode_ref_t
*parent
= EXT4FS_NODE(pfn
)->inode_ref
;
643 rc
= ext4_directory_remove_entry(parent
, name
);
647 /* Decrement links count */
648 ext4_inode_ref_t
*child_inode_ref
= EXT4FS_NODE(cfn
)->inode_ref
;
651 ext4_inode_get_links_count(child_inode_ref
->inode
);
654 /* If directory - handle links from parent */
655 if ((lnk_count
<= 1) && (ext4fs_is_directory(cfn
))) {
656 assert(lnk_count
== 1);
660 ext4_inode_ref_t
*parent_inode_ref
= EXT4FS_NODE(pfn
)->inode_ref
;
662 uint32_t parent_lnk_count
= ext4_inode_get_links_count(
663 parent_inode_ref
->inode
);
666 ext4_inode_set_links_count(parent_inode_ref
->inode
, parent_lnk_count
);
668 parent
->dirty
= true;
672 * TODO: Update timestamps of the parent
673 * (when we have wall-clock time).
675 * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
676 * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
677 * parent->dirty = true;
681 * TODO: Update timestamp for inode.
683 * ext4_inode_set_change_inode_time(child_inode_ref->inode,
687 ext4_inode_set_links_count(child_inode_ref
->inode
, lnk_count
);
688 child_inode_ref
->dirty
= true;
693 /** Check if specified node has children.
695 * For files is response allways false and check is executed only for directories.
697 * @param has_children Output value for response
698 * @param fn Node to check
703 int ext4fs_has_children(bool *has_children
, fs_node_t
*fn
)
705 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
706 ext4_filesystem_t
*fs
= enode
->instance
->filesystem
;
708 /* Check if node is directory */
709 if (!ext4_inode_is_type(fs
->superblock
, enode
->inode_ref
->inode
,
710 EXT4_INODE_MODE_DIRECTORY
)) {
711 *has_children
= false;
715 ext4_directory_iterator_t it
;
716 int rc
= ext4_directory_iterator_init(&it
, enode
->inode_ref
, 0);
720 /* Find a non-empty directory entry */
722 while (it
.current
!= NULL
) {
723 if (it
.current
->inode
!= 0) {
725 ext4_directory_entry_ll_get_name_length(fs
->superblock
,
727 if (!ext4fs_is_dots(it
.current
->name
, name_size
)) {
733 rc
= ext4_directory_iterator_next(&it
);
735 ext4_directory_iterator_fini(&it
);
740 rc
= ext4_directory_iterator_fini(&it
);
744 *has_children
= found
;
749 /** Unpack index number from node.
751 * @param fn Node to load index from
753 * @return Index number of i-node
756 fs_index_t
ext4fs_index_get(fs_node_t
*fn
)
758 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
759 return enode
->inode_ref
->index
;
762 /** Get real size of file / directory.
764 * @param fn Node to get size of
766 * @return Real size of node
769 aoff64_t
ext4fs_size_get(fs_node_t
*fn
)
771 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
772 ext4_superblock_t
*sb
= enode
->instance
->filesystem
->superblock
;
773 return ext4_inode_get_size(sb
, enode
->inode_ref
->inode
);
776 /** Get number of links to specified node.
778 * @param fn Node to get links to
780 * @return Number of links
783 unsigned ext4fs_lnkcnt_get(fs_node_t
*fn
)
785 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
786 uint32_t lnkcnt
= ext4_inode_get_links_count(enode
->inode_ref
->inode
);
788 if (ext4fs_is_directory(fn
)) {
795 /* For regular files return real links count */
799 /** Check if node is directory.
801 * @param fn Node to check
803 * @return Result of check
806 bool ext4fs_is_directory(fs_node_t
*fn
)
808 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
809 ext4_superblock_t
*sb
= enode
->instance
->filesystem
->superblock
;
811 return ext4_inode_is_type(sb
, enode
->inode_ref
->inode
,
812 EXT4_INODE_MODE_DIRECTORY
);
815 /** Check if node is regular file.
817 * @param fn Node to check
819 * @return Result of check
822 bool ext4fs_is_file(fs_node_t
*fn
)
824 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
825 ext4_superblock_t
*sb
= enode
->instance
->filesystem
->superblock
;
827 return ext4_inode_is_type(sb
, enode
->inode_ref
->inode
,
828 EXT4_INODE_MODE_FILE
);
831 /** Extract device identifier from node.
833 * @param node Node to extract id from
835 * @return id of device, where is the filesystem
838 service_id_t
ext4fs_service_get(fs_node_t
*fn
)
840 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
841 return enode
->instance
->service_id
;
844 int ext4fs_size_block(service_id_t service_id
, uint32_t *size
)
846 ext4fs_instance_t
*inst
;
847 int rc
= ext4fs_instance_get(service_id
, &inst
);
854 ext4_superblock_t
*sb
= inst
->filesystem
->superblock
;
855 *size
= ext4_superblock_get_block_size(sb
);
860 int ext4fs_total_block_count(service_id_t service_id
, uint64_t *count
)
862 ext4fs_instance_t
*inst
;
863 int rc
= ext4fs_instance_get(service_id
, &inst
);
870 ext4_superblock_t
*sb
= inst
->filesystem
->superblock
;
871 *count
= ext4_superblock_get_blocks_count(sb
);
876 int ext4fs_free_block_count(service_id_t service_id
, uint64_t *count
)
878 ext4fs_instance_t
*inst
;
879 int rc
= ext4fs_instance_get(service_id
, &inst
);
883 ext4_superblock_t
*sb
= inst
->filesystem
->superblock
;
884 *count
= ext4_superblock_get_free_blocks_count(sb
);
892 libfs_ops_t ext4fs_libfs_ops
= {
893 .root_get
= ext4fs_root_get
,
894 .match
= ext4fs_match
,
895 .node_get
= ext4fs_node_get
,
896 .node_open
= ext4fs_node_open
,
897 .node_put
= ext4fs_node_put
,
898 .create
= ext4fs_create_node
,
899 .destroy
= ext4fs_destroy_node
,
901 .unlink
= ext4fs_unlink
,
902 .has_children
= ext4fs_has_children
,
903 .index_get
= ext4fs_index_get
,
904 .size_get
= ext4fs_size_get
,
905 .lnkcnt_get
= ext4fs_lnkcnt_get
,
906 .is_directory
= ext4fs_is_directory
,
907 .is_file
= ext4fs_is_file
,
908 .service_get
= ext4fs_service_get
,
909 .size_block
= ext4fs_size_block
,
910 .total_block_count
= ext4fs_total_block_count
,
911 .free_block_count
= ext4fs_free_block_count
920 * Try to mount specified filesystem from device.
922 * @param service_id Identifier of device
923 * @param opts Mount options
924 * @param index Output value - index of root node
925 * @param size Output value - size of root node
926 * @param lnkcnt Output value - link count of root node
931 static int ext4fs_mounted(service_id_t service_id
, const char *opts
,
932 fs_index_t
*index
, aoff64_t
*size
, unsigned *lnkcnt
)
934 /* Allocate libext4 filesystem structure */
935 ext4_filesystem_t
*fs
= (ext4_filesystem_t
*)
936 malloc(sizeof(ext4_filesystem_t
));
940 /* Allocate instance structure */
941 ext4fs_instance_t
*inst
= (ext4fs_instance_t
*)
942 malloc(sizeof(ext4fs_instance_t
));
948 enum cache_mode cmode
;
949 if (str_cmp(opts
, "wtcache") == 0)
950 cmode
= CACHE_MODE_WT
;
952 cmode
= CACHE_MODE_WB
;
954 /* Initialize the filesystem */
955 int rc
= ext4_filesystem_init(fs
, service_id
, cmode
);
962 /* Do some sanity checking */
963 rc
= ext4_filesystem_check_sanity(fs
);
965 ext4_filesystem_fini(fs
);
973 rc
= ext4_filesystem_check_features(fs
, &read_only
);
975 ext4_filesystem_fini(fs
);
981 /* Initialize instance */
982 link_initialize(&inst
->link
);
983 inst
->service_id
= service_id
;
984 inst
->filesystem
= fs
;
985 inst
->open_nodes_count
= 0;
988 fs_node_t
*root_node
;
989 rc
= ext4fs_node_get_core(&root_node
, inst
, EXT4_INODE_ROOT_INDEX
);
991 ext4_filesystem_fini(fs
);
997 /* Add instance to the list */
998 fibril_mutex_lock(&instance_list_mutex
);
999 list_append(&inst
->link
, &instance_list
);
1000 fibril_mutex_unlock(&instance_list_mutex
);
1002 ext4fs_node_t
*enode
= EXT4FS_NODE(root_node
);
1004 *index
= EXT4_INODE_ROOT_INDEX
;
1005 *size
= ext4_inode_get_size(fs
->superblock
, enode
->inode_ref
->inode
);
1008 return ext4fs_node_put(root_node
);
1011 /** Unmount operation.
1013 * Correctly release the filesystem.
1015 * @param service_id Device to be unmounted
1017 * @return Error code
1020 static int ext4fs_unmounted(service_id_t service_id
)
1022 ext4fs_instance_t
*inst
;
1023 int rc
= ext4fs_instance_get(service_id
, &inst
);
1027 fibril_mutex_lock(&open_nodes_lock
);
1029 if (inst
->open_nodes_count
!= 0) {
1030 fibril_mutex_unlock(&open_nodes_lock
);
1034 /* Remove the instance from the list */
1035 fibril_mutex_lock(&instance_list_mutex
);
1036 list_remove(&inst
->link
);
1037 fibril_mutex_unlock(&instance_list_mutex
);
1039 fibril_mutex_unlock(&open_nodes_lock
);
1041 return ext4_filesystem_fini(inst
->filesystem
);
1044 /** Read bytes from node.
1046 * @param service_id Device to read data from
1047 * @param index Number of node to read from
1048 * @param pos Position where the read should be started
1049 * @param rbytes Output value, where the real size was returned
1051 * @return Error code
1054 static int ext4fs_read(service_id_t service_id
, fs_index_t index
, aoff64_t pos
,
1058 * Receive the read request.
1060 ipc_callid_t callid
;
1062 if (!async_data_read_receive(&callid
, &size
)) {
1063 async_answer_0(callid
, EINVAL
);
1067 ext4fs_instance_t
*inst
;
1068 int rc
= ext4fs_instance_get(service_id
, &inst
);
1070 async_answer_0(callid
, rc
);
1075 ext4_inode_ref_t
*inode_ref
;
1076 rc
= ext4_filesystem_get_inode_ref(inst
->filesystem
, index
, &inode_ref
);
1078 async_answer_0(callid
, rc
);
1082 /* Read from i-node by type */
1083 if (ext4_inode_is_type(inst
->filesystem
->superblock
, inode_ref
->inode
,
1084 EXT4_INODE_MODE_FILE
)) {
1085 rc
= ext4fs_read_file(callid
, pos
, size
, inst
, inode_ref
,
1087 } else if (ext4_inode_is_type(inst
->filesystem
->superblock
,
1088 inode_ref
->inode
, EXT4_INODE_MODE_DIRECTORY
)) {
1089 rc
= ext4fs_read_directory(callid
, pos
, size
, inst
, inode_ref
,
1092 /* Other inode types not supported */
1093 async_answer_0(callid
, ENOTSUP
);
1097 int const rc2
= ext4_filesystem_put_inode_ref(inode_ref
);
1099 return rc
== EOK
? rc2
: rc
;
1102 /** Check if filename is dot or dotdot (reserved names).
1104 * @param name Name to check
1105 * @param name_size Length of string name
1107 * @return Result of the check
1110 bool ext4fs_is_dots(const uint8_t *name
, size_t name_size
)
1112 if ((name_size
== 1) && (name
[0] == '.'))
1115 if ((name_size
== 2) && (name
[0] == '.') && (name
[1] == '.'))
1121 /** Read data from directory.
1123 * @param callid IPC id of call (for communication)
1124 * @param pos Position to start reading from
1125 * @param size How many bytes to read
1126 * @param inst Filesystem instance
1127 * @param inode_ref Node to read data from
1128 * @param rbytes Output value to return real number of bytes was read
1130 * @return Error code
1133 int ext4fs_read_directory(ipc_callid_t callid
, aoff64_t pos
, size_t size
,
1134 ext4fs_instance_t
*inst
, ext4_inode_ref_t
*inode_ref
, size_t *rbytes
)
1136 ext4_directory_iterator_t it
;
1137 int rc
= ext4_directory_iterator_init(&it
, inode_ref
, pos
);
1139 async_answer_0(callid
, rc
);
1144 * Find next interesting directory entry.
1145 * We want to skip . and .. entries
1146 * as these are not used in HelenOS
1149 while (it
.current
!= NULL
) {
1150 if (it
.current
->inode
== 0)
1153 uint16_t name_size
= ext4_directory_entry_ll_get_name_length(
1154 inst
->filesystem
->superblock
, it
.current
);
1157 if (ext4fs_is_dots(it
.current
->name
, name_size
))
1161 * The on-disk entry does not contain \0 at the end
1162 * end of entry name, so we copy it to new buffer
1163 * and add the \0 at the end
1165 uint8_t *buf
= malloc(name_size
+ 1);
1167 ext4_directory_iterator_fini(&it
);
1168 async_answer_0(callid
, ENOMEM
);
1172 memcpy(buf
, &it
.current
->name
, name_size
);
1173 *(buf
+ name_size
) = 0;
1176 (void) async_data_read_finalize(callid
, buf
, name_size
+ 1);
1181 rc
= ext4_directory_iterator_next(&it
);
1183 ext4_directory_iterator_fini(&it
);
1184 async_answer_0(callid
, rc
);
1191 rc
= ext4_directory_iterator_next(&it
);
1195 next
= it
.current_offset
;
1198 rc
= ext4_directory_iterator_fini(&it
);
1202 /* Prepare return values */
1204 *rbytes
= next
- pos
;
1207 async_answer_0(callid
, ENOENT
);
1212 /** Read data from file.
1214 * @param callid IPC id of call (for communication)
1215 * @param pos Position to start reading from
1216 * @param size How many bytes to read
1217 * @param inst Filesystem instance
1218 * @param inode_ref Node to read data from
1219 * @param rbytes Output value to return real number of bytes was read
1221 * @return Error code
1224 int ext4fs_read_file(ipc_callid_t callid
, aoff64_t pos
, size_t size
,
1225 ext4fs_instance_t
*inst
, ext4_inode_ref_t
*inode_ref
, size_t *rbytes
)
1227 ext4_superblock_t
*sb
= inst
->filesystem
->superblock
;
1228 uint64_t file_size
= ext4_inode_get_size(sb
, inode_ref
->inode
);
1230 if (pos
>= file_size
) {
1231 /* Read 0 bytes successfully */
1232 async_data_read_finalize(callid
, NULL
, 0);
1237 /* For now, we only read data from one block at a time */
1238 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
1239 aoff64_t file_block
= pos
/ block_size
;
1240 uint32_t offset_in_block
= pos
% block_size
;
1241 uint32_t bytes
= min(block_size
- offset_in_block
, size
);
1243 /* Handle end of file */
1244 if (pos
+ bytes
> file_size
)
1245 bytes
= file_size
- pos
;
1247 /* Get the real block number */
1249 int rc
= ext4_filesystem_get_inode_data_block_index(inode_ref
,
1250 file_block
, &fs_block
);
1252 async_answer_0(callid
, rc
);
1257 * Check for sparse file.
1258 * If ext4_filesystem_get_inode_data_block_index returned
1259 * fs_block == 0, it means that the given block is not allocated for the
1260 * file and we need to return a buffer of zeros
1263 if (fs_block
== 0) {
1264 buffer
= malloc(bytes
);
1265 if (buffer
== NULL
) {
1266 async_answer_0(callid
, ENOMEM
);
1270 memset(buffer
, 0, bytes
);
1272 rc
= async_data_read_finalize(callid
, buffer
, bytes
);
1279 /* Usual case - we need to read a block from device */
1281 rc
= block_get(&block
, inst
->service_id
, fs_block
, BLOCK_FLAGS_NONE
);
1283 async_answer_0(callid
, rc
);
1287 assert(offset_in_block
+ bytes
<= block_size
);
1288 rc
= async_data_read_finalize(callid
, block
->data
+ offset_in_block
, bytes
);
1294 rc
= block_put(block
);
1302 /** Write bytes to file
1304 * @param service_id Device identifier
1305 * @param index I-node number of file
1306 * @param pos Position in file to start reading from
1307 * @param wbytes Output value - real number of written bytes
1308 * @param nsize Output value - new size of i-node
1310 * @return Error code
1313 static int ext4fs_write(service_id_t service_id
, fs_index_t index
, aoff64_t pos
,
1314 size_t *wbytes
, aoff64_t
*nsize
)
1317 int rc
= ext4fs_node_get(&fn
, service_id
, index
);
1321 ipc_callid_t callid
;
1323 if (!async_data_write_receive(&callid
, &len
)) {
1325 async_answer_0(callid
, rc
);
1329 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
1330 ext4_filesystem_t
*fs
= enode
->instance
->filesystem
;
1332 uint32_t block_size
= ext4_superblock_get_block_size(fs
->superblock
);
1334 /* Prevent writing to more than one block */
1335 uint32_t bytes
= min(len
, block_size
- (pos
% block_size
));
1337 int flags
= BLOCK_FLAGS_NONE
;
1338 if (bytes
== block_size
)
1339 flags
= BLOCK_FLAGS_NOREAD
;
1341 uint32_t iblock
= pos
/ block_size
;
1345 ext4_inode_ref_t
*inode_ref
= enode
->inode_ref
;
1346 rc
= ext4_filesystem_get_inode_data_block_index(inode_ref
, iblock
,
1349 async_answer_0(callid
, rc
);
1353 /* Check for sparse file */
1355 if ((ext4_superblock_has_feature_incompatible(fs
->superblock
,
1356 EXT4_FEATURE_INCOMPAT_EXTENTS
)) &&
1357 (ext4_inode_has_flag(inode_ref
->inode
, EXT4_INODE_FLAG_EXTENTS
))) {
1358 uint32_t last_iblock
=
1359 ext4_inode_get_size(fs
->superblock
, inode_ref
->inode
) /
1362 while (last_iblock
< iblock
) {
1363 rc
= ext4_extent_append_block(inode_ref
, &last_iblock
,
1366 async_answer_0(callid
, rc
);
1371 rc
= ext4_extent_append_block(inode_ref
, &last_iblock
,
1374 async_answer_0(callid
, rc
);
1378 rc
= ext4_balloc_alloc_block(inode_ref
, &fblock
);
1380 async_answer_0(callid
, rc
);
1384 rc
= ext4_filesystem_set_inode_data_block_index(inode_ref
,
1387 ext4_balloc_free_block(inode_ref
, fblock
);
1388 async_answer_0(callid
, rc
);
1393 flags
= BLOCK_FLAGS_NOREAD
;
1394 inode_ref
->dirty
= true;
1397 /* Load target block */
1398 block_t
*write_block
;
1399 rc
= block_get(&write_block
, service_id
, fblock
, flags
);
1401 async_answer_0(callid
, rc
);
1405 if (flags
== BLOCK_FLAGS_NOREAD
)
1406 memset(write_block
->data
, 0, block_size
);
1408 rc
= async_data_write_finalize(callid
, write_block
->data
+
1409 (pos
% block_size
), bytes
);
1413 write_block
->dirty
= true;
1415 rc
= block_put(write_block
);
1419 /* Do some counting */
1420 uint32_t old_inode_size
= ext4_inode_get_size(fs
->superblock
,
1422 if (pos
+ bytes
> old_inode_size
) {
1423 ext4_inode_set_size(inode_ref
->inode
, pos
+ bytes
);
1424 inode_ref
->dirty
= true;
1427 *nsize
= ext4_inode_get_size(fs
->superblock
, inode_ref
->inode
);
1433 int const rc2
= ext4fs_node_put(fn
);
1434 return rc
== EOK
? rc2
: rc
;
1439 * Only the direction to shorter file is supported.
1441 * @param service_id Device identifier
1442 * @param index Index if node to truncated
1443 * @param new_size New size of file
1445 * @return Error code
1448 static int ext4fs_truncate(service_id_t service_id
, fs_index_t index
,
1452 int rc
= ext4fs_node_get(&fn
, service_id
, index
);
1456 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
1457 ext4_inode_ref_t
*inode_ref
= enode
->inode_ref
;
1459 rc
= ext4_filesystem_truncate_inode(inode_ref
, new_size
);
1460 int const rc2
= ext4fs_node_put(fn
);
1462 return rc
== EOK
? rc2
: rc
;
1467 * @param service_id Device identifier
1468 * @param index I-node number
1470 * @return Error code
1473 static int ext4fs_close(service_id_t service_id
, fs_index_t index
)
1478 /** Destroy node specified by index.
1480 * @param service_id Device identifier
1481 * @param index I-node to destroy
1483 * @return Error code
1486 static int ext4fs_destroy(service_id_t service_id
, fs_index_t index
)
1489 int rc
= ext4fs_node_get(&fn
, service_id
, index
);
1493 /* Destroy the inode */
1494 return ext4fs_destroy_node(fn
);
1497 /** Enforce inode synchronization (write) to device.
1499 * @param service_id Device identifier
1500 * @param index I-node number.
1503 static int ext4fs_sync(service_id_t service_id
, fs_index_t index
)
1506 int rc
= ext4fs_node_get(&fn
, service_id
, index
);
1510 ext4fs_node_t
*enode
= EXT4FS_NODE(fn
);
1511 enode
->inode_ref
->dirty
= true;
1513 return ext4fs_node_put(fn
);
1519 vfs_out_ops_t ext4fs_ops
= {
1520 .mounted
= ext4fs_mounted
,
1521 .unmounted
= ext4fs_unmounted
,
1522 .read
= ext4fs_read
,
1523 .write
= ext4fs_write
,
1524 .truncate
= ext4fs_truncate
,
1525 .close
= ext4fs_close
,
1526 .destroy
= ext4fs_destroy
,