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

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

Skeletons for link and create_node operations

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