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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8867cf6 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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