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

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

simplied headers of more functions, improved bg_ref and inode_ref structures, added block group checksumming and fixed bug in block_group values updating

  • Property mode set to 100644
File size: 27.5 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 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;
527}
528
529
530int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
531{
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
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(parent, name);
549 if (rc != EOK) {
550 return rc;
551 }
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)) {
561
562 assert(lnk_count == 1);
563 lnk_count--;
564
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
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(child_inode_ref);
579 }
580
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);
589 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
590 child_inode_ref->dirty = true;
591
592 return EOK;
593}
594
595
596int ext4fs_has_children(bool *has_children, fs_node_t *fn)
597{
598 int rc;
599
600 ext4fs_node_t *enode = EXT4FS_NODE(fn);
601 ext4_filesystem_t *fs = enode->instance->filesystem;
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
609 ext4_directory_iterator_t it;
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 */
616 bool found = false;
617 while (it.current != NULL) {
618 if (it.current->inode != 0) {
619 uint16_t name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
620 it.current);
621 if (!ext4fs_is_dots(it.current->name, name_size)) {
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;
640
641 return EOK;
642}
643
644
645fs_index_t ext4fs_index_get(fs_node_t *fn)
646{
647 ext4fs_node_t *enode = EXT4FS_NODE(fn);
648 return enode->inode_ref->index;
649}
650
651
652aoff64_t ext4fs_size_get(fs_node_t *fn)
653{
654 ext4fs_node_t *enode = EXT4FS_NODE(fn);
655 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
656 return ext4_inode_get_size(sb, enode->inode_ref->inode);
657}
658
659
660unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
661{
662 ext4fs_node_t *enode = EXT4FS_NODE(fn);
663 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
664
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
674 return lnkcnt;
675}
676
677
678bool ext4fs_is_directory(fs_node_t *fn)
679{
680 ext4fs_node_t *enode = EXT4FS_NODE(fn);
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);
684}
685
686
687bool ext4fs_is_file(fs_node_t *fn)
688{
689 ext4fs_node_t *enode = EXT4FS_NODE(fn);
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);
693}
694
695
696service_id_t ext4fs_service_get(fs_node_t *fn)
697{
698 ext4fs_node_t *enode = EXT4FS_NODE(fn);
699 return enode->instance->service_id;
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{
732 int rc;
733
734 /* Allocate libext4 filesystem structure */
735 ext4_filesystem_t *fs;
736 fs = (ext4_filesystem_t *) malloc(sizeof(ext4_filesystem_t));
737 if (fs == NULL) {
738 return ENOMEM;
739 }
740
741 /* Allocate instance structure */
742 ext4fs_instance_t *inst;
743 inst = (ext4fs_instance_t *) malloc(sizeof(ext4fs_instance_t));
744 if (inst == NULL) {
745 free(fs);
746 return ENOMEM;
747 }
748
749 /* Initialize the filesystem */
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) {
760 ext4_filesystem_fini(fs, false);
761 free(fs);
762 free(inst);
763 return rc;
764 }
765
766 /* Check flags */
767 bool read_only;
768 rc = ext4_filesystem_check_features(fs, &read_only);
769 if (rc != EOK) {
770 ext4_filesystem_fini(fs, false);
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;
784 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
785 if (rc != EOK) {
786 ext4_filesystem_fini(fs, false);
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;
799 *lnkcnt = 1;
800
801 ext4fs_node_put(root_node);
802
803 return EOK;
804}
805
806
807static int ext4fs_unmounted(service_id_t service_id)
808{
809 int rc;
810
811 ext4fs_instance_t *inst;
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
831 return ext4_filesystem_fini(inst->filesystem, true);
832}
833
834
835static int ext4fs_read(service_id_t service_id, fs_index_t index,
836 aoff64_t pos, size_t *rbytes)
837{
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
850 ext4fs_instance_t *inst;
851 rc = ext4fs_instance_get(service_id, &inst);
852 if (rc != EOK) {
853 async_answer_0(callid, rc);
854 return rc;
855 }
856
857 ext4_inode_ref_t *inode_ref;
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);
879
880 return rc;
881}
882
883bool ext4fs_is_dots(const uint8_t *name, size_t name_size)
884{
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
901 ext4_directory_iterator_t it;
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 */
912 bool found = false;
913 while (it.current != NULL) {
914
915 if (it.current->inode == 0) {
916 goto skip;
917 }
918
919 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
920 inst->filesystem->superblock, it.current);
921
922 /* skip . and .. */
923 if (ext4fs_is_dots(it.current->name, name_size)) {
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 */
931 uint8_t *buf = malloc(name_size+1);
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
953 uint64_t next;
954 if (found) {
955 rc = ext4_directory_iterator_next(&it);
956 if (rc != EOK) {
957 return rc;
958 }
959 next = it.current_offset;
960 }
961
962 rc = ext4_directory_iterator_fini(&it);
963 if (rc != EOK) {
964 return rc;
965 }
966
967 if (found) {
968 *rbytes = next - pos;
969 return EOK;
970 } else {
971 async_answer_0(callid, ENOENT);
972 return ENOENT;
973 }
974}
975
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
981 ext4_superblock_t *sb = inst->filesystem->superblock;
982 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
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 */
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);
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 */
1003 uint32_t fs_block;
1004 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, file_block, &fs_block);
1005 if (rc != EOK) {
1006 async_answer_0(callid, rc);
1007 return rc;
1008 }
1009
1010 /* Check for sparse file
1011 * If ext4_filesystem_get_inode_data_block_index returned
1012 * fs_block == 0, it means that the given block is not allocated for the
1013 * file and we need to return a buffer of zeros
1014 */
1015 uint8_t *buffer;
1016 if (fs_block == 0) {
1017 buffer = malloc(bytes);
1018 if (buffer == NULL) {
1019 async_answer_0(callid, ENOMEM);
1020 return ENOMEM;
1021 }
1022
1023 memset(buffer, 0, bytes);
1024
1025 async_data_read_finalize(callid, buffer, bytes);
1026 *rbytes = bytes;
1027
1028 free(buffer);
1029
1030 return EOK;
1031 }
1032
1033 /* Usual case - we need to read a block from device */
1034 block_t *block;
1035 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1036 if (rc != EOK) {
1037 async_answer_0(callid, rc);
1038 return rc;
1039 }
1040
1041 assert(offset_in_block + bytes <= block_size);
1042 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
1043
1044 rc = block_put(block);
1045 if (rc != EOK) {
1046 return rc;
1047 }
1048
1049 *rbytes = bytes;
1050 return EOK;
1051}
1052
1053static int ext4fs_write(service_id_t service_id, fs_index_t index,
1054 aoff64_t pos, size_t *wbytes, aoff64_t *nsize)
1055{
1056 int rc;
1057
1058 fs_node_t *fn;
1059 rc = ext4fs_node_get(&fn, service_id, index);
1060 if (rc != EOK) {
1061 return rc;
1062 }
1063
1064 ipc_callid_t callid;
1065 size_t len;
1066 if (!async_data_write_receive(&callid, &len)) {
1067 rc = EINVAL;
1068 ext4fs_node_put(fn);
1069 async_answer_0(callid, rc);
1070 return rc;
1071 }
1072
1073
1074 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1075 ext4_filesystem_t *fs = enode->instance->filesystem;
1076
1077 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1078
1079 // Prevent writing to more than one block
1080 uint32_t bytes = min(len, block_size - (pos % block_size));
1081
1082 int flags = BLOCK_FLAGS_NONE;
1083 if (bytes == block_size) {
1084 flags = BLOCK_FLAGS_NOREAD;
1085 }
1086
1087 uint32_t iblock = pos / block_size;
1088 uint32_t fblock;
1089
1090 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1091 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, iblock, &fblock);
1092 if (rc != EOK) {
1093 ext4fs_node_put(fn);
1094 async_answer_0(callid, rc);
1095 return rc;
1096 }
1097
1098 if (fblock == 0) {
1099 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
1100 if (rc != EOK) {
1101 ext4fs_node_put(fn);
1102 async_answer_0(callid, rc);
1103 return rc;
1104 }
1105
1106 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, iblock, fblock);
1107 if (rc != EOK) {
1108 ext4_balloc_free_block(inode_ref, fblock);
1109 ext4fs_node_put(fn);
1110 async_answer_0(callid, rc);
1111 return rc;
1112 }
1113 inode_ref->dirty = true;
1114
1115 flags = BLOCK_FLAGS_NOREAD;
1116 }
1117
1118 block_t *write_block;
1119 rc = block_get(&write_block, service_id, fblock, flags);
1120 if (rc != EOK) {
1121 ext4fs_node_put(fn);
1122 async_answer_0(callid, rc);
1123 return rc;
1124 }
1125
1126 if (flags == BLOCK_FLAGS_NOREAD) {
1127 memset(write_block->data, 0, block_size);
1128 }
1129
1130 rc = async_data_write_finalize(callid, write_block->data + (pos % block_size), bytes);
1131 if (rc != EOK) {
1132 ext4fs_node_put(fn);
1133 return rc;
1134 }
1135
1136 write_block->dirty = true;
1137
1138 rc = block_put(write_block);
1139 if (rc != EOK) {
1140 ext4fs_node_put(fn);
1141 return rc;
1142 }
1143
1144 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1145 if (pos + bytes > old_inode_size) {
1146 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1147 inode_ref->dirty = true;
1148 }
1149
1150 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1151 *wbytes = bytes;
1152
1153 return ext4fs_node_put(fn);
1154}
1155
1156
1157static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
1158 aoff64_t new_size)
1159{
1160 int rc;
1161
1162 fs_node_t *fn;
1163 rc = ext4fs_node_get(&fn, service_id, index);
1164 if (rc != EOK) {
1165 return rc;
1166 }
1167
1168 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1169 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1170
1171 rc = ext4_filesystem_truncate_inode(inode_ref, new_size);
1172 ext4fs_node_put(fn);
1173
1174 return rc;
1175}
1176
1177
1178static int ext4fs_close(service_id_t service_id, fs_index_t index)
1179{
1180 return EOK;
1181}
1182
1183
1184static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1185{
1186 int rc;
1187
1188 fs_node_t *fn;
1189 rc = ext4fs_node_get(&fn, service_id, index);
1190 if (rc != EOK) {
1191 return rc;
1192 }
1193
1194 /* Destroy the inode */
1195 return ext4fs_destroy_node(fn);
1196}
1197
1198
1199static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1200{
1201 int rc;
1202
1203 fs_node_t *fn;
1204 rc = ext4fs_node_get(&fn, service_id, index);
1205 if (rc != EOK) {
1206 return rc;
1207 }
1208
1209 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1210 enode->inode_ref->dirty = true;
1211
1212 return ext4fs_node_put(fn);
1213}
1214
1215vfs_out_ops_t ext4fs_ops = {
1216 .mounted = ext4fs_mounted,
1217 .unmounted = ext4fs_unmounted,
1218 .read = ext4fs_read,
1219 .write = ext4fs_write,
1220 .truncate = ext4fs_truncate,
1221 .close = ext4fs_close,
1222 .destroy = ext4fs_destroy,
1223 .sync = ext4fs_sync,
1224};
1225
1226/**
1227 * @}
1228 */
Note: See TracBrowser for help on using the repository browser.