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

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

Some TODOs solved (mostly in error handling)

  • Property mode set to 100644
File size: 27.6 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
[6c501f8]57typedef struct ext4fs_instance {
58 link_t link;
59 service_id_t service_id;
60 ext4_filesystem_t *filesystem;
61 unsigned int open_nodes_count;
62} ext4fs_instance_t;
63
64typedef struct ext4fs_node {
65 ext4fs_instance_t *instance;
66 ext4_inode_ref_t *inode_ref;
67 fs_node_t *fs_node;
68 link_t link;
69 unsigned int references;
70} ext4fs_node_t;
71
72/*
73 * Forward declarations of auxiliary functions
74 */
[9b9d37bb]75
76static int ext4fs_read_directory(ipc_callid_t, aoff64_t, size_t,
77 ext4fs_instance_t *, ext4_inode_ref_t *, size_t *);
78static int ext4fs_read_file(ipc_callid_t, aoff64_t, size_t, ext4fs_instance_t *,
79 ext4_inode_ref_t *, size_t *);
80static bool ext4fs_is_dots(const uint8_t *, size_t);
[6c501f8]81static int ext4fs_instance_get(service_id_t, ext4fs_instance_t **);
82static int ext4fs_node_get_core(fs_node_t **, ext4fs_instance_t *, fs_index_t);
[9c0c0e1]83static int ext4fs_node_put_core(ext4fs_node_t *);
[d3a9ae74]84
85/*
86 * Forward declarations of EXT4 libfs operations.
87 */
88static int ext4fs_root_get(fs_node_t **, service_id_t);
89static int ext4fs_match(fs_node_t **, fs_node_t *, const char *);
90static int ext4fs_node_get(fs_node_t **, service_id_t, fs_index_t);
91static int ext4fs_node_open(fs_node_t *);
92static int ext4fs_node_put(fs_node_t *);
93static int ext4fs_create_node(fs_node_t **, service_id_t, int);
94static int ext4fs_destroy_node(fs_node_t *);
95static int ext4fs_link(fs_node_t *, fs_node_t *, const char *);
96static int ext4fs_unlink(fs_node_t *, fs_node_t *, const char *);
97static int ext4fs_has_children(bool *, fs_node_t *);
98static fs_index_t ext4fs_index_get(fs_node_t *);
99static aoff64_t ext4fs_size_get(fs_node_t *);
100static unsigned ext4fs_lnkcnt_get(fs_node_t *);
101static bool ext4fs_is_directory(fs_node_t *);
102static bool ext4fs_is_file(fs_node_t *node);
103static service_id_t ext4fs_service_get(fs_node_t *node);
104
[6c501f8]105/*
106 * Static variables
107 */
108static LIST_INITIALIZE(instance_list);
109static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
[3711e7e]110static hash_table_t open_nodes;
[6c501f8]111static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
112
[3711e7e]113/* Hash table interface for open nodes hash table */
114static hash_index_t open_nodes_hash(unsigned long key[])
115{
116 /* TODO: This is very simple and probably can be improved */
117 return key[OPEN_NODES_INODE_KEY] % OPEN_NODES_BUCKETS;
118}
119
120static int open_nodes_compare(unsigned long key[], hash_count_t keys,
121 link_t *item)
122{
123 ext4fs_node_t *enode = hash_table_get_instance(item, ext4fs_node_t, link);
124 assert(keys > 0);
125 if (enode->instance->service_id !=
126 ((service_id_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
127 return false;
128 }
129 if (keys == 1) {
130 return true;
131 }
132 assert(keys == 2);
133 return (enode->inode_ref->index == key[OPEN_NODES_INODE_KEY]);
134}
135
136static void open_nodes_remove_cb(link_t *link)
137{
138 /* We don't use remove callback for this hash table */
139}
140
141static hash_table_operations_t open_nodes_ops = {
142 .hash = open_nodes_hash,
143 .compare = open_nodes_compare,
144 .remove_callback = open_nodes_remove_cb,
145};
146
[6c501f8]147
[d3a9ae74]148int ext4fs_global_init(void)
149{
[3711e7e]150 if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
151 OPEN_NODES_KEYS, &open_nodes_ops)) {
152 return ENOMEM;
153 }
[d3a9ae74]154 return EOK;
155}
156
[3711e7e]157
[d3a9ae74]158int ext4fs_global_fini(void)
159{
[3711e7e]160 hash_table_destroy(&open_nodes);
[d3a9ae74]161 return EOK;
162}
163
164
165/*
166 * EXT4 libfs operations.
167 */
168
[6c501f8]169int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
170{
171 fibril_mutex_lock(&instance_list_mutex);
172
173 if (list_empty(&instance_list)) {
174 fibril_mutex_unlock(&instance_list_mutex);
175 return EINVAL;
176 }
177
[5614c7f]178 ext4fs_instance_t *tmp;
[6c501f8]179 list_foreach(instance_list, link) {
180 tmp = list_get_instance(link, ext4fs_instance_t, link);
181
182 if (tmp->service_id == service_id) {
183 *inst = tmp;
184 fibril_mutex_unlock(&instance_list_mutex);
185 return EOK;
186 }
187 }
188
189 fibril_mutex_unlock(&instance_list_mutex);
190 return EINVAL;
191}
192
[3711e7e]193
[d3a9ae74]194int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
195{
[01ab41b]196 return ext4fs_node_get(rfn, service_id, EXT4_INODE_ROOT_INDEX);
[d3a9ae74]197}
198
[3711e7e]199
[d3a9ae74]200int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
201{
[9b9d37bb]202 int rc;
203
[5614c7f]204 ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
205 ext4_filesystem_t *fs = eparent->instance->filesystem;
[9b9d37bb]206
207 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
208 EXT4_INODE_MODE_DIRECTORY)) {
209 return ENOTDIR;
210 }
211
[7689590]212 ext4_directory_search_result_t result;
213 rc = ext4_directory_find_entry(fs, &result, eparent->inode_ref, component);
[8be96a0]214 if (rc != EOK) {
[ea75ceb]215 if (rc == ENOENT) {
216 *rfn = NULL;
217 return EOK;
218 }
[8be96a0]219 return rc;
[9b9d37bb]220 }
221
[7689590]222 uint32_t inode = ext4_directory_entry_ll_get_inode(result.dentry);
[9b9d37bb]223
[8be96a0]224 rc = ext4fs_node_get_core(rfn, eparent->instance, inode);
225 if (rc != EOK) {
226 return rc;
[9b9d37bb]227 }
228
[7689590]229 rc = ext4_directory_destroy_result(&result);
230 if (rc != EOK) {
231 return rc;
232 }
233
[d3a9ae74]234 return EOK;
235}
236
[3711e7e]237
[d3a9ae74]238int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
239{
[9c0c0e1]240 int rc;
241
[5614c7f]242 ext4fs_instance_t *inst;
[9c0c0e1]243 rc = ext4fs_instance_get(service_id, &inst);
244 if (rc != EOK) {
245 return rc;
246 }
247
248 return ext4fs_node_get_core(rfn, inst, index);
[d3a9ae74]249}
250
[3711e7e]251
[6c501f8]252int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
253 fs_index_t index)
254{
[3711e7e]255 int rc;
256
257 fibril_mutex_lock(&open_nodes_lock);
258
259 /* Check if the node is not already open */
260 unsigned long key[] = {
261 [OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
262 [OPEN_NODES_INODE_KEY] = index,
263 };
264
[5614c7f]265 link_t *already_open = hash_table_find(&open_nodes, key);
266 ext4fs_node_t *enode = NULL;
[3711e7e]267 if (already_open) {
268 enode = hash_table_get_instance(already_open, ext4fs_node_t, link);
269 *rfn = enode->fs_node;
270 enode->references++;
271
272 fibril_mutex_unlock(&open_nodes_lock);
273 return EOK;
274 }
275
276 enode = malloc(sizeof(ext4fs_node_t));
277 if (enode == NULL) {
278 fibril_mutex_unlock(&open_nodes_lock);
279 return ENOMEM;
280 }
281
[5614c7f]282 fs_node_t *fs_node = malloc(sizeof(fs_node_t));
283 if (fs_node == NULL) {
[3711e7e]284 free(enode);
285 fibril_mutex_unlock(&open_nodes_lock);
286 return ENOMEM;
287 }
[5614c7f]288 fs_node_initialize(fs_node);
[3711e7e]289
[5614c7f]290 ext4_inode_ref_t *inode_ref;
[3711e7e]291 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
292 if (rc != EOK) {
293 free(enode);
[5614c7f]294 free(fs_node);
[3711e7e]295 fibril_mutex_unlock(&open_nodes_lock);
296 return rc;
297 }
298
299 enode->inode_ref = inode_ref;
300 enode->instance = inst;
301 enode->references = 1;
[5614c7f]302 enode->fs_node = fs_node;
[3711e7e]303 link_initialize(&enode->link);
304
[5614c7f]305 fs_node->data = enode;
306 *rfn = fs_node;
[3711e7e]307
308 hash_table_insert(&open_nodes, key, &enode->link);
309 inst->open_nodes_count++;
310
311 fibril_mutex_unlock(&open_nodes_lock);
312
[6c501f8]313 return EOK;
314}
315
[3711e7e]316
[9b9d37bb]317int ext4fs_node_put_core(ext4fs_node_t *enode)
318{
319 int rc;
320 unsigned long key[] = {
321 [OPEN_NODES_DEV_HANDLE_KEY] = enode->instance->service_id,
322 [OPEN_NODES_INODE_KEY] = enode->inode_ref->index,
323 };
324
325 hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
326 assert(enode->instance->open_nodes_count > 0);
327 enode->instance->open_nodes_count--;
328
329 rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
330 if (rc != EOK) {
331 return rc;
332 }
333
334 free(enode->fs_node);
335 free(enode);
336
[9c0c0e1]337 return EOK;
338}
339
[3711e7e]340
[d3a9ae74]341int ext4fs_node_open(fs_node_t *fn)
342{
[2b9e142]343 // Stateless operation
[d3a9ae74]344 return EOK;
345}
346
347int ext4fs_node_put(fs_node_t *fn)
348{
[9c0c0e1]349 int rc;
350
351 fibril_mutex_lock(&open_nodes_lock);
352
[5614c7f]353 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[9c0c0e1]354 assert(enode->references > 0);
355 enode->references--;
356 if (enode->references == 0) {
357 rc = ext4fs_node_put_core(enode);
358 if (rc != EOK) {
359 fibril_mutex_unlock(&open_nodes_lock);
360 return rc;
361 }
362 }
363
364 fibril_mutex_unlock(&open_nodes_lock);
365
[d3a9ae74]366 return EOK;
367}
368
[3711e7e]369
[d3a9ae74]370int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
371{
[47a89fe]372 int rc;
373
374 ext4fs_node_t *enode;
375 enode = malloc(sizeof(ext4fs_node_t));
376 if (enode == NULL) {
377 return ENOMEM;
378 }
379
380 fs_node_t *fs_node;
381 fs_node = malloc(sizeof(fs_node_t));
382 if (fs_node == NULL) {
383 free(enode);
384 return ENOMEM;
385 }
386
387 ext4fs_instance_t *inst;
388 rc = ext4fs_instance_get(service_id, &inst);
389 if (rc != EOK) {
390 free(enode);
391 free(fs_node);
392 return rc;
393 }
394
395 ext4_inode_ref_t *inode_ref;
[304faab]396 rc = ext4_filesystem_alloc_inode(inst->filesystem, &inode_ref, flags);
[47a89fe]397 if (rc != EOK) {
398 free(enode);
399 free(fs_node);
400 return rc;
401 }
[9b9d37bb]402
[47a89fe]403 enode->inode_ref = inode_ref;
404 enode->instance = inst;
405 enode->references = 1;
406
407 link_initialize(&enode->link);
408
409 unsigned long key[] = {
410 [OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
411 [OPEN_NODES_INODE_KEY] = inode_ref->index,
412 };
413
414 fibril_mutex_lock(&open_nodes_lock);
415 hash_table_insert(&open_nodes, key, &enode->link);
416 fibril_mutex_unlock(&open_nodes_lock);
417 inst->open_nodes_count++;
418
419 enode->inode_ref->dirty = true;
420
421 fs_node_initialize(fs_node);
422 fs_node->data = enode;
423 enode->fs_node = fs_node;
424 *rfn = fs_node;
425
426 return EOK;
[d3a9ae74]427}
428
[3711e7e]429
[d3a9ae74]430int ext4fs_destroy_node(fs_node_t *fn)
431{
[3d4fd2c]432 int rc;
[9b9d37bb]433
[3d4fd2c]434 bool has_children;
435 rc = ext4fs_has_children(&has_children, fn);
436 if (rc != EOK) {
437 ext4fs_node_put(fn);
438 return rc;
439 }
440
441 if (has_children) {
442 ext4fs_node_put(fn);
443 return EINVAL;
444 }
445
446 ext4fs_node_t *enode = EXT4FS_NODE(fn);
447 ext4_filesystem_t *fs = enode->instance->filesystem;
448 ext4_inode_ref_t *inode_ref = enode->inode_ref;
449
450 rc = ext4_filesystem_truncate_inode(fs, inode_ref, 0);
451 if (rc != EOK) {
452 ext4fs_node_put(fn);
453 return rc;
454 }
455
[07fd4cd1]456 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
457 if (rev_level > 0) {
458 ext4_filesystem_delete_orphan(fs, inode_ref);
459 }
460
461 // TODO set real deletion time
462// time_t now = time(NULL);
463 time_t now = ext4_inode_get_change_inode_time(inode_ref->inode);
464 ext4_inode_set_deletion_time(inode_ref->inode, (uint32_t)now);
465 inode_ref->dirty = true;
466
[3d4fd2c]467 rc = ext4_filesystem_free_inode(fs, inode_ref);
468 if (rc != EOK) {
469 ext4fs_node_put(fn);
470 return rc;
471 }
472
473 ext4fs_node_put(fn);
474 return EOK;
[d3a9ae74]475}
476
[3711e7e]477
[d3a9ae74]478int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
479{
[47a89fe]480 int rc;
[9b9d37bb]481
[47a89fe]482 // Check maximum name length
483 if (strlen(name) > EXT4_DIRECTORY_FILENAME_LEN) {
484 return ENAMETOOLONG;
485 }
486 ext4fs_node_t *parent = EXT4FS_NODE(pfn);
487 ext4fs_node_t *child = EXT4FS_NODE(cfn);
488 ext4_filesystem_t *fs = parent->instance->filesystem;
489
490 // Add entry to parent directory
[cd1cc4e6]491 rc = ext4_directory_add_entry(fs, parent->inode_ref, name, child->inode_ref);
[47a89fe]492 if (rc != EOK) {
493 return rc;
494 }
495
496 // Fill new dir -> add '.' and '..' entries
497 if (ext4_inode_is_type(fs->superblock, child->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
498
[cd1cc4e6]499 rc = ext4_directory_add_entry(fs, child->inode_ref, ".", child->inode_ref);
[47a89fe]500 if (rc != EOK) {
501 ext4_directory_remove_entry(fs, parent->inode_ref, name);
502 return rc;
503 }
504
[cd1cc4e6]505 rc = ext4_directory_add_entry(fs, child->inode_ref, "..", parent->inode_ref);
[47a89fe]506 if (rc != EOK) {
507 ext4_directory_remove_entry(fs, parent->inode_ref, name);
508 ext4_directory_remove_entry(fs, child->inode_ref, ".");
509 return rc;
510 }
511
512 uint16_t parent_links = ext4_inode_get_links_count(parent->inode_ref->inode);
513 parent_links++;
514 ext4_inode_set_links_count(parent->inode_ref->inode, parent_links);
515
516 parent->inode_ref->dirty = true;
517
518 }
519
520 uint16_t child_links = ext4_inode_get_links_count(child->inode_ref->inode);
521 child_links++;
522 ext4_inode_set_links_count(child->inode_ref->inode, child_links);
523
524 child->inode_ref->dirty = true;
525
526 return EOK;
[d3a9ae74]527}
528
[3711e7e]529
[ebeaaa06]530int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[d3a9ae74]531{
[ebeaaa06]532 int rc;
533
534 bool has_children;
535 rc = ext4fs_has_children(&has_children, cfn);
536 if (rc != EOK) {
537 return rc;
538 }
539
540 // Cannot unlink non-empty node
541 if (has_children) {
542 return ENOTEMPTY;
543 }
544
545 // Remove entry from parent directory
[f49638e]546 ext4_inode_ref_t *parent = EXT4FS_NODE(pfn)->inode_ref;
547 ext4_filesystem_t *fs = EXT4FS_NODE(pfn)->instance->filesystem;
548 rc = ext4_directory_remove_entry(fs, parent, name);
549 if (rc != EOK) {
550 return rc;
551 }
[ebeaaa06]552
553 // Decrement links count
554 ext4_inode_ref_t * child_inode_ref = EXT4FS_NODE(cfn)->inode_ref;
555
556 uint32_t lnk_count = ext4_inode_get_links_count(child_inode_ref->inode);
557 lnk_count--;
558
559 // If directory - handle links from parent
560 if (lnk_count <= 1 && ext4fs_is_directory(cfn)) {
[1e48a07e]561
562 assert(lnk_count == 1);
563 lnk_count--;
564
[82d7816]565 ext4_inode_ref_t *parent_inode_ref = EXT4FS_NODE(pfn)->inode_ref;
566
567 uint32_t parent_lnk_count = ext4_inode_get_links_count(
568 parent_inode_ref->inode);
569
570 parent_lnk_count--;
571 ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count);
572
[07fd4cd1]573 parent->dirty = true;
574 }
575
576 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
577 if ((rev_level > 0) && (lnk_count == 0)) {
578 ext4_filesystem_add_orphan(fs, child_inode_ref);
[ebeaaa06]579 }
580
[07fd4cd1]581 // TODO set timestamps for parent (when we have wall-clock time)
582// time_t now = time(NULL);
583// ext4_inode_set_change_inode_time(parent->inode, (uint32_t)now);
584// ext4_inode_set_modification_time(parent->inode, (uint32_t)now);
585// parent->dirty = true;
586
587 // TODO set timestamp for inode
588// ext4_inode_set_change_inode_time(child_inode_ref->inode, (uint32_t)now);
[82d7816]589 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
590 child_inode_ref->dirty = true;
591
[ebeaaa06]592 return EOK;
[d3a9ae74]593}
594
[3711e7e]595
[d3a9ae74]596int ext4fs_has_children(bool *has_children, fs_node_t *fn)
597{
[e68c834]598 int rc;
599
[5614c7f]600 ext4fs_node_t *enode = EXT4FS_NODE(fn);
601 ext4_filesystem_t *fs = enode->instance->filesystem;
[e68c834]602
603 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
604 EXT4_INODE_MODE_DIRECTORY)) {
605 *has_children = false;
606 return EOK;
607 }
608
[5614c7f]609 ext4_directory_iterator_t it;
[e68c834]610 rc = ext4_directory_iterator_init(&it, fs, enode->inode_ref, 0);
611 if (rc != EOK) {
612 return rc;
613 }
614
615 /* Find a non-empty directory entry */
[5614c7f]616 bool found = false;
[e68c834]617 while (it.current != NULL) {
618 if (it.current->inode != 0) {
[5614c7f]619 uint16_t name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
[e68c834]620 it.current);
[2ea6392]621 if (!ext4fs_is_dots(it.current->name, name_size)) {
[e68c834]622 found = true;
623 break;
624 }
625 }
626
627 rc = ext4_directory_iterator_next(&it);
628 if (rc != EOK) {
629 ext4_directory_iterator_fini(&it);
630 return rc;
631 }
632 }
633
634 rc = ext4_directory_iterator_fini(&it);
635 if (rc != EOK) {
636 return rc;
637 }
638
639 *has_children = found;
[9b9d37bb]640
[d3a9ae74]641 return EOK;
642}
643
644
645fs_index_t ext4fs_index_get(fs_node_t *fn)
646{
[9b9d37bb]647 ext4fs_node_t *enode = EXT4FS_NODE(fn);
648 return enode->inode_ref->index;
[d3a9ae74]649}
650
[3711e7e]651
[d3a9ae74]652aoff64_t ext4fs_size_get(fs_node_t *fn)
653{
[9b9d37bb]654 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]655 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
656 return ext4_inode_get_size(sb, enode->inode_ref->inode);
[d3a9ae74]657}
658
[3711e7e]659
[d3a9ae74]660unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
661{
[d4d2954]662 ext4fs_node_t *enode = EXT4FS_NODE(fn);
663 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
664
[e5f8762]665 if (ext4fs_is_directory(fn)) {
666 if (lnkcnt > 1) {
667 return 1;
668 } else {
669 return 0;
670 }
671 }
672
673 // For regular files return real links count
[d4d2954]674 return lnkcnt;
[d3a9ae74]675}
676
[3711e7e]677
[d3a9ae74]678bool ext4fs_is_directory(fs_node_t *fn)
679{
[e68c834]680 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]681 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
682 return ext4_inode_is_type(
683 sb, enode->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY);
[d3a9ae74]684}
685
[3711e7e]686
[d3a9ae74]687bool ext4fs_is_file(fs_node_t *fn)
688{
[9b9d37bb]689 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]690 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
691 return ext4_inode_is_type(
692 sb, enode->inode_ref->inode, EXT4_INODE_MODE_FILE);
[d3a9ae74]693}
694
[3711e7e]695
[d3a9ae74]696service_id_t ext4fs_service_get(fs_node_t *fn)
697{
[e68c834]698 ext4fs_node_t *enode = EXT4FS_NODE(fn);
699 return enode->instance->service_id;
[d3a9ae74]700}
701
702/*
703 * libfs operations.
704 */
705libfs_ops_t ext4fs_libfs_ops = {
706 .root_get = ext4fs_root_get,
707 .match = ext4fs_match,
708 .node_get = ext4fs_node_get,
709 .node_open = ext4fs_node_open,
710 .node_put = ext4fs_node_put,
711 .create = ext4fs_create_node,
712 .destroy = ext4fs_destroy_node,
713 .link = ext4fs_link,
714 .unlink = ext4fs_unlink,
715 .has_children = ext4fs_has_children,
716 .index_get = ext4fs_index_get,
717 .size_get = ext4fs_size_get,
718 .lnkcnt_get = ext4fs_lnkcnt_get,
719 .is_directory = ext4fs_is_directory,
720 .is_file = ext4fs_is_file,
721 .service_get = ext4fs_service_get
722};
723
724
725/*
726 * VFS operations.
727 */
728
729static int ext4fs_mounted(service_id_t service_id, const char *opts,
730 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
731{
[6c501f8]732 int rc;
733
734 /* Allocate libext4 filesystem structure */
[5614c7f]735 ext4_filesystem_t *fs;
[6c501f8]736 fs = (ext4_filesystem_t *) malloc(sizeof(ext4_filesystem_t));
737 if (fs == NULL) {
738 return ENOMEM;
739 }
740
741 /* Allocate instance structure */
[5614c7f]742 ext4fs_instance_t *inst;
[6c501f8]743 inst = (ext4fs_instance_t *) malloc(sizeof(ext4fs_instance_t));
744 if (inst == NULL) {
745 free(fs);
746 return ENOMEM;
747 }
748
[9c0c0e1]749 /* Initialize the filesystem */
[6c501f8]750 rc = ext4_filesystem_init(fs, service_id);
751 if (rc != EOK) {
752 free(fs);
753 free(inst);
754 return rc;
755 }
756
757 /* Do some sanity checking */
758 rc = ext4_filesystem_check_sanity(fs);
759 if (rc != EOK) {
[ae3d4f8]760 ext4_filesystem_fini(fs, false);
[6c501f8]761 free(fs);
762 free(inst);
763 return rc;
764 }
765
766 /* Check flags */
[5614c7f]767 bool read_only;
[9c0c0e1]768 rc = ext4_filesystem_check_features(fs, &read_only);
[6c501f8]769 if (rc != EOK) {
[ae3d4f8]770 ext4_filesystem_fini(fs, false);
[6c501f8]771 free(fs);
772 free(inst);
773 return rc;
774 }
775
776 /* Initialize instance */
777 link_initialize(&inst->link);
778 inst->service_id = service_id;
779 inst->filesystem = fs;
780 inst->open_nodes_count = 0;
781
782 /* Read root node */
783 fs_node_t *root_node;
[3711e7e]784 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
[6c501f8]785 if (rc != EOK) {
[ae3d4f8]786 ext4_filesystem_fini(fs, false);
[6c501f8]787 free(fs);
788 free(inst);
789 return rc;
790 }
791
792 /* Add instance to the list */
793 fibril_mutex_lock(&instance_list_mutex);
794 list_append(&inst->link, &instance_list);
795 fibril_mutex_unlock(&instance_list_mutex);
796
797 *index = EXT4_INODE_ROOT_INDEX;
798 *size = 0;
[e5f8762]799 *lnkcnt = 1;
[6c501f8]800
801 ext4fs_node_put(root_node);
802
[d3a9ae74]803 return EOK;
804}
805
[3711e7e]806
[d3a9ae74]807static int ext4fs_unmounted(service_id_t service_id)
808{
[6c501f8]809 int rc;
810
[5614c7f]811 ext4fs_instance_t *inst;
[6c501f8]812 rc = ext4fs_instance_get(service_id, &inst);
813 if (rc != EOK) {
814 return rc;
815 }
816
817 fibril_mutex_lock(&open_nodes_lock);
818
819 if (inst->open_nodes_count != 0) {
820 fibril_mutex_unlock(&open_nodes_lock);
821 return EBUSY;
822 }
823
824 /* Remove the instance from the list */
825 fibril_mutex_lock(&instance_list_mutex);
826 list_remove(&inst->link);
827 fibril_mutex_unlock(&instance_list_mutex);
828
829 fibril_mutex_unlock(&open_nodes_lock);
830
[ae3d4f8]831 return ext4_filesystem_fini(inst->filesystem, true);
[d3a9ae74]832}
833
[3711e7e]834
[5f6cb14]835static int ext4fs_read(service_id_t service_id, fs_index_t index,
836 aoff64_t pos, size_t *rbytes)
[d3a9ae74]837{
[9b9d37bb]838 int rc;
839
840 /*
841 * Receive the read request.
842 */
843 ipc_callid_t callid;
844 size_t size;
845 if (!async_data_read_receive(&callid, &size)) {
846 async_answer_0(callid, EINVAL);
847 return EINVAL;
848 }
849
[5614c7f]850 ext4fs_instance_t *inst;
[9b9d37bb]851 rc = ext4fs_instance_get(service_id, &inst);
852 if (rc != EOK) {
853 async_answer_0(callid, rc);
854 return rc;
855 }
856
[5614c7f]857 ext4_inode_ref_t *inode_ref;
[9b9d37bb]858 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
859 if (rc != EOK) {
860 async_answer_0(callid, rc);
861 return rc;
862 }
863
864 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
865 EXT4_INODE_MODE_FILE)) {
866 rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
867 rbytes);
868 } else if (ext4_inode_is_type(inst->filesystem->superblock,
869 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
870 rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
871 rbytes);
872 } else {
873 /* Other inode types not supported */
874 async_answer_0(callid, ENOTSUP);
875 rc = ENOTSUP;
876 }
877
878 ext4_filesystem_put_inode_ref(inode_ref);
[8958a26]879
[9b9d37bb]880 return rc;
881}
882
[8958a26]883bool ext4fs_is_dots(const uint8_t *name, size_t name_size)
884{
[9b9d37bb]885 if (name_size == 1 && name[0] == '.') {
886 return true;
887 }
888
889 if (name_size == 2 && name[0] == '.' && name[1] == '.') {
890 return true;
891 }
892
893 return false;
894}
895
896int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
897 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
898{
899 int rc;
900
[5614c7f]901 ext4_directory_iterator_t it;
[9b9d37bb]902 rc = ext4_directory_iterator_init(&it, inst->filesystem, inode_ref, pos);
903 if (rc != EOK) {
904 async_answer_0(callid, rc);
905 return rc;
906 }
907
908 /* Find next interesting directory entry.
909 * We want to skip . and .. entries
910 * as these are not used in HelenOS
911 */
[5614c7f]912 bool found = false;
[9b9d37bb]913 while (it.current != NULL) {
[e68c834]914
[9b9d37bb]915 if (it.current->inode == 0) {
916 goto skip;
917 }
918
[5614c7f]919 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
[9b9d37bb]920 inst->filesystem->superblock, it.current);
921
922 /* skip . and .. */
[2ea6392]923 if (ext4fs_is_dots(it.current->name, name_size)) {
[9b9d37bb]924 goto skip;
925 }
926
927 /* The on-disk entry does not contain \0 at the end
928 * end of entry name, so we copy it to new buffer
929 * and add the \0 at the end
930 */
[5614c7f]931 uint8_t *buf = malloc(name_size+1);
[9b9d37bb]932 if (buf == NULL) {
933 ext4_directory_iterator_fini(&it);
934 async_answer_0(callid, ENOMEM);
935 return ENOMEM;
936 }
937 memcpy(buf, &it.current->name, name_size);
938 *(buf + name_size) = 0;
939 found = true;
940 (void) async_data_read_finalize(callid, buf, name_size + 1);
941 free(buf);
942 break;
943
944skip:
945 rc = ext4_directory_iterator_next(&it);
946 if (rc != EOK) {
947 ext4_directory_iterator_fini(&it);
948 async_answer_0(callid, rc);
949 return rc;
950 }
951 }
952
[5614c7f]953 uint64_t next;
[9b9d37bb]954 if (found) {
955 rc = ext4_directory_iterator_next(&it);
[8958a26]956 if (rc != EOK) {
[9b9d37bb]957 return rc;
[8958a26]958 }
[9b9d37bb]959 next = it.current_offset;
960 }
961
962 rc = ext4_directory_iterator_fini(&it);
[8958a26]963 if (rc != EOK) {
[9b9d37bb]964 return rc;
[8958a26]965 }
[9b9d37bb]966
967 if (found) {
968 *rbytes = next - pos;
969 return EOK;
970 } else {
971 async_answer_0(callid, ENOENT);
972 return ENOENT;
973 }
[d3a9ae74]974}
975
[9b9d37bb]976int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
977 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
978{
979 int rc;
980
[5614c7f]981 ext4_superblock_t *sb = inst->filesystem->superblock;
982 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
[9b9d37bb]983
984 if (pos >= file_size) {
985 /* Read 0 bytes successfully */
986 async_data_read_finalize(callid, NULL, 0);
987 *rbytes = 0;
988 return EOK;
989 }
990
991 /* For now, we only read data from one block at a time */
[5614c7f]992 uint32_t block_size = ext4_superblock_get_block_size(sb);
993 aoff64_t file_block = pos / block_size;
994 uint32_t offset_in_block = pos % block_size;
995 uint32_t bytes = min(block_size - offset_in_block, size);
[9b9d37bb]996
997 /* Handle end of file */
998 if (pos + bytes > file_size) {
999 bytes = file_size - pos;
1000 }
1001
1002 /* Get the real block number */
[5614c7f]1003 uint32_t fs_block;
[9b9d37bb]1004 rc = ext4_filesystem_get_inode_data_block_index(inst->filesystem,
1005 inode_ref->inode, file_block, &fs_block);
1006 if (rc != EOK) {
1007 async_answer_0(callid, rc);
1008 return rc;
1009 }
1010
1011 /* Check for sparse file
[2b9e142]1012 * If ext4_filesystem_get_inode_data_block_index returned
[9b9d37bb]1013 * fs_block == 0, it means that the given block is not allocated for the
1014 * file and we need to return a buffer of zeros
1015 */
[5614c7f]1016 uint8_t *buffer;
[9b9d37bb]1017 if (fs_block == 0) {
1018 buffer = malloc(bytes);
1019 if (buffer == NULL) {
1020 async_answer_0(callid, ENOMEM);
1021 return ENOMEM;
1022 }
1023
1024 memset(buffer, 0, bytes);
1025
1026 async_data_read_finalize(callid, buffer, bytes);
1027 *rbytes = bytes;
1028
1029 free(buffer);
1030
1031 return EOK;
1032 }
1033
1034 /* Usual case - we need to read a block from device */
[5614c7f]1035 block_t *block;
[9b9d37bb]1036 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1037 if (rc != EOK) {
1038 async_answer_0(callid, rc);
1039 return rc;
1040 }
1041
1042 assert(offset_in_block + bytes <= block_size);
1043 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
1044
1045 rc = block_put(block);
[8958a26]1046 if (rc != EOK) {
[9b9d37bb]1047 return rc;
[8958a26]1048 }
[9b9d37bb]1049
1050 *rbytes = bytes;
1051 return EOK;
1052}
[3711e7e]1053
[5f6cb14]1054static int ext4fs_write(service_id_t service_id, fs_index_t index,
1055 aoff64_t pos, size_t *wbytes, aoff64_t *nsize)
[d3a9ae74]1056{
[1c1c736]1057 int rc;
1058
[5614c7f]1059 fs_node_t *fn;
[1c1c736]1060 rc = ext4fs_node_get(&fn, service_id, index);
1061 if (rc != EOK) {
1062 return rc;
1063 }
1064
[5614c7f]1065 ipc_callid_t callid;
1066 size_t len;
[1c1c736]1067 if (!async_data_write_receive(&callid, &len)) {
1068 rc = EINVAL;
1069 ext4fs_node_put(fn);
1070 async_answer_0(callid, rc);
1071 return rc;
1072 }
1073
1074
[5614c7f]1075 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1076 ext4_filesystem_t *fs = enode->instance->filesystem;
[1c1c736]1077
[5614c7f]1078 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[1c1c736]1079
1080 // Prevent writing to more than one block
[5614c7f]1081 uint32_t bytes = min(len, block_size - (pos % block_size));
[1c1c736]1082
[5614c7f]1083 int flags = BLOCK_FLAGS_NONE;
[35f48f2]1084 if (bytes == block_size) {
1085 flags = BLOCK_FLAGS_NOREAD;
1086 }
1087
[5614c7f]1088 uint32_t iblock = pos / block_size;
1089 uint32_t fblock;
[1c1c736]1090
[5614c7f]1091 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[1c1c736]1092 rc = ext4_filesystem_get_inode_data_block_index(fs, inode_ref->inode, iblock, &fblock);
[6088193]1093 if (rc != EOK) {
1094 ext4fs_node_put(fn);
[e63ce679]1095 async_answer_0(callid, rc);
[6088193]1096 return rc;
1097 }
[1c1c736]1098
1099 if (fblock == 0) {
[b12ca16]1100 rc = ext4_balloc_alloc_block(fs, inode_ref, &fblock);
[35f48f2]1101 if (rc != EOK) {
1102 ext4fs_node_put(fn);
1103 async_answer_0(callid, rc);
1104 return rc;
1105 }
1106
[b12ca16]1107 rc = ext4_filesystem_set_inode_data_block_index(fs, inode_ref, iblock, fblock);
1108 if (rc != EOK) {
[ca3d77a]1109 ext4_balloc_free_block(fs, inode_ref, fblock);
1110 ext4fs_node_put(fn);
1111 async_answer_0(callid, rc);
1112 return rc;
[b12ca16]1113 }
[35f48f2]1114 inode_ref->dirty = true;
1115
1116 flags = BLOCK_FLAGS_NOREAD;
[1c1c736]1117 }
1118
[5614c7f]1119 block_t *write_block;
[35f48f2]1120 rc = block_get(&write_block, service_id, fblock, flags);
[1c1c736]1121 if (rc != EOK) {
1122 ext4fs_node_put(fn);
1123 async_answer_0(callid, rc);
1124 return rc;
1125 }
1126
[35f48f2]1127 if (flags == BLOCK_FLAGS_NOREAD) {
1128 memset(write_block->data, 0, block_size);
1129 }
1130
1131 rc = async_data_write_finalize(callid, write_block->data + (pos % block_size), bytes);
1132 if (rc != EOK) {
[cd00f93]1133 ext4fs_node_put(fn);
1134 return rc;
[35f48f2]1135 }
1136
[1c1c736]1137 write_block->dirty = true;
1138
1139 rc = block_put(write_block);
1140 if (rc != EOK) {
1141 ext4fs_node_put(fn);
1142 return rc;
1143 }
1144
[5614c7f]1145 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[35f48f2]1146 if (pos + bytes > old_inode_size) {
1147 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1148 inode_ref->dirty = true;
[1c1c736]1149 }
[35f48f2]1150
1151 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[1c1c736]1152 *wbytes = bytes;
[35f48f2]1153
[1c1c736]1154 return ext4fs_node_put(fn);
[d3a9ae74]1155}
1156
[3711e7e]1157
[5f6cb14]1158static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
1159 aoff64_t new_size)
[d3a9ae74]1160{
[d5a78e28]1161 int rc;
[e68c834]1162
[5614c7f]1163 fs_node_t *fn;
[d5a78e28]1164 rc = ext4fs_node_get(&fn, service_id, index);
1165 if (rc != EOK) {
1166 return rc;
1167 }
1168
[3d4fd2c]1169 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1170 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1171 ext4_filesystem_t *fs = enode->instance->filesystem;
[d5a78e28]1172
[3d4fd2c]1173 rc = ext4_filesystem_truncate_inode(fs, inode_ref, new_size);
[d5a78e28]1174 ext4fs_node_put(fn);
[1c1c736]1175
[3d4fd2c]1176 return rc;
[d3a9ae74]1177}
1178
[3711e7e]1179
[d3a9ae74]1180static int ext4fs_close(service_id_t service_id, fs_index_t index)
1181{
1182 return EOK;
1183}
1184
[3711e7e]1185
[d3a9ae74]1186static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1187{
[8be96a0]1188 int rc;
[e68c834]1189
[5614c7f]1190 fs_node_t *fn;
[8be96a0]1191 rc = ext4fs_node_get(&fn, service_id, index);
1192 if (rc != EOK) {
1193 return rc;
1194 }
1195
1196 /* Destroy the inode */
1197 return ext4fs_destroy_node(fn);
[d3a9ae74]1198}
1199
[3711e7e]1200
[d3a9ae74]1201static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1202{
[35f48f2]1203 int rc;
1204
[5614c7f]1205 fs_node_t *fn;
[35f48f2]1206 rc = ext4fs_node_get(&fn, service_id, index);
1207 if (rc != EOK) {
1208 return rc;
1209 }
1210
[5614c7f]1211 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[35f48f2]1212 enode->inode_ref->dirty = true;
1213
1214 return ext4fs_node_put(fn);
[d3a9ae74]1215}
1216
1217vfs_out_ops_t ext4fs_ops = {
1218 .mounted = ext4fs_mounted,
1219 .unmounted = ext4fs_unmounted,
1220 .read = ext4fs_read,
1221 .write = ext4fs_write,
1222 .truncate = ext4fs_truncate,
1223 .close = ext4fs_close,
1224 .destroy = ext4fs_destroy,
1225 .sync = ext4fs_sync,
1226};
1227
1228/**
1229 * @}
[2b9e142]1230 */
Note: See TracBrowser for help on using the repository browser.