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

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

bugfix, TODO resolving

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