source: mainline/uspace/srv/fs/ext2fs/ext2fs_ops.c@ 9f4067b6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9f4067b6 was 4e00f87, checked in by Jakub Jermar <jakub@…>, 13 years ago

Use NULL instead of 0 as a hash_table_ops_t member initializer.

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