source: mainline/uspace/srv/fs/ext2fs/ext2fs_ops.c@ 86ffa27f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 86ffa27f was 86ffa27f, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Merge mainline changes.

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