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

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

Reading volume label from ext4 file system.

  • 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
103static size_t open_nodes_key_hash(void *key_arg)
[3711e7e]104{
[062d900]105 node_key_t *key = (node_key_t *)key_arg;
106 return hash_combine(key->service_id, key->index);
[3711e7e]107}
108
[062d900]109static size_t open_nodes_hash(const ht_link_t *item)
[3711e7e]110{
[be39fc6]111 ext4_node_t *enode = hash_table_get_inst(item, ext4_node_t, link);
[1b20da0]112 return hash_combine(enode->instance->service_id, enode->inode_ref->index);
[3711e7e]113}
114
[062d900]115static bool open_nodes_key_equal(void *key_arg, const ht_link_t *item)
[3711e7e]116{
[062d900]117 node_key_t *key = (node_key_t *)key_arg;
[be39fc6]118 ext4_node_t *enode = hash_table_get_inst(item, ext4_node_t, link);
[a35b458]119
[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.