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

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

bugfixes of inode allocation and link operation

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