source: mainline/uspace/srv/fs/ext4fs/ext4fs_ops.c@ 2d53cfc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d53cfc was 062d900, checked in by Jakub Jermar <jakub@…>, 13 years ago

Cherrypick userspace hash table changes from lp:~adam-hraska+lp/helenos/rcu/.

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