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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d1ef4a1 was 38542dc, checked in by Martin Decky <martin@…>, 13 years ago

ext4 code review and coding style cleanup

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