source: mainline/uspace/lib/ext4/src/ops.c@ 84239b1

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

And there was much fixing.

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