source: mainline/uspace/lib/ext4/src/ops.c@ 71fe4723

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 71fe4723 was 81dd2ed, checked in by Jiri Svoboda <jiri@…>, 8 years ago

The division of ext4 into fs and lib is arbitrary, preventing improvement.

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