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

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

comments

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