source: mainline/uspace/srv/fs/ext4fs/ext4fs_ops.c@ 3dd148d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3dd148d was 3dd148d, checked in by Manuele Conti <conti.ma@…>, 12 years ago

Change stafs function operation to allow correct error handling.

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