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

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

not debugged version of dir index initialization

  • Property mode set to 100644
File size: 28.2 KB
Line 
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>
39#include <fibril_synch.h>
40#include <libext4.h>
41#include <libfs.h>
42#include <macros.h>
43#include <malloc.h>
44#include <string.h>
45#include <adt/hash_table.h>
46#include <ipc/loc.h>
47#include "ext4fs.h"
48#include "../../vfs/vfs.h"
49
50#define EXT4FS_NODE(node) ((node) ? (ext4fs_node_t *) (node)->data : NULL)
51
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
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 */
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);
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);
83static int ext4fs_node_put_core(ext4fs_node_t *);
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
105/*
106 * Static variables
107 */
108static LIST_INITIALIZE(instance_list);
109static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
110static hash_table_t open_nodes;
111static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
112
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
147
148int ext4fs_global_init(void)
149{
150 if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
151 OPEN_NODES_KEYS, &open_nodes_ops)) {
152 return ENOMEM;
153 }
154 return EOK;
155}
156
157
158int ext4fs_global_fini(void)
159{
160 hash_table_destroy(&open_nodes);
161 return EOK;
162}
163
164
165/*
166 * EXT4 libfs operations.
167 */
168
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
178 ext4fs_instance_t *tmp;
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
193
194int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
195{
196 return ext4fs_node_get(rfn, service_id, EXT4_INODE_ROOT_INDEX);
197}
198
199
200int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
201{
202 int rc;
203
204 ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
205 ext4_filesystem_t *fs = eparent->instance->filesystem;
206
207 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
208 EXT4_INODE_MODE_DIRECTORY)) {
209 return ENOTDIR;
210 }
211
212 ext4_directory_search_result_t result;
213 rc = ext4_directory_find_entry(&result, eparent->inode_ref, component);
214 if (rc != EOK) {
215 if (rc == ENOENT) {
216 *rfn = NULL;
217 return EOK;
218 }
219 return rc;
220 }
221
222 uint32_t inode = ext4_directory_entry_ll_get_inode(result.dentry);
223
224 rc = ext4fs_node_get_core(rfn, eparent->instance, inode);
225 if (rc != EOK) {
226 return rc;
227 }
228
229 rc = ext4_directory_destroy_result(&result);
230 if (rc != EOK) {
231 return rc;
232 }
233
234 return EOK;
235}
236
237
238int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
239{
240 int rc;
241
242 ext4fs_instance_t *inst;
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);
249}
250
251
252int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
253 fs_index_t index)
254{
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
265 link_t *already_open = hash_table_find(&open_nodes, key);
266 ext4fs_node_t *enode = NULL;
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
282 fs_node_t *fs_node = malloc(sizeof(fs_node_t));
283 if (fs_node == NULL) {
284 free(enode);
285 fibril_mutex_unlock(&open_nodes_lock);
286 return ENOMEM;
287 }
288 fs_node_initialize(fs_node);
289
290 ext4_inode_ref_t *inode_ref;
291 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
292 if (rc != EOK) {
293 free(enode);
294 free(fs_node);
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;
302 enode->fs_node = fs_node;
303 link_initialize(&enode->link);
304
305 fs_node->data = enode;
306 *rfn = fs_node;
307
308 hash_table_insert(&open_nodes, key, &enode->link);
309 inst->open_nodes_count++;
310
311 fibril_mutex_unlock(&open_nodes_lock);
312
313 return EOK;
314}
315
316
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
337 return EOK;
338}
339
340
341int ext4fs_node_open(fs_node_t *fn)
342{
343 // Stateless operation
344 return EOK;
345}
346
347int ext4fs_node_put(fs_node_t *fn)
348{
349 int rc;
350
351 fibril_mutex_lock(&open_nodes_lock);
352
353 ext4fs_node_t *enode = EXT4FS_NODE(fn);
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
366 return EOK;
367}
368
369
370int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
371{
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;
396 rc = ext4_filesystem_alloc_inode(inst->filesystem, &inode_ref, flags);
397 if (rc != EOK) {
398 free(enode);
399 free(fs_node);
400 return rc;
401 }
402
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;
427}
428
429
430int ext4fs_destroy_node(fs_node_t *fn)
431{
432 int rc;
433
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(inode_ref, 0);
451 if (rc != EOK) {
452 ext4fs_node_put(fn);
453 return rc;
454 }
455
456 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
457 if (rev_level > 0) {
458 ext4_filesystem_delete_orphan(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
467 rc = ext4_filesystem_free_inode(inode_ref);
468 if (rc != EOK) {
469 ext4fs_node_put(fn);
470 return rc;
471 }
472
473 ext4fs_node_put(fn);
474 return EOK;
475}
476
477
478int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
479{
480 int rc;
481
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
491 rc = ext4_directory_add_entry(parent->inode_ref, name, child->inode_ref);
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
499 rc = ext4_directory_add_entry(child->inode_ref, ".", child->inode_ref);
500 if (rc != EOK) {
501 ext4_directory_remove_entry(parent->inode_ref, name);
502 return rc;
503 }
504
505 rc = ext4_directory_add_entry(child->inode_ref, "..", parent->inode_ref);
506 if (rc != EOK) {
507 ext4_directory_remove_entry(parent->inode_ref, name);
508 ext4_directory_remove_entry(child->inode_ref, ".");
509 return rc;
510 }
511
512// if (ext4_superblock_has_feature_compatible(
513// fs->superblock, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
514//
515// rc = ext4_directory_dx_init(child->inode_ref);
516// if (rc != EOK) {
517// return rc;
518// }
519//
520// ext4_inode_set_flag(child->inode_ref->inode, EXT4_INODE_FLAG_INDEX);
521// child->inode_ref->dirty = true;
522// }
523
524 uint16_t parent_links = ext4_inode_get_links_count(parent->inode_ref->inode);
525 parent_links++;
526 ext4_inode_set_links_count(parent->inode_ref->inode, parent_links);
527
528 parent->inode_ref->dirty = true;
529
530 }
531
532 uint16_t child_links = ext4_inode_get_links_count(child->inode_ref->inode);
533 child_links++;
534 ext4_inode_set_links_count(child->inode_ref->inode, child_links);
535
536 child->inode_ref->dirty = true;
537
538 return EOK;
539}
540
541
542int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
543{
544 int rc;
545
546 bool has_children;
547 rc = ext4fs_has_children(&has_children, cfn);
548 if (rc != EOK) {
549 return rc;
550 }
551
552 // Cannot unlink non-empty node
553 if (has_children) {
554 return ENOTEMPTY;
555 }
556
557 // Remove entry from parent directory
558 ext4_inode_ref_t *parent = EXT4FS_NODE(pfn)->inode_ref;
559 ext4_filesystem_t *fs = EXT4FS_NODE(pfn)->instance->filesystem;
560 rc = ext4_directory_remove_entry(parent, name);
561 if (rc != EOK) {
562 return rc;
563 }
564
565 // Decrement links count
566 ext4_inode_ref_t * child_inode_ref = EXT4FS_NODE(cfn)->inode_ref;
567
568 uint32_t lnk_count = ext4_inode_get_links_count(child_inode_ref->inode);
569 lnk_count--;
570
571 // If directory - handle links from parent
572 if (lnk_count <= 1 && ext4fs_is_directory(cfn)) {
573
574 assert(lnk_count == 1);
575 lnk_count--;
576
577 ext4_inode_ref_t *parent_inode_ref = EXT4FS_NODE(pfn)->inode_ref;
578
579 uint32_t parent_lnk_count = ext4_inode_get_links_count(
580 parent_inode_ref->inode);
581
582 parent_lnk_count--;
583 ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count);
584
585 parent->dirty = true;
586 }
587
588 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
589 if ((rev_level > 0) && (lnk_count == 0)) {
590 ext4_filesystem_add_orphan(child_inode_ref);
591 }
592
593 // TODO set timestamps for parent (when we have wall-clock time)
594// time_t now = time(NULL);
595// ext4_inode_set_change_inode_time(parent->inode, (uint32_t)now);
596// ext4_inode_set_modification_time(parent->inode, (uint32_t)now);
597// parent->dirty = true;
598
599 // TODO set timestamp for inode
600// ext4_inode_set_change_inode_time(child_inode_ref->inode, (uint32_t)now);
601 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
602 child_inode_ref->dirty = true;
603
604 return EOK;
605}
606
607
608int ext4fs_has_children(bool *has_children, fs_node_t *fn)
609{
610 int rc;
611
612 ext4fs_node_t *enode = EXT4FS_NODE(fn);
613 ext4_filesystem_t *fs = enode->instance->filesystem;
614
615 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
616 EXT4_INODE_MODE_DIRECTORY)) {
617 *has_children = false;
618 return EOK;
619 }
620
621 ext4_directory_iterator_t it;
622 rc = ext4_directory_iterator_init(&it, fs, enode->inode_ref, 0);
623 if (rc != EOK) {
624 return rc;
625 }
626
627 /* Find a non-empty directory entry */
628 bool found = false;
629 while (it.current != NULL) {
630 if (it.current->inode != 0) {
631 uint16_t name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
632 it.current);
633 if (!ext4fs_is_dots(it.current->name, name_size)) {
634 found = true;
635 break;
636 }
637 }
638
639 rc = ext4_directory_iterator_next(&it);
640 if (rc != EOK) {
641 ext4_directory_iterator_fini(&it);
642 return rc;
643 }
644 }
645
646 rc = ext4_directory_iterator_fini(&it);
647 if (rc != EOK) {
648 return rc;
649 }
650
651 *has_children = found;
652
653 return EOK;
654}
655
656
657fs_index_t ext4fs_index_get(fs_node_t *fn)
658{
659 ext4fs_node_t *enode = EXT4FS_NODE(fn);
660 return enode->inode_ref->index;
661}
662
663
664aoff64_t ext4fs_size_get(fs_node_t *fn)
665{
666 ext4fs_node_t *enode = EXT4FS_NODE(fn);
667 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
668 return ext4_inode_get_size(sb, enode->inode_ref->inode);
669}
670
671
672unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
673{
674 ext4fs_node_t *enode = EXT4FS_NODE(fn);
675 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
676
677 if (ext4fs_is_directory(fn)) {
678 if (lnkcnt > 1) {
679 return 1;
680 } else {
681 return 0;
682 }
683 }
684
685 // For regular files return real links count
686 return lnkcnt;
687}
688
689
690bool ext4fs_is_directory(fs_node_t *fn)
691{
692 ext4fs_node_t *enode = EXT4FS_NODE(fn);
693 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
694 return ext4_inode_is_type(
695 sb, enode->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY);
696}
697
698
699bool ext4fs_is_file(fs_node_t *fn)
700{
701 ext4fs_node_t *enode = EXT4FS_NODE(fn);
702 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
703 return ext4_inode_is_type(
704 sb, enode->inode_ref->inode, EXT4_INODE_MODE_FILE);
705}
706
707
708service_id_t ext4fs_service_get(fs_node_t *fn)
709{
710 ext4fs_node_t *enode = EXT4FS_NODE(fn);
711 return enode->instance->service_id;
712}
713
714/*
715 * libfs operations.
716 */
717libfs_ops_t ext4fs_libfs_ops = {
718 .root_get = ext4fs_root_get,
719 .match = ext4fs_match,
720 .node_get = ext4fs_node_get,
721 .node_open = ext4fs_node_open,
722 .node_put = ext4fs_node_put,
723 .create = ext4fs_create_node,
724 .destroy = ext4fs_destroy_node,
725 .link = ext4fs_link,
726 .unlink = ext4fs_unlink,
727 .has_children = ext4fs_has_children,
728 .index_get = ext4fs_index_get,
729 .size_get = ext4fs_size_get,
730 .lnkcnt_get = ext4fs_lnkcnt_get,
731 .is_directory = ext4fs_is_directory,
732 .is_file = ext4fs_is_file,
733 .service_get = ext4fs_service_get
734};
735
736
737/*
738 * VFS operations.
739 */
740
741static int ext4fs_mounted(service_id_t service_id, const char *opts,
742 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
743{
744 int rc;
745
746 /* Allocate libext4 filesystem structure */
747 ext4_filesystem_t *fs;
748 fs = (ext4_filesystem_t *) malloc(sizeof(ext4_filesystem_t));
749 if (fs == NULL) {
750 return ENOMEM;
751 }
752
753 /* Allocate instance structure */
754 ext4fs_instance_t *inst;
755 inst = (ext4fs_instance_t *) malloc(sizeof(ext4fs_instance_t));
756 if (inst == NULL) {
757 free(fs);
758 return ENOMEM;
759 }
760
761 /* Initialize the filesystem */
762 rc = ext4_filesystem_init(fs, service_id);
763 if (rc != EOK) {
764 free(fs);
765 free(inst);
766 return rc;
767 }
768
769 /* Do some sanity checking */
770 rc = ext4_filesystem_check_sanity(fs);
771 if (rc != EOK) {
772 ext4_filesystem_fini(fs, false);
773 free(fs);
774 free(inst);
775 return rc;
776 }
777
778 /* Check flags */
779 bool read_only;
780 rc = ext4_filesystem_check_features(fs, &read_only);
781 if (rc != EOK) {
782 ext4_filesystem_fini(fs, false);
783 free(fs);
784 free(inst);
785 return rc;
786 }
787
788 /* Initialize instance */
789 link_initialize(&inst->link);
790 inst->service_id = service_id;
791 inst->filesystem = fs;
792 inst->open_nodes_count = 0;
793
794 /* Read root node */
795 fs_node_t *root_node;
796 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
797 if (rc != EOK) {
798 ext4_filesystem_fini(fs, false);
799 free(fs);
800 free(inst);
801 return rc;
802 }
803
804 /* Add instance to the list */
805 fibril_mutex_lock(&instance_list_mutex);
806 list_append(&inst->link, &instance_list);
807 fibril_mutex_unlock(&instance_list_mutex);
808
809 *index = EXT4_INODE_ROOT_INDEX;
810 *size = 0;
811 *lnkcnt = 1;
812
813 ext4fs_node_put(root_node);
814
815 return EOK;
816}
817
818
819static int ext4fs_unmounted(service_id_t service_id)
820{
821 int rc;
822
823 ext4fs_instance_t *inst;
824 rc = ext4fs_instance_get(service_id, &inst);
825 if (rc != EOK) {
826 return rc;
827 }
828
829 fibril_mutex_lock(&open_nodes_lock);
830
831 if (inst->open_nodes_count != 0) {
832 fibril_mutex_unlock(&open_nodes_lock);
833 return EBUSY;
834 }
835
836 /* Remove the instance from the list */
837 fibril_mutex_lock(&instance_list_mutex);
838 list_remove(&inst->link);
839 fibril_mutex_unlock(&instance_list_mutex);
840
841 fibril_mutex_unlock(&open_nodes_lock);
842
843 return ext4_filesystem_fini(inst->filesystem, true);
844}
845
846
847static int ext4fs_read(service_id_t service_id, fs_index_t index,
848 aoff64_t pos, size_t *rbytes)
849{
850 int rc;
851
852 /*
853 * Receive the read request.
854 */
855 ipc_callid_t callid;
856 size_t size;
857 if (!async_data_read_receive(&callid, &size)) {
858 async_answer_0(callid, EINVAL);
859 return EINVAL;
860 }
861
862 ext4fs_instance_t *inst;
863 rc = ext4fs_instance_get(service_id, &inst);
864 if (rc != EOK) {
865 async_answer_0(callid, rc);
866 return rc;
867 }
868
869 ext4_inode_ref_t *inode_ref;
870 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
871 if (rc != EOK) {
872 async_answer_0(callid, rc);
873 return rc;
874 }
875
876 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
877 EXT4_INODE_MODE_FILE)) {
878 rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
879 rbytes);
880 } else if (ext4_inode_is_type(inst->filesystem->superblock,
881 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
882 rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
883 rbytes);
884 } else {
885 /* Other inode types not supported */
886 async_answer_0(callid, ENOTSUP);
887 rc = ENOTSUP;
888 }
889
890 ext4_filesystem_put_inode_ref(inode_ref);
891
892 return rc;
893}
894
895bool ext4fs_is_dots(const uint8_t *name, size_t name_size)
896{
897 if (name_size == 1 && name[0] == '.') {
898 return true;
899 }
900
901 if (name_size == 2 && name[0] == '.' && name[1] == '.') {
902 return true;
903 }
904
905 return false;
906}
907
908int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
909 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
910{
911 int rc;
912
913 ext4_directory_iterator_t it;
914 rc = ext4_directory_iterator_init(&it, inst->filesystem, inode_ref, pos);
915 if (rc != EOK) {
916 async_answer_0(callid, rc);
917 return rc;
918 }
919
920 /* Find next interesting directory entry.
921 * We want to skip . and .. entries
922 * as these are not used in HelenOS
923 */
924 bool found = false;
925 while (it.current != NULL) {
926
927 if (it.current->inode == 0) {
928 goto skip;
929 }
930
931 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
932 inst->filesystem->superblock, it.current);
933
934 /* skip . and .. */
935 if (ext4fs_is_dots(it.current->name, name_size)) {
936 goto skip;
937 }
938
939 /* The on-disk entry does not contain \0 at the end
940 * end of entry name, so we copy it to new buffer
941 * and add the \0 at the end
942 */
943 uint8_t *buf = malloc(name_size+1);
944 if (buf == NULL) {
945 ext4_directory_iterator_fini(&it);
946 async_answer_0(callid, ENOMEM);
947 return ENOMEM;
948 }
949 memcpy(buf, &it.current->name, name_size);
950 *(buf + name_size) = 0;
951 found = true;
952 (void) async_data_read_finalize(callid, buf, name_size + 1);
953 free(buf);
954 break;
955
956skip:
957 rc = ext4_directory_iterator_next(&it);
958 if (rc != EOK) {
959 ext4_directory_iterator_fini(&it);
960 async_answer_0(callid, rc);
961 return rc;
962 }
963 }
964
965 uint64_t next;
966 if (found) {
967 rc = ext4_directory_iterator_next(&it);
968 if (rc != EOK) {
969 return rc;
970 }
971 next = it.current_offset;
972 }
973
974 rc = ext4_directory_iterator_fini(&it);
975 if (rc != EOK) {
976 return rc;
977 }
978
979 if (found) {
980 *rbytes = next - pos;
981 return EOK;
982 } else {
983 async_answer_0(callid, ENOENT);
984 return ENOENT;
985 }
986}
987
988int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
989 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
990{
991 int rc;
992
993 ext4_superblock_t *sb = inst->filesystem->superblock;
994 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
995
996 if (pos >= file_size) {
997 /* Read 0 bytes successfully */
998 async_data_read_finalize(callid, NULL, 0);
999 *rbytes = 0;
1000 return EOK;
1001 }
1002
1003 /* For now, we only read data from one block at a time */
1004 uint32_t block_size = ext4_superblock_get_block_size(sb);
1005 aoff64_t file_block = pos / block_size;
1006 uint32_t offset_in_block = pos % block_size;
1007 uint32_t bytes = min(block_size - offset_in_block, size);
1008
1009 /* Handle end of file */
1010 if (pos + bytes > file_size) {
1011 bytes = file_size - pos;
1012 }
1013
1014 /* Get the real block number */
1015 uint32_t fs_block;
1016 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, file_block, &fs_block);
1017 if (rc != EOK) {
1018 async_answer_0(callid, rc);
1019 return rc;
1020 }
1021
1022 /* Check for sparse file
1023 * If ext4_filesystem_get_inode_data_block_index returned
1024 * fs_block == 0, it means that the given block is not allocated for the
1025 * file and we need to return a buffer of zeros
1026 */
1027 uint8_t *buffer;
1028 if (fs_block == 0) {
1029 buffer = malloc(bytes);
1030 if (buffer == NULL) {
1031 async_answer_0(callid, ENOMEM);
1032 return ENOMEM;
1033 }
1034
1035 memset(buffer, 0, bytes);
1036
1037 async_data_read_finalize(callid, buffer, bytes);
1038 *rbytes = bytes;
1039
1040 free(buffer);
1041
1042 return EOK;
1043 }
1044
1045 /* Usual case - we need to read a block from device */
1046 block_t *block;
1047 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1048 if (rc != EOK) {
1049 async_answer_0(callid, rc);
1050 return rc;
1051 }
1052
1053 assert(offset_in_block + bytes <= block_size);
1054 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
1055
1056 rc = block_put(block);
1057 if (rc != EOK) {
1058 return rc;
1059 }
1060
1061 *rbytes = bytes;
1062 return EOK;
1063}
1064
1065static int ext4fs_write(service_id_t service_id, fs_index_t index,
1066 aoff64_t pos, size_t *wbytes, aoff64_t *nsize)
1067{
1068 int rc;
1069
1070 fs_node_t *fn;
1071 rc = ext4fs_node_get(&fn, service_id, index);
1072 if (rc != EOK) {
1073 return rc;
1074 }
1075
1076 ipc_callid_t callid;
1077 size_t len;
1078 if (!async_data_write_receive(&callid, &len)) {
1079 rc = EINVAL;
1080 ext4fs_node_put(fn);
1081 async_answer_0(callid, rc);
1082 return rc;
1083 }
1084
1085
1086 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1087 ext4_filesystem_t *fs = enode->instance->filesystem;
1088
1089 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1090
1091 // Prevent writing to more than one block
1092 uint32_t bytes = min(len, block_size - (pos % block_size));
1093
1094 int flags = BLOCK_FLAGS_NONE;
1095 if (bytes == block_size) {
1096 flags = BLOCK_FLAGS_NOREAD;
1097 }
1098
1099 uint32_t iblock = pos / block_size;
1100 uint32_t fblock;
1101
1102 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1103 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, iblock, &fblock);
1104 if (rc != EOK) {
1105 ext4fs_node_put(fn);
1106 async_answer_0(callid, rc);
1107 return rc;
1108 }
1109
1110 if (fblock == 0) {
1111
1112 if (ext4_superblock_has_feature_incompatible(
1113 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1114 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1115
1116 uint32_t tmp_iblock = 0;
1117 do {
1118 rc = ext4_filesystem_append_inode_block(inode_ref, &fblock, &tmp_iblock);
1119 if (rc != EOK) {
1120 ext4fs_node_put(fn);
1121 async_answer_0(callid, rc);
1122 return rc;
1123 }
1124 } while (tmp_iblock < iblock);
1125
1126 } else {
1127 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
1128 if (rc != EOK) {
1129 ext4fs_node_put(fn);
1130 async_answer_0(callid, rc);
1131 return rc;
1132 }
1133
1134 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, iblock, fblock);
1135 if (rc != EOK) {
1136 ext4_balloc_free_block(inode_ref, fblock);
1137 ext4fs_node_put(fn);
1138 async_answer_0(callid, rc);
1139 return rc;
1140 }
1141 }
1142
1143 flags = BLOCK_FLAGS_NOREAD;
1144 inode_ref->dirty = true;
1145 }
1146
1147 block_t *write_block;
1148 rc = block_get(&write_block, service_id, fblock, flags);
1149 if (rc != EOK) {
1150 ext4fs_node_put(fn);
1151 async_answer_0(callid, rc);
1152 return rc;
1153 }
1154
1155 if (flags == BLOCK_FLAGS_NOREAD) {
1156 memset(write_block->data, 0, block_size);
1157 }
1158
1159 rc = async_data_write_finalize(callid, write_block->data + (pos % block_size), bytes);
1160 if (rc != EOK) {
1161 ext4fs_node_put(fn);
1162 return rc;
1163 }
1164
1165 write_block->dirty = true;
1166
1167 rc = block_put(write_block);
1168 if (rc != EOK) {
1169 ext4fs_node_put(fn);
1170 return rc;
1171 }
1172
1173 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1174 if (pos + bytes > old_inode_size) {
1175 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1176 inode_ref->dirty = true;
1177 }
1178
1179 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1180 *wbytes = bytes;
1181
1182 return ext4fs_node_put(fn);
1183}
1184
1185
1186static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
1187 aoff64_t new_size)
1188{
1189 int rc;
1190
1191 fs_node_t *fn;
1192 rc = ext4fs_node_get(&fn, service_id, index);
1193 if (rc != EOK) {
1194 return rc;
1195 }
1196
1197 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1198 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1199
1200 rc = ext4_filesystem_truncate_inode(inode_ref, new_size);
1201 ext4fs_node_put(fn);
1202
1203 return rc;
1204}
1205
1206
1207static int ext4fs_close(service_id_t service_id, fs_index_t index)
1208{
1209 return EOK;
1210}
1211
1212
1213static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1214{
1215 int rc;
1216
1217 fs_node_t *fn;
1218 rc = ext4fs_node_get(&fn, service_id, index);
1219 if (rc != EOK) {
1220 return rc;
1221 }
1222
1223 /* Destroy the inode */
1224 return ext4fs_destroy_node(fn);
1225}
1226
1227
1228static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1229{
1230 int rc;
1231
1232 fs_node_t *fn;
1233 rc = ext4fs_node_get(&fn, service_id, index);
1234 if (rc != EOK) {
1235 return rc;
1236 }
1237
1238 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1239 enode->inode_ref->dirty = true;
1240
1241 return ext4fs_node_put(fn);
1242}
1243
1244vfs_out_ops_t ext4fs_ops = {
1245 .mounted = ext4fs_mounted,
1246 .unmounted = ext4fs_unmounted,
1247 .read = ext4fs_read,
1248 .write = ext4fs_write,
1249 .truncate = ext4fs_truncate,
1250 .close = ext4fs_close,
1251 .destroy = ext4fs_destroy,
1252 .sync = ext4fs_sync,
1253};
1254
1255/**
1256 * @}
1257 */
Note: See TracBrowser for help on using the repository browser.