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
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(fs, &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(fs, 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(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
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;
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(fs, 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(fs, child->inode_ref, ".", child->inode_ref);
500 if (rc != EOK) {
501 ext4_directory_remove_entry(fs, parent->inode_ref, name);
502 return rc;
503 }
504
505 rc = ext4_directory_add_entry(fs, child->inode_ref, "..", parent->inode_ref);
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;
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(fs, 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(fs, 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(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
1012 * If ext4_filesystem_get_inode_data_block_index returned
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 */
1016 uint8_t *buffer;
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 */
1035 block_t *block;
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);
1046 if (rc != EOK) {
1047 return rc;
1048 }
1049
1050 *rbytes = bytes;
1051 return EOK;
1052}
1053
1054static int ext4fs_write(service_id_t service_id, fs_index_t index,
1055 aoff64_t pos, size_t *wbytes, aoff64_t *nsize)
1056{
1057 int rc;
1058
1059 fs_node_t *fn;
1060 rc = ext4fs_node_get(&fn, service_id, index);
1061 if (rc != EOK) {
1062 return rc;
1063 }
1064
1065 ipc_callid_t callid;
1066 size_t len;
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
1075 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1076 ext4_filesystem_t *fs = enode->instance->filesystem;
1077
1078 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1079
1080 // Prevent writing to more than one block
1081 uint32_t bytes = min(len, block_size - (pos % block_size));
1082
1083 int flags = BLOCK_FLAGS_NONE;
1084 if (bytes == block_size) {
1085 flags = BLOCK_FLAGS_NOREAD;
1086 }
1087
1088 uint32_t iblock = pos / block_size;
1089 uint32_t fblock;
1090
1091 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1092 rc = ext4_filesystem_get_inode_data_block_index(fs, inode_ref->inode, iblock, &fblock);
1093 if (rc != EOK) {
1094 ext4fs_node_put(fn);
1095 async_answer_0(callid, rc);
1096 return rc;
1097 }
1098
1099 if (fblock == 0) {
1100 rc = ext4_balloc_alloc_block(fs, inode_ref, &fblock);
1101 if (rc != EOK) {
1102 ext4fs_node_put(fn);
1103 async_answer_0(callid, rc);
1104 return rc;
1105 }
1106
1107 rc = ext4_filesystem_set_inode_data_block_index(fs, inode_ref, iblock, fblock);
1108 if (rc != EOK) {
1109 ext4_balloc_free_block(fs, inode_ref, fblock);
1110 ext4fs_node_put(fn);
1111 async_answer_0(callid, rc);
1112 return rc;
1113 }
1114 inode_ref->dirty = true;
1115
1116 flags = BLOCK_FLAGS_NOREAD;
1117 }
1118
1119 block_t *write_block;
1120 rc = block_get(&write_block, service_id, fblock, flags);
1121 if (rc != EOK) {
1122 ext4fs_node_put(fn);
1123 async_answer_0(callid, rc);
1124 return rc;
1125 }
1126
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) {
1133 ext4fs_node_put(fn);
1134 return rc;
1135 }
1136
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
1145 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1146 if (pos + bytes > old_inode_size) {
1147 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1148 inode_ref->dirty = true;
1149 }
1150
1151 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
1152 *wbytes = bytes;
1153
1154 return ext4fs_node_put(fn);
1155}
1156
1157
1158static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
1159 aoff64_t new_size)
1160{
1161 int rc;
1162
1163 fs_node_t *fn;
1164 rc = ext4fs_node_get(&fn, service_id, index);
1165 if (rc != EOK) {
1166 return rc;
1167 }
1168
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;
1172
1173 rc = ext4_filesystem_truncate_inode(fs, inode_ref, new_size);
1174 ext4fs_node_put(fn);
1175
1176 return rc;
1177}
1178
1179
1180static int ext4fs_close(service_id_t service_id, fs_index_t index)
1181{
1182 return EOK;
1183}
1184
1185
1186static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1187{
1188 int rc;
1189
1190 fs_node_t *fn;
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);
1198}
1199
1200
1201static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1202{
1203 int rc;
1204
1205 fs_node_t *fn;
1206 rc = ext4fs_node_get(&fn, service_id, index);
1207 if (rc != EOK) {
1208 return rc;
1209 }
1210
1211 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1212 enode->inode_ref->dirty = true;
1213
1214 return ext4fs_node_put(fn);
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 * @}
1230 */
Note: See TracBrowser for help on using the repository browser.