source: mainline/uspace/srv/fs/ext4fs/ext4fs_ops.c@ fa4b7ba3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fa4b7ba3 was d1538a1, checked in by Martin Sucha <sucha14@…>, 13 years ago

Add missing copyright headers to ext4

Those files are based on ext2 filesystem driver code.

  • Property mode set to 100644
File size: 34.8 KB
RevLine 
[d3a9ae74]1/*
[d1538a1]2 * Copyright (c) 2011 Martin Sucha
[f22d5ef0]3 * Copyright (c) 2012 Frantisek Princ
[d3a9ae74]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup fs
31 * @{
[38542dc]32 */
[d3a9ae74]33/**
[38542dc]34 * @file ext4fs_ops.c
35 * @brief VFS operations for ext4 filesystem.
[d3a9ae74]36 */
37
38#include <errno.h>
[6c501f8]39#include <fibril_synch.h>
40#include <libext4.h>
[d3a9ae74]41#include <libfs.h>
[9b9d37bb]42#include <macros.h>
[6c501f8]43#include <malloc.h>
[3711e7e]44#include <adt/hash_table.h>
[062d900]45#include <adt/hash.h>
[d3a9ae74]46#include <ipc/loc.h>
47#include "ext4fs.h"
48#include "../../vfs/vfs.h"
49
[38542dc]50#define EXT4FS_NODE(node) \
51 ((node) ? (ext4fs_node_t *) (node)->data : NULL)
52
[1fff583]53/**
54 * Type for holding an instance of mounted partition.
55 */
[6c501f8]56typedef struct ext4fs_instance {
57 link_t link;
58 service_id_t service_id;
59 ext4_filesystem_t *filesystem;
60 unsigned int open_nodes_count;
61} ext4fs_instance_t;
62
[1fff583]63/**
64 * Type for wrapping common fs_node and add some useful pointers.
65 */
[6c501f8]66typedef struct ext4fs_node {
67 ext4fs_instance_t *instance;
68 ext4_inode_ref_t *inode_ref;
69 fs_node_t *fs_node;
[062d900]70 ht_link_t link;
[6c501f8]71 unsigned int references;
72} ext4fs_node_t;
73
[06d85e5]74/* Forward declarations of auxiliary functions */
[9b9d37bb]75
76static int ext4fs_read_directory(ipc_callid_t, aoff64_t, size_t,
77 ext4fs_instance_t *, ext4_inode_ref_t *, size_t *);
78static int ext4fs_read_file(ipc_callid_t, aoff64_t, size_t, ext4fs_instance_t *,
79 ext4_inode_ref_t *, size_t *);
80static bool ext4fs_is_dots(const uint8_t *, size_t);
[6c501f8]81static int ext4fs_instance_get(service_id_t, ext4fs_instance_t **);
82static int ext4fs_node_get_core(fs_node_t **, ext4fs_instance_t *, fs_index_t);
[9c0c0e1]83static int ext4fs_node_put_core(ext4fs_node_t *);
[d3a9ae74]84
[38542dc]85/* Forward declarations of ext4 libfs operations. */
[1fff583]86
[d3a9ae74]87static int ext4fs_root_get(fs_node_t **, service_id_t);
88static int ext4fs_match(fs_node_t **, fs_node_t *, const char *);
89static int ext4fs_node_get(fs_node_t **, service_id_t, fs_index_t);
90static int ext4fs_node_open(fs_node_t *);
91static int ext4fs_node_put(fs_node_t *);
92static int ext4fs_create_node(fs_node_t **, service_id_t, int);
93static int ext4fs_destroy_node(fs_node_t *);
94static int ext4fs_link(fs_node_t *, fs_node_t *, const char *);
95static int ext4fs_unlink(fs_node_t *, fs_node_t *, const char *);
96static int ext4fs_has_children(bool *, fs_node_t *);
97static fs_index_t ext4fs_index_get(fs_node_t *);
98static aoff64_t ext4fs_size_get(fs_node_t *);
99static unsigned ext4fs_lnkcnt_get(fs_node_t *);
100static bool ext4fs_is_directory(fs_node_t *);
101static bool ext4fs_is_file(fs_node_t *node);
102static service_id_t ext4fs_service_get(fs_node_t *node);
103
[06d85e5]104/* Static variables */
[1fff583]105
[6c501f8]106static LIST_INITIALIZE(instance_list);
107static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
[3711e7e]108static hash_table_t open_nodes;
[6c501f8]109static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
110
[3711e7e]111/* Hash table interface for open nodes hash table */
[062d900]112
113typedef struct {
114 service_id_t service_id;
115 fs_index_t index;
116} node_key_t;
117
118static size_t open_nodes_key_hash(void *key_arg)
[3711e7e]119{
[062d900]120 node_key_t *key = (node_key_t *)key_arg;
121 return hash_combine(key->service_id, key->index);
[3711e7e]122}
123
[062d900]124static size_t open_nodes_hash(const ht_link_t *item)
[3711e7e]125{
[062d900]126 ext4fs_node_t *enode = hash_table_get_inst(item, ext4fs_node_t, link);
127 return hash_combine(enode->instance->service_id, enode->inode_ref->index);
[3711e7e]128}
129
[062d900]130static bool open_nodes_key_equal(void *key_arg, const ht_link_t *item)
[3711e7e]131{
[062d900]132 node_key_t *key = (node_key_t *)key_arg;
133 ext4fs_node_t *enode = hash_table_get_inst(item, ext4fs_node_t, link);
134
135 return key->service_id == enode->instance->service_id
136 && key->index == enode->inode_ref->index;
[3711e7e]137}
138
[062d900]139static hash_table_ops_t open_nodes_ops = {
[3711e7e]140 .hash = open_nodes_hash,
[062d900]141 .key_hash = open_nodes_key_hash,
142 .key_equal = open_nodes_key_equal,
143 .equal = NULL,
144 .remove_callback = NULL,
[3711e7e]145};
146
[1fff583]147/** Basic initialization of the driver.
148 *
[38542dc]149 * This is only needed to create the hash table
150 * for storing open nodes.
151 *
152 * @return Error code
[1fff583]153 *
154 */
[d3a9ae74]155int ext4fs_global_init(void)
156{
[062d900]157 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops))
[3711e7e]158 return ENOMEM;
[38542dc]159
[d3a9ae74]160 return EOK;
161}
162
[1fff583]163/* Finalization of the driver.
164 *
[38542dc]165 * This is only needed to destroy the hash table.
[1fff583]166 *
[38542dc]167 * @return Error code
[1fff583]168 */
[d3a9ae74]169int ext4fs_global_fini(void)
170{
[3711e7e]171 hash_table_destroy(&open_nodes);
[d3a9ae74]172 return EOK;
173}
174
175/*
[38542dc]176 * Ext4 libfs operations.
[d3a9ae74]177 */
178
[1fff583]179/** Get instance from internal table by service_id.
180 *
[38542dc]181 * @param service_id Device identifier
182 * @param inst Output instance if successful operation
183 *
184 * @return Error code
185 *
[1fff583]186 */
[6c501f8]187int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
188{
189 fibril_mutex_lock(&instance_list_mutex);
[38542dc]190
[6c501f8]191 if (list_empty(&instance_list)) {
192 fibril_mutex_unlock(&instance_list_mutex);
193 return EINVAL;
194 }
[38542dc]195
[6c501f8]196 list_foreach(instance_list, link) {
[38542dc]197 ext4fs_instance_t *tmp =
198 list_get_instance(link, ext4fs_instance_t, link);
199
[6c501f8]200 if (tmp->service_id == service_id) {
201 *inst = tmp;
202 fibril_mutex_unlock(&instance_list_mutex);
203 return EOK;
204 }
205 }
[38542dc]206
[6c501f8]207 fibril_mutex_unlock(&instance_list_mutex);
208 return EINVAL;
209}
210
[1fff583]211/** Get root node of filesystem specified by service_id.
212 *
[38542dc]213 * @param rfn Output pointer to loaded node
214 * @param service_id Device to load root node from
215 *
216 * @return Error code
217 *
[1fff583]218 */
[d3a9ae74]219int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
220{
[01ab41b]221 return ext4fs_node_get(rfn, service_id, EXT4_INODE_ROOT_INDEX);
[d3a9ae74]222}
223
[1fff583]224/** Check if specified name (component) matches with any directory entry.
225 *
226 * If match is found, load and return matching node.
227 *
[38542dc]228 * @param rfn Output pointer to node if operation successful
229 * @param pfn Parent directory node
230 * @param component Name to check directory for
231 *
232 * @return Error code
233 *
[1fff583]234 */
[d3a9ae74]235int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
236{
[5614c7f]237 ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
238 ext4_filesystem_t *fs = eparent->instance->filesystem;
[38542dc]239
[9b9d37bb]240 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
[38542dc]241 EXT4_INODE_MODE_DIRECTORY))
[9b9d37bb]242 return ENOTDIR;
[38542dc]243
[06d85e5]244 /* Try to find entry */
[7689590]245 ext4_directory_search_result_t result;
[38542dc]246 int rc = ext4_directory_find_entry(&result, eparent->inode_ref,
247 component);
[8be96a0]248 if (rc != EOK) {
[ea75ceb]249 if (rc == ENOENT) {
250 *rfn = NULL;
251 return EOK;
252 }
[38542dc]253
[8be96a0]254 return rc;
[9b9d37bb]255 }
[38542dc]256
[06d85e5]257 /* Load node from search result */
[7689590]258 uint32_t inode = ext4_directory_entry_ll_get_inode(result.dentry);
[8be96a0]259 rc = ext4fs_node_get_core(rfn, eparent->instance, inode);
[38542dc]260 if (rc != EOK)
[8be96a0]261 return rc;
[38542dc]262
[06d85e5]263 /* Destroy search result structure */
[38542dc]264 return ext4_directory_destroy_result(&result);
[d3a9ae74]265}
266
[1fff583]267/** Get node specified by index
268 *
269 * It's wrapper for node_put_core operation
270 *
[38542dc]271 * @param rfn Output pointer to loaded node if operation successful
272 * @param service_id Device identifier
273 * @param index Node index (here i-node number)
274 *
275 * @return Error code
276 *
[1fff583]277 */
[d3a9ae74]278int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
279{
[5614c7f]280 ext4fs_instance_t *inst;
[38542dc]281 int rc = ext4fs_instance_get(service_id, &inst);
282 if (rc != EOK)
[9c0c0e1]283 return rc;
[38542dc]284
[9c0c0e1]285 return ext4fs_node_get_core(rfn, inst, index);
[d3a9ae74]286}
287
[1fff583]288/** Main function for getting node from the filesystem.
289 *
[38542dc]290 * @param rfn Output point to loaded node if operation successful
291 * @param inst Instance of filesystem
292 * @param index Index of node (i-node number)
293 *
294 * @return Error code
295 *
[1fff583]296 */
[6c501f8]297int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
[38542dc]298 fs_index_t index)
[6c501f8]299{
[3711e7e]300 fibril_mutex_lock(&open_nodes_lock);
[38542dc]301
[3711e7e]302 /* Check if the node is not already open */
[062d900]303 node_key_t key = {
304 .service_id = inst->service_id,
305 .index = index
[3711e7e]306 };
[38542dc]307
[062d900]308 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
[5614c7f]309 ext4fs_node_t *enode = NULL;
[3711e7e]310 if (already_open) {
[062d900]311 enode = hash_table_get_inst(already_open, ext4fs_node_t, link);
[3711e7e]312 *rfn = enode->fs_node;
313 enode->references++;
[38542dc]314
[3711e7e]315 fibril_mutex_unlock(&open_nodes_lock);
316 return EOK;
317 }
[38542dc]318
[06d85e5]319 /* Prepare new enode */
[3711e7e]320 enode = malloc(sizeof(ext4fs_node_t));
321 if (enode == NULL) {
322 fibril_mutex_unlock(&open_nodes_lock);
323 return ENOMEM;
324 }
[38542dc]325
[06d85e5]326 /* Prepare new fs_node and initialize */
[5614c7f]327 fs_node_t *fs_node = malloc(sizeof(fs_node_t));
328 if (fs_node == NULL) {
[3711e7e]329 free(enode);
330 fibril_mutex_unlock(&open_nodes_lock);
331 return ENOMEM;
332 }
[38542dc]333
[5614c7f]334 fs_node_initialize(fs_node);
[38542dc]335
[06d85e5]336 /* Load i-node from filesystem */
[5614c7f]337 ext4_inode_ref_t *inode_ref;
[38542dc]338 int rc = ext4_filesystem_get_inode_ref(inst->filesystem, index,
339 &inode_ref);
[3711e7e]340 if (rc != EOK) {
341 free(enode);
[5614c7f]342 free(fs_node);
[3711e7e]343 fibril_mutex_unlock(&open_nodes_lock);
344 return rc;
345 }
[38542dc]346
[06d85e5]347 /* Initialize enode */
[3711e7e]348 enode->inode_ref = inode_ref;
349 enode->instance = inst;
350 enode->references = 1;
[5614c7f]351 enode->fs_node = fs_node;
[38542dc]352
[5614c7f]353 fs_node->data = enode;
354 *rfn = fs_node;
[38542dc]355
[062d900]356 hash_table_insert(&open_nodes, &enode->link);
[3711e7e]357 inst->open_nodes_count++;
[38542dc]358
[3711e7e]359 fibril_mutex_unlock(&open_nodes_lock);
[38542dc]360
[6c501f8]361 return EOK;
362}
363
[1fff583]364/** Put previously loaded node.
365 *
[38542dc]366 * @param enode Node to put back
367 *
368 * @return Error code
369 *
[1fff583]370 */
[9b9d37bb]371int ext4fs_node_put_core(ext4fs_node_t *enode)
372{
[062d900]373 hash_table_remove_item(&open_nodes, &enode->link);
[9b9d37bb]374 assert(enode->instance->open_nodes_count > 0);
375 enode->instance->open_nodes_count--;
[38542dc]376
[06d85e5]377 /* Put inode back in filesystem */
[38542dc]378 int rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
379 if (rc != EOK)
[9b9d37bb]380 return rc;
[38542dc]381
[06d85e5]382 /* Destroy data structure */
[9b9d37bb]383 free(enode->fs_node);
384 free(enode);
[38542dc]385
[9c0c0e1]386 return EOK;
387}
388
[1fff583]389/** Open node.
390 *
391 * This operation is stateless in this driver.
392 *
[38542dc]393 * @param fn Node to open
394 *
395 * @return EOK
396 *
[1fff583]397 */
[d3a9ae74]398int ext4fs_node_open(fs_node_t *fn)
399{
[06d85e5]400 /* Stateless operation */
[d3a9ae74]401 return EOK;
402}
403
[1fff583]404/** Put previously loaded node.
405 *
[38542dc]406 * A wrapper for node_put_core operation
407 *
408 * @param fn Node to put back
409 * @return Error code
[1fff583]410 *
411 */
[d3a9ae74]412int ext4fs_node_put(fs_node_t *fn)
413{
[9c0c0e1]414 fibril_mutex_lock(&open_nodes_lock);
[38542dc]415
[5614c7f]416 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[9c0c0e1]417 assert(enode->references > 0);
418 enode->references--;
419 if (enode->references == 0) {
[38542dc]420 int rc = ext4fs_node_put_core(enode);
[9c0c0e1]421 if (rc != EOK) {
422 fibril_mutex_unlock(&open_nodes_lock);
423 return rc;
424 }
425 }
[38542dc]426
[9c0c0e1]427 fibril_mutex_unlock(&open_nodes_lock);
[38542dc]428
[d3a9ae74]429 return EOK;
430}
431
[1fff583]432/** Create new node in filesystem.
433 *
[38542dc]434 * @param rfn Output pointer to newly created node if successful
435 * @param service_id Device identifier, where the filesystem is
436 * @param flags Flags for specification of new node parameters
437 *
438 * @return Error code
439 *
[1fff583]440 */
[d3a9ae74]441int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
442{
[06d85e5]443 /* Allocate enode */
[47a89fe]444 ext4fs_node_t *enode;
445 enode = malloc(sizeof(ext4fs_node_t));
[38542dc]446 if (enode == NULL)
[47a89fe]447 return ENOMEM;
[38542dc]448
[06d85e5]449 /* Allocate fs_node */
[47a89fe]450 fs_node_t *fs_node;
451 fs_node = malloc(sizeof(fs_node_t));
452 if (fs_node == NULL) {
453 free(enode);
454 return ENOMEM;
455 }
[38542dc]456
[06d85e5]457 /* Load instance */
[47a89fe]458 ext4fs_instance_t *inst;
[38542dc]459 int rc = ext4fs_instance_get(service_id, &inst);
[47a89fe]460 if (rc != EOK) {
461 free(enode);
462 free(fs_node);
463 return rc;
464 }
[38542dc]465
[06d85e5]466 /* Allocate new i-node in filesystem */
[47a89fe]467 ext4_inode_ref_t *inode_ref;
[304faab]468 rc = ext4_filesystem_alloc_inode(inst->filesystem, &inode_ref, flags);
[47a89fe]469 if (rc != EOK) {
470 free(enode);
471 free(fs_node);
472 return rc;
473 }
[38542dc]474
[06d85e5]475 /* Do some interconnections in references */
[47a89fe]476 enode->inode_ref = inode_ref;
477 enode->instance = inst;
478 enode->references = 1;
[38542dc]479
[47a89fe]480 fibril_mutex_lock(&open_nodes_lock);
[062d900]481 hash_table_insert(&open_nodes, &enode->link);
[47a89fe]482 fibril_mutex_unlock(&open_nodes_lock);
483 inst->open_nodes_count++;
[38542dc]484
[47a89fe]485 enode->inode_ref->dirty = true;
[38542dc]486
[47a89fe]487 fs_node_initialize(fs_node);
488 fs_node->data = enode;
489 enode->fs_node = fs_node;
490 *rfn = fs_node;
[38542dc]491
[47a89fe]492 return EOK;
[d3a9ae74]493}
494
[1fff583]495/** Destroy existing node.
496 *
[38542dc]497 * @param fs Node to destroy
498 *
499 * @return Error code
500 *
[1fff583]501 */
[d3a9ae74]502int ext4fs_destroy_node(fs_node_t *fn)
503{
[06d85e5]504 /* If directory, check for children */
[3d4fd2c]505 bool has_children;
[38542dc]506 int rc = ext4fs_has_children(&has_children, fn);
[3d4fd2c]507 if (rc != EOK) {
508 ext4fs_node_put(fn);
509 return rc;
510 }
[38542dc]511
[3d4fd2c]512 if (has_children) {
513 ext4fs_node_put(fn);
514 return EINVAL;
515 }
[38542dc]516
[3d4fd2c]517 ext4fs_node_t *enode = EXT4FS_NODE(fn);
518 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[38542dc]519
[06d85e5]520 /* Release data blocks */
[1ac1ab4]521 rc = ext4_filesystem_truncate_inode(inode_ref, 0);
[3d4fd2c]522 if (rc != EOK) {
523 ext4fs_node_put(fn);
524 return rc;
525 }
[38542dc]526
527 /*
528 * TODO: Sset real deletion time when it will be supported.
529 * Temporary set fake deletion time.
530 */
[ca47f656]531 ext4_inode_set_deletion_time(inode_ref->inode, 0xdeadbeef);
[07fd4cd1]532 inode_ref->dirty = true;
[38542dc]533
[06d85e5]534 /* Free inode */
[1ac1ab4]535 rc = ext4_filesystem_free_inode(inode_ref);
[3d4fd2c]536 if (rc != EOK) {
537 ext4fs_node_put(fn);
538 return rc;
539 }
[38542dc]540
[ca47f656]541 return ext4fs_node_put(fn);
[d3a9ae74]542}
543
[1fff583]544/** Link the specfied node to directory.
545 *
[38542dc]546 * @param pfn Parent node to link in
547 * @param cfn Node to be linked
548 * @param name Name which will be assigned to directory entry
549 *
550 * @return Error code
551 *
[1fff583]552 */
[d3a9ae74]553int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
554{
[06d85e5]555 /* Check maximum name length */
[38542dc]556 if (str_size(name) > EXT4_DIRECTORY_FILENAME_LEN)
[47a89fe]557 return ENAMETOOLONG;
[38542dc]558
[47a89fe]559 ext4fs_node_t *parent = EXT4FS_NODE(pfn);
560 ext4fs_node_t *child = EXT4FS_NODE(cfn);
561 ext4_filesystem_t *fs = parent->instance->filesystem;
[38542dc]562
[06d85e5]563 /* Add entry to parent directory */
[38542dc]564 int rc = ext4_directory_add_entry(parent->inode_ref, name,
565 child->inode_ref);
566 if (rc != EOK)
[47a89fe]567 return rc;
[38542dc]568
[06d85e5]569 /* Fill new dir -> add '.' and '..' entries */
[38542dc]570 if (ext4_inode_is_type(fs->superblock, child->inode_ref->inode,
571 EXT4_INODE_MODE_DIRECTORY)) {
572 rc = ext4_directory_add_entry(child->inode_ref, ".",
573 child->inode_ref);
[47a89fe]574 if (rc != EOK) {
[1ac1ab4]575 ext4_directory_remove_entry(parent->inode_ref, name);
[47a89fe]576 return rc;
577 }
[38542dc]578
579 rc = ext4_directory_add_entry(child->inode_ref, "..",
580 parent->inode_ref);
[47a89fe]581 if (rc != EOK) {
[1ac1ab4]582 ext4_directory_remove_entry(parent->inode_ref, name);
583 ext4_directory_remove_entry(child->inode_ref, ".");
[47a89fe]584 return rc;
585 }
[38542dc]586
[06d85e5]587 /* Initialize directory index if supported */
[38542dc]588 if (ext4_superblock_has_feature_compatible(fs->superblock,
589 EXT4_FEATURE_COMPAT_DIR_INDEX)) {
[b6d7b7c]590 rc = ext4_directory_dx_init(child->inode_ref);
[38542dc]591 if (rc != EOK)
[b6d7b7c]592 return rc;
[38542dc]593
594 ext4_inode_set_flag(child->inode_ref->inode,
595 EXT4_INODE_FLAG_INDEX);
[b6d7b7c]596 child->inode_ref->dirty = true;
597 }
[38542dc]598
599 uint16_t parent_links =
600 ext4_inode_get_links_count(parent->inode_ref->inode);
[47a89fe]601 parent_links++;
602 ext4_inode_set_links_count(parent->inode_ref->inode, parent_links);
[38542dc]603
[47a89fe]604 parent->inode_ref->dirty = true;
605 }
[38542dc]606
607 uint16_t child_links =
608 ext4_inode_get_links_count(child->inode_ref->inode);
[47a89fe]609 child_links++;
610 ext4_inode_set_links_count(child->inode_ref->inode, child_links);
[38542dc]611
[47a89fe]612 child->inode_ref->dirty = true;
[38542dc]613
[47a89fe]614 return EOK;
[d3a9ae74]615}
616
[1fff583]617/** Unlink node from specified directory.
618 *
[38542dc]619 * @param pfn Parent node to delete node from
620 * @param cfn Child node to be unlinked from directory
621 * @param name Name of entry that will be removed
622 *
623 * @return Error code
624 *
[1fff583]625 */
[ebeaaa06]626int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[d3a9ae74]627{
[ebeaaa06]628 bool has_children;
[38542dc]629 int rc = ext4fs_has_children(&has_children, cfn);
630 if (rc != EOK)
[ebeaaa06]631 return rc;
[38542dc]632
[06d85e5]633 /* Cannot unlink non-empty node */
[38542dc]634 if (has_children)
[ebeaaa06]635 return ENOTEMPTY;
[38542dc]636
[06d85e5]637 /* Remove entry from parent directory */
[f49638e]638 ext4_inode_ref_t *parent = EXT4FS_NODE(pfn)->inode_ref;
[1ac1ab4]639 rc = ext4_directory_remove_entry(parent, name);
[38542dc]640 if (rc != EOK)
[f49638e]641 return rc;
[38542dc]642
[06d85e5]643 /* Decrement links count */
[38542dc]644 ext4_inode_ref_t *child_inode_ref = EXT4FS_NODE(cfn)->inode_ref;
645
646 uint32_t lnk_count =
647 ext4_inode_get_links_count(child_inode_ref->inode);
[ebeaaa06]648 lnk_count--;
[38542dc]649
[06d85e5]650 /* If directory - handle links from parent */
[38542dc]651 if ((lnk_count <= 1) && (ext4fs_is_directory(cfn))) {
[1e48a07e]652 assert(lnk_count == 1);
[38542dc]653
[1e48a07e]654 lnk_count--;
[38542dc]655
[82d7816]656 ext4_inode_ref_t *parent_inode_ref = EXT4FS_NODE(pfn)->inode_ref;
[38542dc]657
[82d7816]658 uint32_t parent_lnk_count = ext4_inode_get_links_count(
[38542dc]659 parent_inode_ref->inode);
660
[82d7816]661 parent_lnk_count--;
662 ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count);
[38542dc]663
[07fd4cd1]664 parent->dirty = true;
665 }
666
[38542dc]667 /*
668 * TODO: Update timestamps of the parent
669 * (when we have wall-clock time).
670 *
671 * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
672 * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
673 * parent->dirty = true;
674 */
675
676 /*
677 * TODO: Update timestamp for inode.
678 *
679 * ext4_inode_set_change_inode_time(child_inode_ref->inode,
680 * (uint32_t) now);
681 */
682
[82d7816]683 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
684 child_inode_ref->dirty = true;
[38542dc]685
[ebeaaa06]686 return EOK;
[d3a9ae74]687}
688
[1fff583]689/** Check if specified node has children.
[38542dc]690 *
[1fff583]691 * For files is response allways false and check is executed only for directories.
692 *
[38542dc]693 * @param has_children Output value for response
694 * @param fn Node to check
695 *
696 * @return Error code
697 *
[1fff583]698 */
[d3a9ae74]699int ext4fs_has_children(bool *has_children, fs_node_t *fn)
700{
[5614c7f]701 ext4fs_node_t *enode = EXT4FS_NODE(fn);
702 ext4_filesystem_t *fs = enode->instance->filesystem;
[38542dc]703
[06d85e5]704 /* Check if node is directory */
[e68c834]705 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
706 EXT4_INODE_MODE_DIRECTORY)) {
707 *has_children = false;
708 return EOK;
709 }
[38542dc]710
[5614c7f]711 ext4_directory_iterator_t it;
[38542dc]712 int rc = ext4_directory_iterator_init(&it, enode->inode_ref, 0);
713 if (rc != EOK)
[e68c834]714 return rc;
[38542dc]715
[e68c834]716 /* Find a non-empty directory entry */
[5614c7f]717 bool found = false;
[e68c834]718 while (it.current != NULL) {
719 if (it.current->inode != 0) {
[38542dc]720 uint16_t name_size =
721 ext4_directory_entry_ll_get_name_length(fs->superblock,
722 it.current);
[2ea6392]723 if (!ext4fs_is_dots(it.current->name, name_size)) {
[e68c834]724 found = true;
725 break;
726 }
727 }
[38542dc]728
[e68c834]729 rc = ext4_directory_iterator_next(&it);
730 if (rc != EOK) {
731 ext4_directory_iterator_fini(&it);
732 return rc;
733 }
734 }
[38542dc]735
[e68c834]736 rc = ext4_directory_iterator_fini(&it);
[38542dc]737 if (rc != EOK)
[e68c834]738 return rc;
[38542dc]739
[e68c834]740 *has_children = found;
[38542dc]741
[d3a9ae74]742 return EOK;
743}
744
[1fff583]745/** Unpack index number from node.
[38542dc]746 *
747 * @param fn Node to load index from
748 *
749 * @return Index number of i-node
750 *
[1fff583]751 */
[d3a9ae74]752fs_index_t ext4fs_index_get(fs_node_t *fn)
753{
[9b9d37bb]754 ext4fs_node_t *enode = EXT4FS_NODE(fn);
755 return enode->inode_ref->index;
[d3a9ae74]756}
757
[1fff583]758/** Get real size of file / directory.
759 *
[38542dc]760 * @param fn Node to get size of
761 *
762 * @return Real size of node
763 *
[1fff583]764 */
[d3a9ae74]765aoff64_t ext4fs_size_get(fs_node_t *fn)
766{
[9b9d37bb]767 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]768 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
769 return ext4_inode_get_size(sb, enode->inode_ref->inode);
[d3a9ae74]770}
771
[1fff583]772/** Get number of links to specified node.
773 *
[38542dc]774 * @param fn Node to get links to
775 *
776 * @return Number of links
777 *
[1fff583]778 */
[d3a9ae74]779unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
780{
[d4d2954]781 ext4fs_node_t *enode = EXT4FS_NODE(fn);
782 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
[38542dc]783
[e5f8762]784 if (ext4fs_is_directory(fn)) {
[38542dc]785 if (lnkcnt > 1)
[e5f8762]786 return 1;
[38542dc]787 else
[e5f8762]788 return 0;
789 }
[38542dc]790
[06d85e5]791 /* For regular files return real links count */
[d4d2954]792 return lnkcnt;
[d3a9ae74]793}
794
[1fff583]795/** Check if node is directory.
796 *
[38542dc]797 * @param fn Node to check
798 *
799 * @return Result of check
800 *
[1fff583]801 */
[d3a9ae74]802bool ext4fs_is_directory(fs_node_t *fn)
803{
[e68c834]804 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]805 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
[38542dc]806
807 return ext4_inode_is_type(sb, enode->inode_ref->inode,
808 EXT4_INODE_MODE_DIRECTORY);
[d3a9ae74]809}
810
[1fff583]811/** Check if node is regular file.
812 *
[38542dc]813 * @param fn Node to check
814 *
815 * @return Result of check
816 *
[1fff583]817 */
[d3a9ae74]818bool ext4fs_is_file(fs_node_t *fn)
819{
[9b9d37bb]820 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]821 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
[38542dc]822
823 return ext4_inode_is_type(sb, enode->inode_ref->inode,
824 EXT4_INODE_MODE_FILE);
[d3a9ae74]825}
826
[1fff583]827/** Extract device identifier from node.
828 *
[38542dc]829 * @param node Node to extract id from
830 *
831 * @return id of device, where is the filesystem
832 *
[1fff583]833 */
[d3a9ae74]834service_id_t ext4fs_service_get(fs_node_t *fn)
835{
[e68c834]836 ext4fs_node_t *enode = EXT4FS_NODE(fn);
837 return enode->instance->service_id;
[d3a9ae74]838}
839
840/*
841 * libfs operations.
842 */
843libfs_ops_t ext4fs_libfs_ops = {
844 .root_get = ext4fs_root_get,
845 .match = ext4fs_match,
846 .node_get = ext4fs_node_get,
847 .node_open = ext4fs_node_open,
848 .node_put = ext4fs_node_put,
849 .create = ext4fs_create_node,
850 .destroy = ext4fs_destroy_node,
851 .link = ext4fs_link,
852 .unlink = ext4fs_unlink,
853 .has_children = ext4fs_has_children,
854 .index_get = ext4fs_index_get,
855 .size_get = ext4fs_size_get,
856 .lnkcnt_get = ext4fs_lnkcnt_get,
857 .is_directory = ext4fs_is_directory,
858 .is_file = ext4fs_is_file,
859 .service_get = ext4fs_service_get
860};
861
862/*
863 * VFS operations.
864 */
865
[38542dc]866/** Mount operation.
[1fff583]867 *
868 * Try to mount specified filesystem from device.
[38542dc]869 *
870 * @param service_id Identifier of device
871 * @param opts Mount options
872 * @param index Output value - index of root node
873 * @param size Output value - size of root node
874 * @param lnkcnt Output value - link count of root node
875 *
876 * @return Error code
877 *
[1fff583]878 */
[d3a9ae74]879static int ext4fs_mounted(service_id_t service_id, const char *opts,
[38542dc]880 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
[d3a9ae74]881{
[6c501f8]882 /* Allocate libext4 filesystem structure */
[38542dc]883 ext4_filesystem_t *fs = (ext4_filesystem_t *)
884 malloc(sizeof(ext4_filesystem_t));
885 if (fs == NULL)
[6c501f8]886 return ENOMEM;
[38542dc]887
[6c501f8]888 /* Allocate instance structure */
[38542dc]889 ext4fs_instance_t *inst = (ext4fs_instance_t *)
890 malloc(sizeof(ext4fs_instance_t));
[6c501f8]891 if (inst == NULL) {
892 free(fs);
893 return ENOMEM;
894 }
[38542dc]895
[0b293a6]896 enum cache_mode cmode;
[38542dc]897 if (str_cmp(opts, "wtcache") == 0)
[0b293a6]898 cmode = CACHE_MODE_WT;
[38542dc]899 else
[0b293a6]900 cmode = CACHE_MODE_WB;
[38542dc]901
[9c0c0e1]902 /* Initialize the filesystem */
[38542dc]903 int rc = ext4_filesystem_init(fs, service_id, cmode);
[6c501f8]904 if (rc != EOK) {
905 free(fs);
906 free(inst);
907 return rc;
908 }
[38542dc]909
[6c501f8]910 /* Do some sanity checking */
911 rc = ext4_filesystem_check_sanity(fs);
912 if (rc != EOK) {
[fb04cd90]913 ext4_filesystem_fini(fs);
[6c501f8]914 free(fs);
915 free(inst);
916 return rc;
917 }
[38542dc]918
[6c501f8]919 /* Check flags */
[5614c7f]920 bool read_only;
[9c0c0e1]921 rc = ext4_filesystem_check_features(fs, &read_only);
[6c501f8]922 if (rc != EOK) {
[fb04cd90]923 ext4_filesystem_fini(fs);
[6c501f8]924 free(fs);
925 free(inst);
926 return rc;
927 }
[38542dc]928
[6c501f8]929 /* Initialize instance */
930 link_initialize(&inst->link);
931 inst->service_id = service_id;
932 inst->filesystem = fs;
933 inst->open_nodes_count = 0;
[38542dc]934
[6c501f8]935 /* Read root node */
936 fs_node_t *root_node;
[3711e7e]937 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
[6c501f8]938 if (rc != EOK) {
[fb04cd90]939 ext4_filesystem_fini(fs);
[6c501f8]940 free(fs);
941 free(inst);
942 return rc;
943 }
[38542dc]944
[6c501f8]945 /* Add instance to the list */
946 fibril_mutex_lock(&instance_list_mutex);
947 list_append(&inst->link, &instance_list);
948 fibril_mutex_unlock(&instance_list_mutex);
[38542dc]949
[1df3f57a]950 ext4fs_node_t *enode = EXT4FS_NODE(root_node);
[38542dc]951
[6c501f8]952 *index = EXT4_INODE_ROOT_INDEX;
[1df3f57a]953 *size = ext4_inode_get_size(fs->superblock, enode->inode_ref->inode);
[e5f8762]954 *lnkcnt = 1;
[38542dc]955
[6c501f8]956 ext4fs_node_put(root_node);
[38542dc]957
[d3a9ae74]958 return EOK;
959}
960
[1fff583]961/** Unmount operation.
962 *
963 * Correctly release the filesystem.
964 *
[38542dc]965 * @param service_id Device to be unmounted
966 *
967 * @return Error code
968 *
[1fff583]969 */
[d3a9ae74]970static int ext4fs_unmounted(service_id_t service_id)
971{
[5614c7f]972 ext4fs_instance_t *inst;
[38542dc]973 int rc = ext4fs_instance_get(service_id, &inst);
974 if (rc != EOK)
[6c501f8]975 return rc;
[38542dc]976
[6c501f8]977 fibril_mutex_lock(&open_nodes_lock);
[38542dc]978
[6c501f8]979 if (inst->open_nodes_count != 0) {
980 fibril_mutex_unlock(&open_nodes_lock);
981 return EBUSY;
982 }
[38542dc]983
[6c501f8]984 /* Remove the instance from the list */
985 fibril_mutex_lock(&instance_list_mutex);
986 list_remove(&inst->link);
987 fibril_mutex_unlock(&instance_list_mutex);
[38542dc]988
[6c501f8]989 fibril_mutex_unlock(&open_nodes_lock);
[38542dc]990
[fb04cd90]991 return ext4_filesystem_fini(inst->filesystem);
[d3a9ae74]992}
993
[1fff583]994/** Read bytes from node.
995 *
[38542dc]996 * @param service_id Device to read data from
997 * @param index Number of node to read from
998 * @param pos Position where the read should be started
999 * @param rbytes Output value, where the real size was returned
1000 *
1001 * @return Error code
1002 *
[1fff583]1003 */
[38542dc]1004static int ext4fs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
1005 size_t *rbytes)
[d3a9ae74]1006{
[9b9d37bb]1007 /*
1008 * Receive the read request.
1009 */
1010 ipc_callid_t callid;
1011 size_t size;
1012 if (!async_data_read_receive(&callid, &size)) {
1013 async_answer_0(callid, EINVAL);
1014 return EINVAL;
1015 }
[38542dc]1016
[5614c7f]1017 ext4fs_instance_t *inst;
[38542dc]1018 int rc = ext4fs_instance_get(service_id, &inst);
[9b9d37bb]1019 if (rc != EOK) {
1020 async_answer_0(callid, rc);
1021 return rc;
1022 }
[38542dc]1023
[06d85e5]1024 /* Load i-node */
[5614c7f]1025 ext4_inode_ref_t *inode_ref;
[9b9d37bb]1026 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
1027 if (rc != EOK) {
1028 async_answer_0(callid, rc);
1029 return rc;
1030 }
[38542dc]1031
[06d85e5]1032 /* Read from i-node by type */
[9b9d37bb]1033 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
[38542dc]1034 EXT4_INODE_MODE_FILE)) {
[9b9d37bb]1035 rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
[38542dc]1036 rbytes);
[9b9d37bb]1037 } else if (ext4_inode_is_type(inst->filesystem->superblock,
[38542dc]1038 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
[9b9d37bb]1039 rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
[38542dc]1040 rbytes);
[9b9d37bb]1041 } else {
1042 /* Other inode types not supported */
1043 async_answer_0(callid, ENOTSUP);
1044 rc = ENOTSUP;
1045 }
[38542dc]1046
[9b9d37bb]1047 ext4_filesystem_put_inode_ref(inode_ref);
[38542dc]1048
[9b9d37bb]1049 return rc;
1050}
1051
[1fff583]1052/** Check if filename is dot or dotdot (reserved names).
1053 *
[38542dc]1054 * @param name Name to check
1055 * @param name_size Length of string name
1056 *
1057 * @return Result of the check
1058 *
[1fff583]1059 */
[8958a26]1060bool ext4fs_is_dots(const uint8_t *name, size_t name_size)
1061{
[38542dc]1062 if ((name_size == 1) && (name[0] == '.'))
[9b9d37bb]1063 return true;
[38542dc]1064
1065 if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
[9b9d37bb]1066 return true;
[38542dc]1067
[9b9d37bb]1068 return false;
1069}
1070
[1fff583]1071/** Read data from directory.
1072 *
[38542dc]1073 * @param callid IPC id of call (for communication)
1074 * @param pos Position to start reading from
1075 * @param size How many bytes to read
1076 * @param inst Filesystem instance
1077 * @param inode_ref Node to read data from
1078 * @param rbytes Output value to return real number of bytes was read
1079 *
1080 * @return Error code
1081 *
[1fff583]1082 */
[9b9d37bb]1083int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
1084 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
1085{
[5614c7f]1086 ext4_directory_iterator_t it;
[38542dc]1087 int rc = ext4_directory_iterator_init(&it, inode_ref, pos);
[9b9d37bb]1088 if (rc != EOK) {
1089 async_answer_0(callid, rc);
1090 return rc;
1091 }
[38542dc]1092
1093 /*
1094 * Find next interesting directory entry.
[9b9d37bb]1095 * We want to skip . and .. entries
1096 * as these are not used in HelenOS
1097 */
[5614c7f]1098 bool found = false;
[9b9d37bb]1099 while (it.current != NULL) {
[38542dc]1100 if (it.current->inode == 0)
[9b9d37bb]1101 goto skip;
[38542dc]1102
[5614c7f]1103 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
[9b9d37bb]1104 inst->filesystem->superblock, it.current);
[38542dc]1105
1106 /* Skip . and .. */
1107 if (ext4fs_is_dots(it.current->name, name_size))
[9b9d37bb]1108 goto skip;
[38542dc]1109
1110 /*
1111 * The on-disk entry does not contain \0 at the end
[9b9d37bb]1112 * end of entry name, so we copy it to new buffer
1113 * and add the \0 at the end
1114 */
[38542dc]1115 uint8_t *buf = malloc(name_size + 1);
[9b9d37bb]1116 if (buf == NULL) {
1117 ext4_directory_iterator_fini(&it);
1118 async_answer_0(callid, ENOMEM);
1119 return ENOMEM;
1120 }
[38542dc]1121
[9b9d37bb]1122 memcpy(buf, &it.current->name, name_size);
1123 *(buf + name_size) = 0;
1124 found = true;
[38542dc]1125
[9b9d37bb]1126 (void) async_data_read_finalize(callid, buf, name_size + 1);
1127 free(buf);
1128 break;
[38542dc]1129
[9b9d37bb]1130skip:
1131 rc = ext4_directory_iterator_next(&it);
1132 if (rc != EOK) {
1133 ext4_directory_iterator_fini(&it);
1134 async_answer_0(callid, rc);
1135 return rc;
1136 }
1137 }
[38542dc]1138
[5614c7f]1139 uint64_t next;
[9b9d37bb]1140 if (found) {
1141 rc = ext4_directory_iterator_next(&it);
[38542dc]1142 if (rc != EOK)
[9b9d37bb]1143 return rc;
[38542dc]1144
[9b9d37bb]1145 next = it.current_offset;
1146 }
[38542dc]1147
[9b9d37bb]1148 rc = ext4_directory_iterator_fini(&it);
[38542dc]1149 if (rc != EOK)
[9b9d37bb]1150 return rc;
[38542dc]1151
[06d85e5]1152 /* Prepare return values */
[9b9d37bb]1153 if (found) {
1154 *rbytes = next - pos;
1155 return EOK;
1156 } else {
1157 async_answer_0(callid, ENOENT);
1158 return ENOENT;
1159 }
[d3a9ae74]1160}
1161
[1fff583]1162/** Read data from file.
1163 *
[38542dc]1164 * @param callid IPC id of call (for communication)
1165 * @param pos Position to start reading from
1166 * @param size How many bytes to read
1167 * @param inst Filesystem instance
1168 * @param inode_ref Node to read data from
1169 * @param rbytes Output value to return real number of bytes was read
1170 *
1171 * @return Error code
1172 *
[1fff583]1173 */
[9b9d37bb]1174int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
1175 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
1176{
[5614c7f]1177 ext4_superblock_t *sb = inst->filesystem->superblock;
1178 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
[38542dc]1179
[9b9d37bb]1180 if (pos >= file_size) {
1181 /* Read 0 bytes successfully */
1182 async_data_read_finalize(callid, NULL, 0);
1183 *rbytes = 0;
1184 return EOK;
1185 }
[38542dc]1186
[9b9d37bb]1187 /* For now, we only read data from one block at a time */
[5614c7f]1188 uint32_t block_size = ext4_superblock_get_block_size(sb);
1189 aoff64_t file_block = pos / block_size;
1190 uint32_t offset_in_block = pos % block_size;
1191 uint32_t bytes = min(block_size - offset_in_block, size);
[38542dc]1192
[9b9d37bb]1193 /* Handle end of file */
[38542dc]1194 if (pos + bytes > file_size)
[9b9d37bb]1195 bytes = file_size - pos;
[38542dc]1196
[9b9d37bb]1197 /* Get the real block number */
[5614c7f]1198 uint32_t fs_block;
[38542dc]1199 int rc = ext4_filesystem_get_inode_data_block_index(inode_ref,
1200 file_block, &fs_block);
[9b9d37bb]1201 if (rc != EOK) {
1202 async_answer_0(callid, rc);
1203 return rc;
1204 }
[38542dc]1205
1206 /*
1207 * Check for sparse file.
[2b9e142]1208 * If ext4_filesystem_get_inode_data_block_index returned
[9b9d37bb]1209 * fs_block == 0, it means that the given block is not allocated for the
1210 * file and we need to return a buffer of zeros
1211 */
[5614c7f]1212 uint8_t *buffer;
[9b9d37bb]1213 if (fs_block == 0) {
1214 buffer = malloc(bytes);
1215 if (buffer == NULL) {
1216 async_answer_0(callid, ENOMEM);
1217 return ENOMEM;
1218 }
[38542dc]1219
[9b9d37bb]1220 memset(buffer, 0, bytes);
[38542dc]1221
[9b9d37bb]1222 async_data_read_finalize(callid, buffer, bytes);
1223 *rbytes = bytes;
[38542dc]1224
[9b9d37bb]1225 free(buffer);
1226 return EOK;
1227 }
[38542dc]1228
[9b9d37bb]1229 /* Usual case - we need to read a block from device */
[5614c7f]1230 block_t *block;
[9b9d37bb]1231 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1232 if (rc != EOK) {
1233 async_answer_0(callid, rc);
1234 return rc;
1235 }
[38542dc]1236
[9b9d37bb]1237 assert(offset_in_block + bytes <= block_size);
1238 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
[38542dc]1239
[9b9d37bb]1240 rc = block_put(block);
[38542dc]1241 if (rc != EOK)
[9b9d37bb]1242 return rc;
[38542dc]1243
[9b9d37bb]1244 *rbytes = bytes;
1245 return EOK;
1246}
[3711e7e]1247
[1fff583]1248/** Write bytes to file
1249 *
[38542dc]1250 * @param service_id Device identifier
1251 * @param index I-node number of file
1252 * @param pos Position in file to start reading from
1253 * @param wbytes Output value - real number of written bytes
1254 * @param nsize Output value - new size of i-node
1255 *
1256 * @return Error code
1257 *
[1fff583]1258 */
[38542dc]1259static int ext4fs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
1260 size_t *wbytes, aoff64_t *nsize)
[d3a9ae74]1261{
[5614c7f]1262 fs_node_t *fn;
[38542dc]1263 int rc = ext4fs_node_get(&fn, service_id, index);
1264 if (rc != EOK)
[1c1c736]1265 return rc;
[38542dc]1266
[5614c7f]1267 ipc_callid_t callid;
1268 size_t len;
[1c1c736]1269 if (!async_data_write_receive(&callid, &len)) {
1270 ext4fs_node_put(fn);
[38542dc]1271 async_answer_0(callid, EINVAL);
1272 return EINVAL;
[1c1c736]1273 }
[38542dc]1274
[5614c7f]1275 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1276 ext4_filesystem_t *fs = enode->instance->filesystem;
[38542dc]1277
[5614c7f]1278 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[38542dc]1279
[06d85e5]1280 /* Prevent writing to more than one block */
[5614c7f]1281 uint32_t bytes = min(len, block_size - (pos % block_size));
[38542dc]1282
[5614c7f]1283 int flags = BLOCK_FLAGS_NONE;
[38542dc]1284 if (bytes == block_size)
[35f48f2]1285 flags = BLOCK_FLAGS_NOREAD;
[38542dc]1286
[5614c7f]1287 uint32_t iblock = pos / block_size;
1288 uint32_t fblock;
[38542dc]1289
[06d85e5]1290 /* Load inode */
[5614c7f]1291 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[38542dc]1292 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, iblock,
1293 &fblock);
[6088193]1294 if (rc != EOK) {
1295 ext4fs_node_put(fn);
[e63ce679]1296 async_answer_0(callid, rc);
[6088193]1297 return rc;
1298 }
[38542dc]1299
[06d85e5]1300 /* Check for sparse file */
[1c1c736]1301 if (fblock == 0) {
[38542dc]1302 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1303 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1304 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1305 uint32_t last_iblock =
1306 ext4_inode_get_size(fs->superblock, inode_ref->inode) /
1307 block_size;
1308
[d510ac01]1309 while (last_iblock < iblock) {
[38542dc]1310 rc = ext4_extent_append_block(inode_ref, &last_iblock,
1311 &fblock, true);
[1196df6]1312 if (rc != EOK) {
1313 ext4fs_node_put(fn);
1314 async_answer_0(callid, rc);
1315 return rc;
1316 }
[d510ac01]1317 }
[38542dc]1318
1319 rc = ext4_extent_append_block(inode_ref, &last_iblock,
1320 &fblock, false);
[d510ac01]1321 if (rc != EOK) {
1322 ext4fs_node_put(fn);
1323 async_answer_0(callid, rc);
1324 return rc;
1325 }
[1196df6]1326 } else {
[38542dc]1327 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
[1196df6]1328 if (rc != EOK) {
1329 ext4fs_node_put(fn);
1330 async_answer_0(callid, rc);
1331 return rc;
1332 }
[38542dc]1333
1334 rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
1335 iblock, fblock);
[1196df6]1336 if (rc != EOK) {
1337 ext4_balloc_free_block(inode_ref, fblock);
1338 ext4fs_node_put(fn);
1339 async_answer_0(callid, rc);
1340 return rc;
1341 }
[b12ca16]1342 }
[38542dc]1343
[35f48f2]1344 flags = BLOCK_FLAGS_NOREAD;
[1196df6]1345 inode_ref->dirty = true;
[1c1c736]1346 }
[38542dc]1347
[06d85e5]1348 /* Load target block */
[5614c7f]1349 block_t *write_block;
[35f48f2]1350 rc = block_get(&write_block, service_id, fblock, flags);
[1c1c736]1351 if (rc != EOK) {
1352 ext4fs_node_put(fn);
1353 async_answer_0(callid, rc);
1354 return rc;
1355 }
[38542dc]1356
1357 if (flags == BLOCK_FLAGS_NOREAD)
[35f48f2]1358 memset(write_block->data, 0, block_size);
[38542dc]1359
1360 rc = async_data_write_finalize(callid, write_block->data +
1361 (pos % block_size), bytes);
[35f48f2]1362 if (rc != EOK) {
[cd00f93]1363 ext4fs_node_put(fn);
1364 return rc;
[35f48f2]1365 }
[38542dc]1366
[1c1c736]1367 write_block->dirty = true;
[38542dc]1368
[1c1c736]1369 rc = block_put(write_block);
1370 if (rc != EOK) {
1371 ext4fs_node_put(fn);
1372 return rc;
1373 }
[38542dc]1374
[06d85e5]1375 /* Do some counting */
[38542dc]1376 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock,
1377 inode_ref->inode);
[35f48f2]1378 if (pos + bytes > old_inode_size) {
1379 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1380 inode_ref->dirty = true;
[1c1c736]1381 }
[38542dc]1382
[35f48f2]1383 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[1c1c736]1384 *wbytes = bytes;
[38542dc]1385
[1c1c736]1386 return ext4fs_node_put(fn);
[d3a9ae74]1387}
1388
[1fff583]1389/** Truncate file.
1390 *
1391 * Only the direction to shorter file is supported.
1392 *
[38542dc]1393 * @param service_id Device identifier
1394 * @param index Index if node to truncated
1395 * @param new_size New size of file
1396 *
1397 * @return Error code
1398 *
[1fff583]1399 */
[5f6cb14]1400static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
[38542dc]1401 aoff64_t new_size)
[d3a9ae74]1402{
[5614c7f]1403 fs_node_t *fn;
[38542dc]1404 int rc = ext4fs_node_get(&fn, service_id, index);
1405 if (rc != EOK)
[d5a78e28]1406 return rc;
[38542dc]1407
[3d4fd2c]1408 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1409 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[38542dc]1410
[1ac1ab4]1411 rc = ext4_filesystem_truncate_inode(inode_ref, new_size);
[d5a78e28]1412 ext4fs_node_put(fn);
[38542dc]1413
[3d4fd2c]1414 return rc;
[d3a9ae74]1415}
1416
[1fff583]1417/** Close file.
[81a7858]1418 *
[38542dc]1419 * @param service_id Device identifier
1420 * @param index I-node number
1421 *
1422 * @return Error code
1423 *
[1fff583]1424 */
[d3a9ae74]1425static int ext4fs_close(service_id_t service_id, fs_index_t index)
1426{
1427 return EOK;
1428}
1429
[1fff583]1430/** Destroy node specified by index.
1431 *
[38542dc]1432 * @param service_id Device identifier
1433 * @param index I-node to destroy
1434 *
1435 * @return Error code
1436 *
[1fff583]1437 */
[d3a9ae74]1438static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1439{
[5614c7f]1440 fs_node_t *fn;
[38542dc]1441 int rc = ext4fs_node_get(&fn, service_id, index);
1442 if (rc != EOK)
[8be96a0]1443 return rc;
[38542dc]1444
[8be96a0]1445 /* Destroy the inode */
1446 return ext4fs_destroy_node(fn);
[d3a9ae74]1447}
1448
[38542dc]1449/** Enforce inode synchronization (write) to device.
1450 *
1451 * @param service_id Device identifier
1452 * @param index I-node number.
[1fff583]1453 *
1454 */
[d3a9ae74]1455static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1456{
[5614c7f]1457 fs_node_t *fn;
[38542dc]1458 int rc = ext4fs_node_get(&fn, service_id, index);
1459 if (rc != EOK)
[35f48f2]1460 return rc;
[38542dc]1461
[5614c7f]1462 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[35f48f2]1463 enode->inode_ref->dirty = true;
[38542dc]1464
[35f48f2]1465 return ext4fs_node_put(fn);
[d3a9ae74]1466}
1467
[1fff583]1468/** VFS operations
1469 *
1470 */
[d3a9ae74]1471vfs_out_ops_t ext4fs_ops = {
1472 .mounted = ext4fs_mounted,
1473 .unmounted = ext4fs_unmounted,
1474 .read = ext4fs_read,
1475 .write = ext4fs_write,
1476 .truncate = ext4fs_truncate,
1477 .close = ext4fs_close,
1478 .destroy = ext4fs_destroy,
[38542dc]1479 .sync = ext4fs_sync
[d3a9ae74]1480};
1481
1482/**
1483 * @}
[2b9e142]1484 */
Note: See TracBrowser for help on using the repository browser.