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

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

cleaning debug some debug messages and added feature checkers

  • Property mode set to 100644
File size: 21.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 <adt/hash_table.h>
45#include <ipc/loc.h>
46#include "ext4fs.h"
47#include "../../vfs/vfs.h"
48
49#define EXT4FS_NODE(node) ((node) ? (ext4fs_node_t *) (node)->data : NULL)
50
51#define OPEN_NODES_KEYS 2
52#define OPEN_NODES_DEV_HANDLE_KEY 0
53#define OPEN_NODES_INODE_KEY 1
54#define OPEN_NODES_BUCKETS 256
55
56typedef struct ext4fs_instance {
57 link_t link;
58 service_id_t service_id;
59 ext4_filesystem_t *filesystem;
60 unsigned int open_nodes_count;
61} ext4fs_instance_t;
62
63typedef struct ext4fs_node {
64 ext4fs_instance_t *instance;
65 ext4_inode_ref_t *inode_ref;
66 fs_node_t *fs_node;
67 link_t link;
68 unsigned int references;
69} ext4fs_node_t;
70
71/*
72 * Forward declarations of auxiliary functions
73 */
74
75static int ext4fs_read_directory(ipc_callid_t, aoff64_t, size_t,
76 ext4fs_instance_t *, ext4_inode_ref_t *, size_t *);
77static int ext4fs_read_file(ipc_callid_t, aoff64_t, size_t, ext4fs_instance_t *,
78 ext4_inode_ref_t *, size_t *);
79static bool ext4fs_is_dots(const uint8_t *, size_t);
80static int ext4fs_instance_get(service_id_t, ext4fs_instance_t **);
81static int ext4fs_node_get_core(fs_node_t **, ext4fs_instance_t *, fs_index_t);
82static int ext4fs_node_put_core(ext4fs_node_t *);
83
84/*
85 * Forward declarations of EXT4 libfs operations.
86 */
87static int ext4fs_root_get(fs_node_t **, service_id_t);
88static int ext4fs_match(fs_node_t **, fs_node_t *, const char *);
89static int ext4fs_node_get(fs_node_t **, service_id_t, fs_index_t);
90static int ext4fs_node_open(fs_node_t *);
91static int ext4fs_node_put(fs_node_t *);
92static int ext4fs_create_node(fs_node_t **, service_id_t, int);
93static int ext4fs_destroy_node(fs_node_t *);
94static int ext4fs_link(fs_node_t *, fs_node_t *, const char *);
95static int ext4fs_unlink(fs_node_t *, fs_node_t *, const char *);
96static int ext4fs_has_children(bool *, fs_node_t *);
97static fs_index_t ext4fs_index_get(fs_node_t *);
98static aoff64_t ext4fs_size_get(fs_node_t *);
99static unsigned ext4fs_lnkcnt_get(fs_node_t *);
100static bool ext4fs_is_directory(fs_node_t *);
101static bool ext4fs_is_file(fs_node_t *node);
102static service_id_t ext4fs_service_get(fs_node_t *node);
103
104/*
105 * Static variables
106 */
107static LIST_INITIALIZE(instance_list);
108static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
109static hash_table_t open_nodes;
110static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
111
112/* Hash table interface for open nodes hash table */
113static hash_index_t open_nodes_hash(unsigned long key[])
114{
115 /* TODO: This is very simple and probably can be improved */
116 return key[OPEN_NODES_INODE_KEY] % OPEN_NODES_BUCKETS;
117}
118
119static int open_nodes_compare(unsigned long key[], hash_count_t keys,
120 link_t *item)
121{
122 ext4fs_node_t *enode = hash_table_get_instance(item, ext4fs_node_t, link);
123 assert(keys > 0);
124 if (enode->instance->service_id !=
125 ((service_id_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
126 return false;
127 }
128 if (keys == 1) {
129 return true;
130 }
131 assert(keys == 2);
132 return (enode->inode_ref->index == key[OPEN_NODES_INODE_KEY]);
133}
134
135static void open_nodes_remove_cb(link_t *link)
136{
137 /* We don't use remove callback for this hash table */
138}
139
140static hash_table_operations_t open_nodes_ops = {
141 .hash = open_nodes_hash,
142 .compare = open_nodes_compare,
143 .remove_callback = open_nodes_remove_cb,
144};
145
146
147int ext4fs_global_init(void)
148{
149 if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
150 OPEN_NODES_KEYS, &open_nodes_ops)) {
151 return ENOMEM;
152 }
153 return EOK;
154}
155
156
157int ext4fs_global_fini(void)
158{
159 hash_table_destroy(&open_nodes);
160 return EOK;
161}
162
163
164/*
165 * EXT4 libfs operations.
166 */
167
168int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
169{
170 ext4fs_instance_t *tmp;
171
172 fibril_mutex_lock(&instance_list_mutex);
173
174 if (list_empty(&instance_list)) {
175 fibril_mutex_unlock(&instance_list_mutex);
176 return EINVAL;
177 }
178
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 ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
203 ext4_filesystem_t *fs;
204 ext4_directory_iterator_t it;
205 int rc;
206 size_t name_size;
207 size_t component_size;
208 bool found = false;
209 uint32_t inode;
210
211 fs = eparent->instance->filesystem;
212
213 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
214 EXT4_INODE_MODE_DIRECTORY)) {
215 return ENOTDIR;
216 }
217
218 rc = ext4_directory_iterator_init(&it, fs, eparent->inode_ref, 0);
219 if (rc != EOK) {
220 return rc;
221 }
222
223 /* Find length of component in bytes
224 * TODO: check for library function call that does this
225 */
226 component_size = 0;
227 while (*(component+component_size) != 0) {
228 component_size++;
229 }
230
231 while (it.current != NULL) {
232 inode = ext4_directory_entry_ll_get_inode(it.current);
233
234 /* ignore empty directory entries */
235 if (inode != 0) {
236 name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
237 it.current);
238
239 if (name_size == component_size && bcmp(component, &it.current->name,
240 name_size) == 0) {
241 rc = ext4fs_node_get_core(rfn, eparent->instance,
242 inode);
243 if (rc != EOK) {
244 ext4_directory_iterator_fini(&it);
245 return rc;
246 }
247 found = true;
248 break;
249 }
250 }
251
252 rc = ext4_directory_iterator_next(&it);
253 if (rc != EOK) {
254 ext4_directory_iterator_fini(&it);
255 return rc;
256 }
257 }
258
259 ext4_directory_iterator_fini(&it);
260
261 if (!found) {
262 return ENOENT;
263 }
264
265 return EOK;
266}
267
268
269int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
270{
271 ext4fs_instance_t *inst = NULL;
272 int rc;
273
274 rc = ext4fs_instance_get(service_id, &inst);
275 if (rc != EOK) {
276 return rc;
277 }
278
279 return ext4fs_node_get_core(rfn, inst, index);
280}
281
282
283int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
284 fs_index_t index)
285{
286 int rc;
287 fs_node_t *node = NULL;
288 ext4fs_node_t *enode = NULL;
289
290 ext4_inode_ref_t *inode_ref = NULL;
291
292 fibril_mutex_lock(&open_nodes_lock);
293
294 /* Check if the node is not already open */
295 unsigned long key[] = {
296 [OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
297 [OPEN_NODES_INODE_KEY] = index,
298 };
299 link_t *already_open = hash_table_find(&open_nodes, key);
300
301 if (already_open) {
302 enode = hash_table_get_instance(already_open, ext4fs_node_t, link);
303 *rfn = enode->fs_node;
304 enode->references++;
305
306 fibril_mutex_unlock(&open_nodes_lock);
307 return EOK;
308 }
309
310 enode = malloc(sizeof(ext4fs_node_t));
311 if (enode == NULL) {
312 fibril_mutex_unlock(&open_nodes_lock);
313 return ENOMEM;
314 }
315
316 node = malloc(sizeof(fs_node_t));
317 if (node == NULL) {
318 free(enode);
319 fibril_mutex_unlock(&open_nodes_lock);
320 return ENOMEM;
321 }
322 fs_node_initialize(node);
323
324 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
325 if (rc != EOK) {
326 free(enode);
327 free(node);
328 fibril_mutex_unlock(&open_nodes_lock);
329 return rc;
330 }
331
332 enode->inode_ref = inode_ref;
333 enode->instance = inst;
334 enode->references = 1;
335 enode->fs_node = node;
336 link_initialize(&enode->link);
337
338 node->data = enode;
339 *rfn = node;
340
341 hash_table_insert(&open_nodes, key, &enode->link);
342 inst->open_nodes_count++;
343
344 fibril_mutex_unlock(&open_nodes_lock);
345
346 return EOK;
347}
348
349
350int ext4fs_node_put_core(ext4fs_node_t *enode)
351{
352 int rc;
353 unsigned long key[] = {
354 [OPEN_NODES_DEV_HANDLE_KEY] = enode->instance->service_id,
355 [OPEN_NODES_INODE_KEY] = enode->inode_ref->index,
356 };
357
358 hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
359 assert(enode->instance->open_nodes_count > 0);
360 enode->instance->open_nodes_count--;
361
362 rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
363 if (rc != EOK) {
364 return rc;
365 }
366
367 free(enode->fs_node);
368 free(enode);
369
370 return EOK;
371}
372
373
374int ext4fs_node_open(fs_node_t *fn)
375{
376 // TODO stateless operation
377 return EOK;
378}
379
380int ext4fs_node_put(fs_node_t *fn)
381{
382 int rc;
383 ext4fs_node_t *enode = EXT4FS_NODE(fn);
384
385 fibril_mutex_lock(&open_nodes_lock);
386
387 assert(enode->references > 0);
388 enode->references--;
389 if (enode->references == 0) {
390 rc = ext4fs_node_put_core(enode);
391 if (rc != EOK) {
392 fibril_mutex_unlock(&open_nodes_lock);
393 return rc;
394 }
395 }
396
397 fibril_mutex_unlock(&open_nodes_lock);
398
399 return EOK;
400}
401
402
403int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
404{
405 EXT4FS_DBG("not supported");
406
407 // TODO
408 return ENOTSUP;
409}
410
411
412int ext4fs_destroy_node(fs_node_t *fn)
413{
414 EXT4FS_DBG("not supported");
415
416 // TODO
417 return ENOTSUP;
418}
419
420
421int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
422{
423 EXT4FS_DBG("not supported");
424
425 // TODO
426 return ENOTSUP;
427}
428
429
430int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
431{
432 EXT4FS_DBG("not supported");
433
434 // TODO
435 return ENOTSUP;
436}
437
438
439int ext4fs_has_children(bool *has_children, fs_node_t *fn)
440{
441 ext4fs_node_t *enode = EXT4FS_NODE(fn);
442 ext4_directory_iterator_t it;
443 ext4_filesystem_t *fs;
444 int rc;
445 bool found = false;
446 size_t name_size;
447
448 fs = enode->instance->filesystem;
449
450 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
451 EXT4_INODE_MODE_DIRECTORY)) {
452 *has_children = false;
453 return EOK;
454 }
455
456 // TODO check if directory uses HTree
457 if (ext4_filesystem_has_feature_compatible(fs, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
458 EXT4FS_DBG("Using HTree");
459 }
460
461 rc = ext4_directory_iterator_init(&it, fs, enode->inode_ref, 0);
462 if (rc != EOK) {
463 return rc;
464 }
465
466 /* Find a non-empty directory entry */
467 while (it.current != NULL) {
468 if (it.current->inode != 0) {
469 name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
470 it.current);
471 if (!ext4fs_is_dots(it.current->name, name_size)) {
472 found = true;
473 break;
474 }
475 }
476
477 rc = ext4_directory_iterator_next(&it);
478 if (rc != EOK) {
479 ext4_directory_iterator_fini(&it);
480 return rc;
481 }
482 }
483
484 rc = ext4_directory_iterator_fini(&it);
485 if (rc != EOK) {
486 return rc;
487 }
488
489 *has_children = found;
490
491 return EOK;
492}
493
494
495fs_index_t ext4fs_index_get(fs_node_t *fn)
496{
497 ext4fs_node_t *enode = EXT4FS_NODE(fn);
498 return enode->inode_ref->index;
499}
500
501
502aoff64_t ext4fs_size_get(fs_node_t *fn)
503{
504 ext4fs_node_t *enode = EXT4FS_NODE(fn);
505 aoff64_t size = ext4_inode_get_size(enode->instance->filesystem->superblock,
506 enode->inode_ref->inode);
507 return size;
508}
509
510
511unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
512{
513 ext4fs_node_t *enode = EXT4FS_NODE(fn);
514 unsigned count = ext4_inode_get_links_count(enode->inode_ref->inode);
515 return count;
516}
517
518
519bool ext4fs_is_directory(fs_node_t *fn)
520{
521 ext4fs_node_t *enode = EXT4FS_NODE(fn);
522 bool is_dir = ext4_inode_is_type(enode->instance->filesystem->superblock,
523 enode->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY);
524 return is_dir;
525}
526
527
528bool ext4fs_is_file(fs_node_t *fn)
529{
530 ext4fs_node_t *enode = EXT4FS_NODE(fn);
531 bool is_file = ext4_inode_is_type(enode->instance->filesystem->superblock,
532 enode->inode_ref->inode, EXT4_INODE_MODE_FILE);
533 return is_file;
534}
535
536
537service_id_t ext4fs_service_get(fs_node_t *fn)
538{
539 ext4fs_node_t *enode = EXT4FS_NODE(fn);
540 return enode->instance->service_id;
541}
542
543/*
544 * libfs operations.
545 */
546libfs_ops_t ext4fs_libfs_ops = {
547 .root_get = ext4fs_root_get,
548 .match = ext4fs_match,
549 .node_get = ext4fs_node_get,
550 .node_open = ext4fs_node_open,
551 .node_put = ext4fs_node_put,
552 .create = ext4fs_create_node,
553 .destroy = ext4fs_destroy_node,
554 .link = ext4fs_link,
555 .unlink = ext4fs_unlink,
556 .has_children = ext4fs_has_children,
557 .index_get = ext4fs_index_get,
558 .size_get = ext4fs_size_get,
559 .lnkcnt_get = ext4fs_lnkcnt_get,
560 .is_directory = ext4fs_is_directory,
561 .is_file = ext4fs_is_file,
562 .service_get = ext4fs_service_get
563};
564
565
566/*
567 * VFS operations.
568 */
569
570static int ext4fs_mounted(service_id_t service_id, const char *opts,
571 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
572{
573 int rc;
574 ext4_filesystem_t *fs;
575 ext4fs_instance_t *inst;
576 bool read_only;
577
578 /* Allocate libext4 filesystem structure */
579 fs = (ext4_filesystem_t *) malloc(sizeof(ext4_filesystem_t));
580 if (fs == NULL) {
581 return ENOMEM;
582 }
583
584 /* Allocate instance structure */
585 inst = (ext4fs_instance_t *) malloc(sizeof(ext4fs_instance_t));
586 if (inst == NULL) {
587 free(fs);
588 return ENOMEM;
589 }
590
591 /* Initialize the filesystem */
592 rc = ext4_filesystem_init(fs, service_id);
593 if (rc != EOK) {
594 free(fs);
595 free(inst);
596 return rc;
597 }
598
599 /* Do some sanity checking */
600 rc = ext4_filesystem_check_sanity(fs);
601 if (rc != EOK) {
602 ext4_filesystem_fini(fs);
603 free(fs);
604 free(inst);
605 return rc;
606 }
607
608 /* Check flags */
609 rc = ext4_filesystem_check_features(fs, &read_only);
610 if (rc != EOK) {
611 ext4_filesystem_fini(fs);
612 free(fs);
613 free(inst);
614 return rc;
615 }
616
617 /* Initialize instance */
618 link_initialize(&inst->link);
619 inst->service_id = service_id;
620 inst->filesystem = fs;
621 inst->open_nodes_count = 0;
622
623 /* Read root node */
624 fs_node_t *root_node;
625 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
626 if (rc != EOK) {
627 ext4_filesystem_fini(fs);
628 free(fs);
629 free(inst);
630 return rc;
631 }
632 ext4fs_node_t *enode = EXT4FS_NODE(root_node);
633
634 /* Add instance to the list */
635 fibril_mutex_lock(&instance_list_mutex);
636 list_append(&inst->link, &instance_list);
637 fibril_mutex_unlock(&instance_list_mutex);
638
639 *index = EXT4_INODE_ROOT_INDEX;
640 *size = 0;
641 *lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
642
643 ext4fs_node_put(root_node);
644
645 return EOK;
646}
647
648
649static int ext4fs_unmounted(service_id_t service_id)
650{
651 int rc;
652 ext4fs_instance_t *inst;
653
654 rc = ext4fs_instance_get(service_id, &inst);
655
656 if (rc != EOK) {
657 return rc;
658 }
659
660 fibril_mutex_lock(&open_nodes_lock);
661
662 if (inst->open_nodes_count != 0) {
663 fibril_mutex_unlock(&open_nodes_lock);
664 return EBUSY;
665 }
666
667 /* Remove the instance from the list */
668 fibril_mutex_lock(&instance_list_mutex);
669 list_remove(&inst->link);
670 fibril_mutex_unlock(&instance_list_mutex);
671
672 fibril_mutex_unlock(&open_nodes_lock);
673
674 ext4_filesystem_fini(inst->filesystem);
675
676 return EOK;
677}
678
679
680static int
681ext4fs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
682 size_t *rbytes)
683{
684 ext4fs_instance_t *inst;
685 ext4_inode_ref_t *inode_ref;
686 int rc;
687
688 /*
689 * Receive the read request.
690 */
691 ipc_callid_t callid;
692 size_t size;
693 if (!async_data_read_receive(&callid, &size)) {
694 async_answer_0(callid, EINVAL);
695 return EINVAL;
696 }
697
698 rc = ext4fs_instance_get(service_id, &inst);
699 if (rc != EOK) {
700 async_answer_0(callid, rc);
701 return rc;
702 }
703
704 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
705 if (rc != EOK) {
706 async_answer_0(callid, rc);
707 return rc;
708 }
709
710 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
711 EXT4_INODE_MODE_FILE)) {
712 rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
713 rbytes);
714 } else if (ext4_inode_is_type(inst->filesystem->superblock,
715 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
716 rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
717 rbytes);
718 } else {
719 /* Other inode types not supported */
720 async_answer_0(callid, ENOTSUP);
721 rc = ENOTSUP;
722 }
723
724 ext4_filesystem_put_inode_ref(inode_ref);
725 return rc;
726}
727
728bool ext4fs_is_dots(const uint8_t *name, size_t name_size) {
729 if (name_size == 1 && name[0] == '.') {
730 return true;
731 }
732
733 if (name_size == 2 && name[0] == '.' && name[1] == '.') {
734 return true;
735 }
736
737 return false;
738}
739
740int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
741 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
742{
743
744 ext4_directory_iterator_t it;
745 aoff64_t next;
746 uint8_t *buf;
747 size_t name_size;
748 int rc;
749 bool found = false;
750
751 // TODO check if directory uses HTree
752 if (ext4_filesystem_has_feature_compatible(inst->filesystem, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
753 EXT4FS_DBG("Using HTree");
754 }
755
756 rc = ext4_directory_iterator_init(&it, inst->filesystem, inode_ref, pos);
757 if (rc != EOK) {
758 async_answer_0(callid, rc);
759 return rc;
760 }
761
762 /* Find next interesting directory entry.
763 * We want to skip . and .. entries
764 * as these are not used in HelenOS
765 */
766 while (it.current != NULL) {
767
768 if (it.current->inode == 0) {
769 goto skip;
770 }
771
772 name_size = ext4_directory_entry_ll_get_name_length(
773 inst->filesystem->superblock, it.current);
774
775 /* skip . and .. */
776 if (ext4fs_is_dots(it.current->name, name_size)) {
777 goto skip;
778 }
779
780 /* The on-disk entry does not contain \0 at the end
781 * end of entry name, so we copy it to new buffer
782 * and add the \0 at the end
783 */
784 buf = malloc(name_size+1);
785 if (buf == NULL) {
786 ext4_directory_iterator_fini(&it);
787 async_answer_0(callid, ENOMEM);
788 return ENOMEM;
789 }
790 memcpy(buf, &it.current->name, name_size);
791 *(buf + name_size) = 0;
792 found = true;
793 (void) async_data_read_finalize(callid, buf, name_size + 1);
794 free(buf);
795 break;
796
797skip:
798 rc = ext4_directory_iterator_next(&it);
799 if (rc != EOK) {
800 ext4_directory_iterator_fini(&it);
801 async_answer_0(callid, rc);
802 return rc;
803 }
804 }
805
806 if (found) {
807 rc = ext4_directory_iterator_next(&it);
808 if (rc != EOK)
809 return rc;
810 next = it.current_offset;
811 }
812
813 rc = ext4_directory_iterator_fini(&it);
814 if (rc != EOK)
815 return rc;
816
817 if (found) {
818 *rbytes = next - pos;
819 return EOK;
820 } else {
821 async_answer_0(callid, ENOENT);
822 return ENOENT;
823 }
824}
825
826int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
827 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
828{
829 EXT4FS_DBG("");
830
831 int rc;
832 uint32_t block_size;
833 aoff64_t file_block;
834 uint64_t file_size;
835 uint32_t fs_block;
836 size_t offset_in_block;
837 size_t bytes;
838 block_t *block;
839 uint8_t *buffer;
840
841 file_size = ext4_inode_get_size(inst->filesystem->superblock,
842 inode_ref->inode);
843
844 if (pos >= file_size) {
845 /* Read 0 bytes successfully */
846 async_data_read_finalize(callid, NULL, 0);
847 *rbytes = 0;
848 return EOK;
849 }
850
851 /* For now, we only read data from one block at a time */
852 block_size = ext4_superblock_get_block_size(inst->filesystem->superblock);
853 file_block = pos / block_size;
854 offset_in_block = pos % block_size;
855 bytes = min(block_size - offset_in_block, size);
856
857 /* Handle end of file */
858 if (pos + bytes > file_size) {
859 bytes = file_size - pos;
860 }
861
862 /* Get the real block number */
863 rc = ext4_filesystem_get_inode_data_block_index(inst->filesystem,
864 inode_ref->inode, file_block, &fs_block);
865 if (rc != EOK) {
866 async_answer_0(callid, rc);
867 return rc;
868 }
869
870 /* Check for sparse file
871 * If ext2_filesystem_get_inode_data_block_index returned
872 * fs_block == 0, it means that the given block is not allocated for the
873 * file and we need to return a buffer of zeros
874 */
875 if (fs_block == 0) {
876 buffer = malloc(bytes);
877 if (buffer == NULL) {
878 async_answer_0(callid, ENOMEM);
879 return ENOMEM;
880 }
881
882 memset(buffer, 0, bytes);
883
884 async_data_read_finalize(callid, buffer, bytes);
885 *rbytes = bytes;
886
887 free(buffer);
888
889 return EOK;
890 }
891
892 /* Usual case - we need to read a block from device */
893 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
894 if (rc != EOK) {
895 async_answer_0(callid, rc);
896 return rc;
897 }
898
899 assert(offset_in_block + bytes <= block_size);
900 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
901
902 rc = block_put(block);
903 if (rc != EOK)
904 return rc;
905
906 *rbytes = bytes;
907 return EOK;
908}
909
910static int
911ext4fs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
912 size_t *wbytes, aoff64_t *nsize)
913{
914 EXT4FS_DBG("not supported");
915
916 // TODO
917 return ENOTSUP;
918}
919
920
921static int
922ext4fs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
923{
924 EXT4FS_DBG("not supported");
925
926 // TODO
927 return ENOTSUP;
928}
929
930
931static int ext4fs_close(service_id_t service_id, fs_index_t index)
932{
933 // TODO
934 return EOK;
935}
936
937
938static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
939{
940 EXT4FS_DBG("not supported");
941
942 //TODO
943 return ENOTSUP;
944}
945
946
947static int ext4fs_sync(service_id_t service_id, fs_index_t index)
948{
949 EXT4FS_DBG("not supported");
950
951 // TODO
952 return ENOTSUP;
953}
954
955vfs_out_ops_t ext4fs_ops = {
956 .mounted = ext4fs_mounted,
957 .unmounted = ext4fs_unmounted,
958 .read = ext4fs_read,
959 .write = ext4fs_write,
960 .truncate = ext4fs_truncate,
961 .close = ext4fs_close,
962 .destroy = ext4fs_destroy,
963 .sync = ext4fs_sync,
964};
965
966/**
967 * @}
968 */
Note: See TracBrowser for help on using the repository browser.