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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0b63dc2 was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

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