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

Last change on this file since cb747b3 was 61eb2ce2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 years ago

Make hash table operations immutable, because global mutable state is evil

  • Property mode set to 100644
File size: 35.0 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
[984a9ba]60static errno_t ext4_read_directory(ipc_call_t *, aoff64_t, size_t,
[be39fc6]61 ext4_instance_t *, ext4_inode_ref_t *, size_t *);
[984a9ba]62static errno_t ext4_read_file(ipc_call_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 *);
[3bacee1]73errno_t ext4_node_put(fs_node_t *);
[b7fd2a0]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
[5e801dc]103static size_t open_nodes_key_hash(const void *key_arg)
[3711e7e]104{
[5e801dc]105 const node_key_t *key = key_arg;
[062d900]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
[5e801dc]115static bool open_nodes_key_equal(const void *key_arg, const ht_link_t *item)
[3711e7e]116{
[5e801dc]117 const node_key_t *key = key_arg;
[be39fc6]118 ext4_node_t *enode = hash_table_get_inst(item, ext4_node_t, link);
[a35b458]119
[3bacee1]120 return key->service_id == enode->instance->service_id &&
121 key->index == enode->inode_ref->index;
[3711e7e]122}
123
[61eb2ce2]124static const 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
[7c3fb9b]148/** Finalization of the driver.
[1fff583]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
[f783081]812/** Get service ID for a service-special file.
[1fff583]813 *
[38542dc]814 * @param node Node to extract id from
815 *
[f783081]816 * @return Service ID for a service-special file, zero for a regular file.
[38542dc]817 *
[1fff583]818 */
[be39fc6]819service_id_t ext4_service_get(fs_node_t *fn)
[d3a9ae74]820{
[f783081]821 return 0;
[d3a9ae74]822}
823
[b7fd2a0]824errno_t ext4_size_block(service_id_t service_id, uint32_t *size)
[5930e3f]825{
[be39fc6]826 ext4_instance_t *inst;
[b7fd2a0]827 errno_t rc = ext4_instance_get(service_id, &inst);
[5930e3f]828 if (rc != EOK)
829 return rc;
[3dd148d]830
[5930e3f]831 if (NULL == inst)
832 return ENOENT;
[e8f0158]833
[5930e3f]834 ext4_superblock_t *sb = inst->filesystem->superblock;
[3dd148d]835 *size = ext4_superblock_get_block_size(sb);
[e8f0158]836
[3dd148d]837 return EOK;
[5930e3f]838}
839
[b7fd2a0]840errno_t ext4_total_block_count(service_id_t service_id, uint64_t *count)
[e8f0158]841{
[be39fc6]842 ext4_instance_t *inst;
[b7fd2a0]843 errno_t rc = ext4_instance_get(service_id, &inst);
[e8f0158]844 if (rc != EOK)
845 return rc;
[3dd148d]846
[e8f0158]847 if (NULL == inst)
848 return ENOENT;
849
850 ext4_superblock_t *sb = inst->filesystem->superblock;
[3dd148d]851 *count = ext4_superblock_get_blocks_count(sb);
[e8f0158]852
[3dd148d]853 return EOK;
[e8f0158]854}
855
[b7fd2a0]856errno_t ext4_free_block_count(service_id_t service_id, uint64_t *count)
[e8f0158]857{
[be39fc6]858 ext4_instance_t *inst;
[b7fd2a0]859 errno_t rc = ext4_instance_get(service_id, &inst);
[e8f0158]860 if (rc != EOK)
861 return rc;
862
863 ext4_superblock_t *sb = inst->filesystem->superblock;
[3dd148d]864 *count = ext4_superblock_get_free_blocks_count(sb);
[e8f0158]865
[3dd148d]866 return EOK;
[e8f0158]867}
868
[d3a9ae74]869/*
870 * libfs operations.
871 */
[be39fc6]872libfs_ops_t ext4_libfs_ops = {
873 .root_get = ext4_root_get,
874 .match = ext4_match,
875 .node_get = ext4_node_get,
876 .node_open = ext4_node_open,
877 .node_put = ext4_node_put,
878 .create = ext4_create_node,
879 .destroy = ext4_destroy_node,
880 .link = ext4_link,
881 .unlink = ext4_unlink,
882 .has_children = ext4_has_children,
883 .index_get = ext4_index_get,
884 .size_get = ext4_size_get,
885 .lnkcnt_get = ext4_lnkcnt_get,
886 .is_directory = ext4_is_directory,
887 .is_file = ext4_is_file,
888 .service_get = ext4_service_get,
889 .size_block = ext4_size_block,
890 .total_block_count = ext4_total_block_count,
891 .free_block_count = ext4_free_block_count
[d3a9ae74]892};
893
894/*
895 * VFS operations.
896 */
897
[d2c8533]898/** Probe operation.
899 *
900 * Try to get information about specified filesystem from device.
901 *
902 * @param sevice_id Service ID
903 * @param info Place to store information
904 *
905 * @return Error code
906 */
[b7fd2a0]907static errno_t ext4_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
[d2c8533]908{
[b209135]909 ext4_fs_probe_info_t pinfo;
910 errno_t rc;
911
912 rc = ext4_filesystem_probe(service_id, &pinfo);
913 if (rc != EOK)
914 return rc;
915
916 memcpy(info->label, pinfo.vol_name, sizeof(pinfo.vol_name));
917 return EOK;
[d2c8533]918}
919
[38542dc]920/** Mount operation.
[1fff583]921 *
922 * Try to mount specified filesystem from device.
[38542dc]923 *
924 * @param service_id Identifier of device
925 * @param opts Mount options
926 * @param index Output value - index of root node
927 * @param size Output value - size of root node
928 *
929 * @return Error code
930 *
[1fff583]931 */
[b7fd2a0]932static errno_t ext4_mounted(service_id_t service_id, const char *opts,
[4f30222]933 fs_index_t *index, aoff64_t *size)
[d3a9ae74]934{
[de5b708]935 ext4_filesystem_t *fs;
[a35b458]936
[6c501f8]937 /* Allocate instance structure */
[be39fc6]938 ext4_instance_t *inst = (ext4_instance_t *)
939 malloc(sizeof(ext4_instance_t));
[de5b708]940 if (inst == NULL)
[6c501f8]941 return ENOMEM;
[a35b458]942
[0b293a6]943 enum cache_mode cmode;
[38542dc]944 if (str_cmp(opts, "wtcache") == 0)
[0b293a6]945 cmode = CACHE_MODE_WT;
[38542dc]946 else
[0b293a6]947 cmode = CACHE_MODE_WB;
[a35b458]948
[6c501f8]949 /* Initialize instance */
950 link_initialize(&inst->link);
951 inst->service_id = service_id;
952 inst->open_nodes_count = 0;
[a35b458]953
[4bfad34]954 /* Initialize the filesystem */
955 aoff64_t rnsize;
[b7fd2a0]956 errno_t rc = ext4_filesystem_open(inst, service_id, cmode, &rnsize, &fs);
[6c501f8]957 if (rc != EOK) {
958 free(inst);
959 return rc;
960 }
[a35b458]961
[6c501f8]962 /* Add instance to the list */
963 fibril_mutex_lock(&instance_list_mutex);
964 list_append(&inst->link, &instance_list);
965 fibril_mutex_unlock(&instance_list_mutex);
[a35b458]966
[6c501f8]967 *index = EXT4_INODE_ROOT_INDEX;
[4bfad34]968 *size = rnsize;
[a35b458]969
[4bfad34]970 return EOK;
[d3a9ae74]971}
972
[1fff583]973/** Unmount operation.
974 *
975 * Correctly release the filesystem.
976 *
[38542dc]977 * @param service_id Device to be unmounted
978 *
979 * @return Error code
980 *
[1fff583]981 */
[b7fd2a0]982static errno_t ext4_unmounted(service_id_t service_id)
[d3a9ae74]983{
[be39fc6]984 ext4_instance_t *inst;
[b7fd2a0]985 errno_t rc = ext4_instance_get(service_id, &inst);
[38542dc]986 if (rc != EOK)
[6c501f8]987 return rc;
[a35b458]988
[6c501f8]989 fibril_mutex_lock(&open_nodes_lock);
[a35b458]990
[6c501f8]991 if (inst->open_nodes_count != 0) {
992 fibril_mutex_unlock(&open_nodes_lock);
993 return EBUSY;
994 }
[a35b458]995
[6c501f8]996 /* Remove the instance from the list */
997 fibril_mutex_lock(&instance_list_mutex);
998 list_remove(&inst->link);
999 fibril_mutex_unlock(&instance_list_mutex);
[a35b458]1000
[6c501f8]1001 fibril_mutex_unlock(&open_nodes_lock);
[a35b458]1002
[de5b708]1003 rc = ext4_filesystem_close(inst->filesystem);
1004 if (rc != EOK) {
1005 fibril_mutex_lock(&instance_list_mutex);
1006 list_append(&inst->link, &instance_list);
1007 fibril_mutex_unlock(&instance_list_mutex);
1008 }
1009
1010 free(inst);
1011 return EOK;
[d3a9ae74]1012}
1013
[1fff583]1014/** Read bytes from node.
1015 *
[38542dc]1016 * @param service_id Device to read data from
1017 * @param index Number of node to read from
1018 * @param pos Position where the read should be started
1019 * @param rbytes Output value, where the real size was returned
1020 *
1021 * @return Error code
1022 *
[1fff583]1023 */
[b7fd2a0]1024static errno_t ext4_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
[38542dc]1025 size_t *rbytes)
[d3a9ae74]1026{
[9b9d37bb]1027 /*
1028 * Receive the read request.
1029 */
[984a9ba]1030 ipc_call_t call;
[9b9d37bb]1031 size_t size;
[984a9ba]1032 if (!async_data_read_receive(&call, &size)) {
1033 async_answer_0(&call, EINVAL);
[9b9d37bb]1034 return EINVAL;
1035 }
[a35b458]1036
[be39fc6]1037 ext4_instance_t *inst;
[b7fd2a0]1038 errno_t rc = ext4_instance_get(service_id, &inst);
[9b9d37bb]1039 if (rc != EOK) {
[984a9ba]1040 async_answer_0(&call, rc);
[9b9d37bb]1041 return rc;
1042 }
[a35b458]1043
[06d85e5]1044 /* Load i-node */
[5614c7f]1045 ext4_inode_ref_t *inode_ref;
[9b9d37bb]1046 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
1047 if (rc != EOK) {
[984a9ba]1048 async_answer_0(&call, rc);
[9b9d37bb]1049 return rc;
1050 }
[a35b458]1051
[06d85e5]1052 /* Read from i-node by type */
[9b9d37bb]1053 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
[38542dc]1054 EXT4_INODE_MODE_FILE)) {
[984a9ba]1055 rc = ext4_read_file(&call, pos, size, inst, inode_ref,
[38542dc]1056 rbytes);
[9b9d37bb]1057 } else if (ext4_inode_is_type(inst->filesystem->superblock,
[38542dc]1058 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
[984a9ba]1059 rc = ext4_read_directory(&call, pos, size, inst, inode_ref,
[38542dc]1060 rbytes);
[9b9d37bb]1061 } else {
1062 /* Other inode types not supported */
[984a9ba]1063 async_answer_0(&call, ENOTSUP);
[9b9d37bb]1064 rc = ENOTSUP;
1065 }
[a35b458]1066
[b7fd2a0]1067 errno_t const rc2 = ext4_filesystem_put_inode_ref(inode_ref);
[a35b458]1068
[532f53d]1069 return rc == EOK ? rc2 : rc;
[9b9d37bb]1070}
1071
[1fff583]1072/** Check if filename is dot or dotdot (reserved names).
1073 *
[38542dc]1074 * @param name Name to check
1075 * @param name_size Length of string name
1076 *
1077 * @return Result of the check
1078 *
[1fff583]1079 */
[be39fc6]1080bool ext4_is_dots(const uint8_t *name, size_t name_size)
[8958a26]1081{
[38542dc]1082 if ((name_size == 1) && (name[0] == '.'))
[9b9d37bb]1083 return true;
[a35b458]1084
[38542dc]1085 if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
[9b9d37bb]1086 return true;
[a35b458]1087
[9b9d37bb]1088 return false;
1089}
1090
[1fff583]1091/** Read data from directory.
1092 *
[984a9ba]1093 * @param call IPC call
[38542dc]1094 * @param pos Position to start reading from
1095 * @param size How many bytes to read
1096 * @param inst Filesystem instance
1097 * @param inode_ref Node to read data from
1098 * @param rbytes Output value to return real number of bytes was read
1099 *
1100 * @return Error code
1101 *
[1fff583]1102 */
[984a9ba]1103errno_t ext4_read_directory(ipc_call_t *call, aoff64_t pos, size_t size,
[be39fc6]1104 ext4_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
[9b9d37bb]1105{
[5614c7f]1106 ext4_directory_iterator_t it;
[b7fd2a0]1107 errno_t rc = ext4_directory_iterator_init(&it, inode_ref, pos);
[9b9d37bb]1108 if (rc != EOK) {
[984a9ba]1109 async_answer_0(call, rc);
[9b9d37bb]1110 return rc;
1111 }
[a35b458]1112
[38542dc]1113 /*
1114 * Find next interesting directory entry.
[9b9d37bb]1115 * We want to skip . and .. entries
1116 * as these are not used in HelenOS
1117 */
[5614c7f]1118 bool found = false;
[9b9d37bb]1119 while (it.current != NULL) {
[38542dc]1120 if (it.current->inode == 0)
[9b9d37bb]1121 goto skip;
[a35b458]1122
[5614c7f]1123 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
[9b9d37bb]1124 inst->filesystem->superblock, it.current);
[a35b458]1125
[38542dc]1126 /* Skip . and .. */
[be39fc6]1127 if (ext4_is_dots(it.current->name, name_size))
[9b9d37bb]1128 goto skip;
[a35b458]1129
[38542dc]1130 /*
1131 * The on-disk entry does not contain \0 at the end
[9b9d37bb]1132 * end of entry name, so we copy it to new buffer
1133 * and add the \0 at the end
1134 */
[38542dc]1135 uint8_t *buf = malloc(name_size + 1);
[9b9d37bb]1136 if (buf == NULL) {
1137 ext4_directory_iterator_fini(&it);
[984a9ba]1138 async_answer_0(call, ENOMEM);
[9b9d37bb]1139 return ENOMEM;
1140 }
[a35b458]1141
[9b9d37bb]1142 memcpy(buf, &it.current->name, name_size);
1143 *(buf + name_size) = 0;
1144 found = true;
[a35b458]1145
[984a9ba]1146 (void) async_data_read_finalize(call, buf, name_size + 1);
[9b9d37bb]1147 free(buf);
1148 break;
[a35b458]1149
[3bacee1]1150 skip:
[9b9d37bb]1151 rc = ext4_directory_iterator_next(&it);
1152 if (rc != EOK) {
1153 ext4_directory_iterator_fini(&it);
[984a9ba]1154 async_answer_0(call, rc);
[9b9d37bb]1155 return rc;
1156 }
1157 }
[a35b458]1158
[5614c7f]1159 uint64_t next;
[9b9d37bb]1160 if (found) {
1161 rc = ext4_directory_iterator_next(&it);
[38542dc]1162 if (rc != EOK)
[9b9d37bb]1163 return rc;
[a35b458]1164
[9b9d37bb]1165 next = it.current_offset;
1166 }
[a35b458]1167
[9b9d37bb]1168 rc = ext4_directory_iterator_fini(&it);
[38542dc]1169 if (rc != EOK)
[9b9d37bb]1170 return rc;
[a35b458]1171
[06d85e5]1172 /* Prepare return values */
[9b9d37bb]1173 if (found) {
1174 *rbytes = next - pos;
1175 return EOK;
1176 } else {
[984a9ba]1177 async_answer_0(call, ENOENT);
[9b9d37bb]1178 return ENOENT;
1179 }
[d3a9ae74]1180}
1181
[1fff583]1182/** Read data from file.
1183 *
[984a9ba]1184 * @param call IPC call
[38542dc]1185 * @param pos Position to start reading from
1186 * @param size How many bytes to read
1187 * @param inst Filesystem instance
1188 * @param inode_ref Node to read data from
1189 * @param rbytes Output value to return real number of bytes was read
1190 *
1191 * @return Error code
1192 *
[1fff583]1193 */
[984a9ba]1194errno_t ext4_read_file(ipc_call_t *call, aoff64_t pos, size_t size,
[be39fc6]1195 ext4_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
[9b9d37bb]1196{
[5614c7f]1197 ext4_superblock_t *sb = inst->filesystem->superblock;
1198 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
[a35b458]1199
[9b9d37bb]1200 if (pos >= file_size) {
1201 /* Read 0 bytes successfully */
[984a9ba]1202 async_data_read_finalize(call, NULL, 0);
[9b9d37bb]1203 *rbytes = 0;
1204 return EOK;
1205 }
[a35b458]1206
[9b9d37bb]1207 /* For now, we only read data from one block at a time */
[5614c7f]1208 uint32_t block_size = ext4_superblock_get_block_size(sb);
1209 aoff64_t file_block = pos / block_size;
1210 uint32_t offset_in_block = pos % block_size;
1211 uint32_t bytes = min(block_size - offset_in_block, size);
[a35b458]1212
[9b9d37bb]1213 /* Handle end of file */
[38542dc]1214 if (pos + bytes > file_size)
[9b9d37bb]1215 bytes = file_size - pos;
[a35b458]1216
[9b9d37bb]1217 /* Get the real block number */
[5614c7f]1218 uint32_t fs_block;
[b7fd2a0]1219 errno_t rc = ext4_filesystem_get_inode_data_block_index(inode_ref,
[38542dc]1220 file_block, &fs_block);
[9b9d37bb]1221 if (rc != EOK) {
[984a9ba]1222 async_answer_0(call, rc);
[9b9d37bb]1223 return rc;
1224 }
[a35b458]1225
[38542dc]1226 /*
1227 * Check for sparse file.
[2b9e142]1228 * If ext4_filesystem_get_inode_data_block_index returned
[9b9d37bb]1229 * fs_block == 0, it means that the given block is not allocated for the
1230 * file and we need to return a buffer of zeros
1231 */
[5614c7f]1232 uint8_t *buffer;
[9b9d37bb]1233 if (fs_block == 0) {
1234 buffer = malloc(bytes);
1235 if (buffer == NULL) {
[984a9ba]1236 async_answer_0(call, ENOMEM);
[9b9d37bb]1237 return ENOMEM;
1238 }
[a35b458]1239
[9b9d37bb]1240 memset(buffer, 0, bytes);
[a35b458]1241
[984a9ba]1242 rc = async_data_read_finalize(call, buffer, bytes);
[9b9d37bb]1243 *rbytes = bytes;
[a35b458]1244
[9b9d37bb]1245 free(buffer);
[532f53d]1246 return rc;
[9b9d37bb]1247 }
[a35b458]1248
[9b9d37bb]1249 /* Usual case - we need to read a block from device */
[5614c7f]1250 block_t *block;
[9b9d37bb]1251 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1252 if (rc != EOK) {
[984a9ba]1253 async_answer_0(call, rc);
[9b9d37bb]1254 return rc;
1255 }
[a35b458]1256
[9b9d37bb]1257 assert(offset_in_block + bytes <= block_size);
[984a9ba]1258 rc = async_data_read_finalize(call, block->data + offset_in_block, bytes);
[532f53d]1259 if (rc != EOK) {
1260 block_put(block);
1261 return rc;
1262 }
[a35b458]1263
[9b9d37bb]1264 rc = block_put(block);
[38542dc]1265 if (rc != EOK)
[9b9d37bb]1266 return rc;
[a35b458]1267
[9b9d37bb]1268 *rbytes = bytes;
1269 return EOK;
1270}
[3711e7e]1271
[1fff583]1272/** Write bytes to file
1273 *
[38542dc]1274 * @param service_id Device identifier
1275 * @param index I-node number of file
1276 * @param pos Position in file to start reading from
1277 * @param wbytes Output value - real number of written bytes
1278 * @param nsize Output value - new size of i-node
1279 *
1280 * @return Error code
1281 *
[1fff583]1282 */
[b7fd2a0]1283static errno_t ext4_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
[38542dc]1284 size_t *wbytes, aoff64_t *nsize)
[d3a9ae74]1285{
[5614c7f]1286 fs_node_t *fn;
[84239b1]1287 errno_t rc2;
[b7fd2a0]1288 errno_t rc = ext4_node_get(&fn, service_id, index);
[38542dc]1289 if (rc != EOK)
[1c1c736]1290 return rc;
[a35b458]1291
[984a9ba]1292 ipc_call_t call;
[5614c7f]1293 size_t len;
[984a9ba]1294 if (!async_data_write_receive(&call, &len)) {
[532f53d]1295 rc = EINVAL;
[984a9ba]1296 async_answer_0(&call, rc);
[532f53d]1297 goto exit;
[1c1c736]1298 }
[a35b458]1299
[be39fc6]1300 ext4_node_t *enode = EXT4_NODE(fn);
[5614c7f]1301 ext4_filesystem_t *fs = enode->instance->filesystem;
[a35b458]1302
[5614c7f]1303 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[a35b458]1304
[06d85e5]1305 /* Prevent writing to more than one block */
[5614c7f]1306 uint32_t bytes = min(len, block_size - (pos % block_size));
[a35b458]1307
[5614c7f]1308 int flags = BLOCK_FLAGS_NONE;
[38542dc]1309 if (bytes == block_size)
[35f48f2]1310 flags = BLOCK_FLAGS_NOREAD;
[a35b458]1311
[5614c7f]1312 uint32_t iblock = pos / block_size;
1313 uint32_t fblock;
[a35b458]1314
[06d85e5]1315 /* Load inode */
[5614c7f]1316 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[38542dc]1317 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, iblock,
1318 &fblock);
[6088193]1319 if (rc != EOK) {
[984a9ba]1320 async_answer_0(&call, rc);
[532f53d]1321 goto exit;
[6088193]1322 }
[a35b458]1323
[06d85e5]1324 /* Check for sparse file */
[1c1c736]1325 if (fblock == 0) {
[38542dc]1326 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1327 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1328 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1329 uint32_t last_iblock =
1330 ext4_inode_get_size(fs->superblock, inode_ref->inode) /
1331 block_size;
[a35b458]1332
[d510ac01]1333 while (last_iblock < iblock) {
[38542dc]1334 rc = ext4_extent_append_block(inode_ref, &last_iblock,
1335 &fblock, true);
[1196df6]1336 if (rc != EOK) {
[984a9ba]1337 async_answer_0(&call, rc);
[532f53d]1338 goto exit;
[1196df6]1339 }
[d510ac01]1340 }
[a35b458]1341
[38542dc]1342 rc = ext4_extent_append_block(inode_ref, &last_iblock,
1343 &fblock, false);
[d510ac01]1344 if (rc != EOK) {
[984a9ba]1345 async_answer_0(&call, rc);
[532f53d]1346 goto exit;
[d510ac01]1347 }
[1196df6]1348 } else {
[38542dc]1349 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
[1196df6]1350 if (rc != EOK) {
[984a9ba]1351 async_answer_0(&call, rc);
[532f53d]1352 goto exit;
[1196df6]1353 }
[a35b458]1354
[38542dc]1355 rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
1356 iblock, fblock);
[1196df6]1357 if (rc != EOK) {
1358 ext4_balloc_free_block(inode_ref, fblock);
[984a9ba]1359 async_answer_0(&call, rc);
[532f53d]1360 goto exit;
[1196df6]1361 }
[b12ca16]1362 }
[a35b458]1363
[35f48f2]1364 flags = BLOCK_FLAGS_NOREAD;
[1196df6]1365 inode_ref->dirty = true;
[1c1c736]1366 }
[a35b458]1367
[06d85e5]1368 /* Load target block */
[5614c7f]1369 block_t *write_block;
[35f48f2]1370 rc = block_get(&write_block, service_id, fblock, flags);
[1c1c736]1371 if (rc != EOK) {
[984a9ba]1372 async_answer_0(&call, rc);
[532f53d]1373 goto exit;
[1c1c736]1374 }
[a35b458]1375
[89dcf93]1376 if (flags == BLOCK_FLAGS_NOREAD)
[35f48f2]1377 memset(write_block->data, 0, block_size);
[532f53d]1378
[984a9ba]1379 rc = async_data_write_finalize(&call, write_block->data +
[38542dc]1380 (pos % block_size), bytes);
[2f591127]1381 if (rc != EOK) {
1382 block_put(write_block);
[532f53d]1383 goto exit;
[2f591127]1384 }
[532f53d]1385
[89dcf93]1386 write_block->dirty = true;
1387
[1c1c736]1388 rc = block_put(write_block);
[532f53d]1389 if (rc != EOK)
1390 goto exit;
1391
[06d85e5]1392 /* Do some counting */
[38542dc]1393 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock,
1394 inode_ref->inode);
[35f48f2]1395 if (pos + bytes > old_inode_size) {
1396 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1397 inode_ref->dirty = true;
[1c1c736]1398 }
[532f53d]1399
[35f48f2]1400 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[1c1c736]1401 *wbytes = bytes;
[532f53d]1402
1403exit:
[84239b1]1404 rc2 = ext4_node_put(fn);
[532f53d]1405 return rc == EOK ? rc2 : rc;
[d3a9ae74]1406}
1407
[1fff583]1408/** Truncate file.
1409 *
1410 * Only the direction to shorter file is supported.
1411 *
[38542dc]1412 * @param service_id Device identifier
1413 * @param index Index if node to truncated
1414 * @param new_size New size of file
1415 *
1416 * @return Error code
1417 *
[1fff583]1418 */
[b7fd2a0]1419static errno_t ext4_truncate(service_id_t service_id, fs_index_t index,
[38542dc]1420 aoff64_t new_size)
[d3a9ae74]1421{
[5614c7f]1422 fs_node_t *fn;
[b7fd2a0]1423 errno_t rc = ext4_node_get(&fn, service_id, index);
[38542dc]1424 if (rc != EOK)
[d5a78e28]1425 return rc;
[a35b458]1426
[be39fc6]1427 ext4_node_t *enode = EXT4_NODE(fn);
[3d4fd2c]1428 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[a35b458]1429
[1ac1ab4]1430 rc = ext4_filesystem_truncate_inode(inode_ref, new_size);
[b7fd2a0]1431 errno_t const rc2 = ext4_node_put(fn);
[a35b458]1432
[532f53d]1433 return rc == EOK ? rc2 : rc;
[d3a9ae74]1434}
1435
[1fff583]1436/** Close file.
[81a7858]1437 *
[38542dc]1438 * @param service_id Device identifier
1439 * @param index I-node number
1440 *
1441 * @return Error code
1442 *
[1fff583]1443 */
[b7fd2a0]1444static errno_t ext4_close(service_id_t service_id, fs_index_t index)
[d3a9ae74]1445{
1446 return EOK;
1447}
1448
[1fff583]1449/** Destroy node specified by index.
1450 *
[38542dc]1451 * @param service_id Device identifier
1452 * @param index I-node to destroy
1453 *
1454 * @return Error code
1455 *
[1fff583]1456 */
[b7fd2a0]1457static errno_t ext4_destroy(service_id_t service_id, fs_index_t index)
[d3a9ae74]1458{
[5614c7f]1459 fs_node_t *fn;
[b7fd2a0]1460 errno_t rc = ext4_node_get(&fn, service_id, index);
[38542dc]1461 if (rc != EOK)
[8be96a0]1462 return rc;
[a35b458]1463
[8be96a0]1464 /* Destroy the inode */
[be39fc6]1465 return ext4_destroy_node(fn);
[d3a9ae74]1466}
1467
[38542dc]1468/** Enforce inode synchronization (write) to device.
1469 *
1470 * @param service_id Device identifier
1471 * @param index I-node number.
[1fff583]1472 *
1473 */
[b7fd2a0]1474static errno_t ext4_sync(service_id_t service_id, fs_index_t index)
[d3a9ae74]1475{
[5614c7f]1476 fs_node_t *fn;
[b7fd2a0]1477 errno_t rc = ext4_node_get(&fn, service_id, index);
[38542dc]1478 if (rc != EOK)
[35f48f2]1479 return rc;
[a35b458]1480
[be39fc6]1481 ext4_node_t *enode = EXT4_NODE(fn);
[35f48f2]1482 enode->inode_ref->dirty = true;
[a35b458]1483
[be39fc6]1484 return ext4_node_put(fn);
[d3a9ae74]1485}
1486
[1fff583]1487/** VFS operations
1488 *
1489 */
[be39fc6]1490vfs_out_ops_t ext4_ops = {
1491 .fsprobe = ext4_fsprobe,
1492 .mounted = ext4_mounted,
1493 .unmounted = ext4_unmounted,
1494 .read = ext4_read,
1495 .write = ext4_write,
1496 .truncate = ext4_truncate,
1497 .close = ext4_close,
1498 .destroy = ext4_destroy,
1499 .sync = ext4_sync
[d3a9ae74]1500};
1501
1502/**
1503 * @}
[2b9e142]1504 */
Note: See TracBrowser for help on using the repository browser.