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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bae2a79e was bae2a79e, checked in by Frantisek Princ <frantisek.princ@…>, 13 years ago

Comments and some simple modifications of code for work with directory entry

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