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

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

bugfixes of inode allocation and link operation

  • Property mode set to 100644
File size: 28.2 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>
[2b9e142]44#include <string.h>
[3711e7e]45#include <adt/hash_table.h>
[d3a9ae74]46#include <ipc/loc.h>
47#include "ext4fs.h"
48#include "../../vfs/vfs.h"
49
[6c501f8]50#define EXT4FS_NODE(node) ((node) ? (ext4fs_node_t *) (node)->data : NULL)
51
[3711e7e]52#define OPEN_NODES_KEYS 2
53#define OPEN_NODES_DEV_HANDLE_KEY 0
54#define OPEN_NODES_INODE_KEY 1
55#define OPEN_NODES_BUCKETS 256
56
[6c501f8]57typedef struct ext4fs_instance {
58 link_t link;
59 service_id_t service_id;
60 ext4_filesystem_t *filesystem;
61 unsigned int open_nodes_count;
62} ext4fs_instance_t;
63
64typedef struct ext4fs_node {
65 ext4fs_instance_t *instance;
66 ext4_inode_ref_t *inode_ref;
67 fs_node_t *fs_node;
68 link_t link;
69 unsigned int references;
70} ext4fs_node_t;
71
72/*
73 * Forward declarations of auxiliary functions
74 */
[9b9d37bb]75
76static int ext4fs_read_directory(ipc_callid_t, aoff64_t, size_t,
77 ext4fs_instance_t *, ext4_inode_ref_t *, size_t *);
78static int ext4fs_read_file(ipc_callid_t, aoff64_t, size_t, ext4fs_instance_t *,
79 ext4_inode_ref_t *, size_t *);
80static bool ext4fs_is_dots(const uint8_t *, size_t);
[6c501f8]81static int ext4fs_instance_get(service_id_t, ext4fs_instance_t **);
82static int ext4fs_node_get_core(fs_node_t **, ext4fs_instance_t *, fs_index_t);
[9c0c0e1]83static int ext4fs_node_put_core(ext4fs_node_t *);
[d3a9ae74]84
85/*
86 * Forward declarations of EXT4 libfs operations.
87 */
88static int ext4fs_root_get(fs_node_t **, service_id_t);
89static int ext4fs_match(fs_node_t **, fs_node_t *, const char *);
90static int ext4fs_node_get(fs_node_t **, service_id_t, fs_index_t);
91static int ext4fs_node_open(fs_node_t *);
92static int ext4fs_node_put(fs_node_t *);
93static int ext4fs_create_node(fs_node_t **, service_id_t, int);
94static int ext4fs_destroy_node(fs_node_t *);
95static int ext4fs_link(fs_node_t *, fs_node_t *, const char *);
96static int ext4fs_unlink(fs_node_t *, fs_node_t *, const char *);
97static int ext4fs_has_children(bool *, fs_node_t *);
98static fs_index_t ext4fs_index_get(fs_node_t *);
99static aoff64_t ext4fs_size_get(fs_node_t *);
100static unsigned ext4fs_lnkcnt_get(fs_node_t *);
101static bool ext4fs_is_directory(fs_node_t *);
102static bool ext4fs_is_file(fs_node_t *node);
103static service_id_t ext4fs_service_get(fs_node_t *node);
104
[6c501f8]105/*
106 * Static variables
107 */
108static LIST_INITIALIZE(instance_list);
109static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
[3711e7e]110static hash_table_t open_nodes;
[6c501f8]111static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
112
[3711e7e]113/* Hash table interface for open nodes hash table */
114static hash_index_t open_nodes_hash(unsigned long key[])
115{
116 /* TODO: This is very simple and probably can be improved */
117 return key[OPEN_NODES_INODE_KEY] % OPEN_NODES_BUCKETS;
118}
119
120static int open_nodes_compare(unsigned long key[], hash_count_t keys,
121 link_t *item)
122{
123 ext4fs_node_t *enode = hash_table_get_instance(item, ext4fs_node_t, link);
124 assert(keys > 0);
125 if (enode->instance->service_id !=
126 ((service_id_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
127 return false;
128 }
129 if (keys == 1) {
130 return true;
131 }
132 assert(keys == 2);
133 return (enode->inode_ref->index == key[OPEN_NODES_INODE_KEY]);
134}
135
136static void open_nodes_remove_cb(link_t *link)
137{
138 /* We don't use remove callback for this hash table */
139}
140
141static hash_table_operations_t open_nodes_ops = {
142 .hash = open_nodes_hash,
143 .compare = open_nodes_compare,
144 .remove_callback = open_nodes_remove_cb,
145};
146
[6c501f8]147
[d3a9ae74]148int ext4fs_global_init(void)
149{
[3711e7e]150 if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
151 OPEN_NODES_KEYS, &open_nodes_ops)) {
152 return ENOMEM;
153 }
[d3a9ae74]154 return EOK;
155}
156
[3711e7e]157
[d3a9ae74]158int ext4fs_global_fini(void)
159{
[3711e7e]160 hash_table_destroy(&open_nodes);
[d3a9ae74]161 return EOK;
162}
163
164
165/*
166 * EXT4 libfs operations.
167 */
168
[6c501f8]169int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
170{
171 fibril_mutex_lock(&instance_list_mutex);
172
173 if (list_empty(&instance_list)) {
174 fibril_mutex_unlock(&instance_list_mutex);
175 return EINVAL;
176 }
177
[5614c7f]178 ext4fs_instance_t *tmp;
[6c501f8]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 int rc;
203
[5614c7f]204 ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
205 ext4_filesystem_t *fs = eparent->instance->filesystem;
[9b9d37bb]206
207 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
208 EXT4_INODE_MODE_DIRECTORY)) {
209 return ENOTDIR;
210 }
211
[5614c7f]212 ext4_directory_iterator_t it;
[9b9d37bb]213 rc = ext4_directory_iterator_init(&it, fs, eparent->inode_ref, 0);
214 if (rc != EOK) {
215 return rc;
216 }
217
[8be96a0]218 rc = ext4_directory_find_entry(&it, eparent->inode_ref, component);
219 if (rc != EOK) {
220 ext4_directory_iterator_fini(&it);
221 return rc;
[9b9d37bb]222 }
223
[8be96a0]224 uint32_t inode = ext4_directory_entry_ll_get_inode(it.current);
[9b9d37bb]225
[8be96a0]226 rc = ext4fs_node_get_core(rfn, eparent->instance, inode);
227 if (rc != EOK) {
228 ext4_directory_iterator_fini(&it);
229 return rc;
[9b9d37bb]230 }
231
[8be96a0]232 ext4_directory_iterator_fini(&it);
[d3a9ae74]233 return EOK;
234}
235
[3711e7e]236
[d3a9ae74]237int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
238{
[9c0c0e1]239 int rc;
240
[5614c7f]241 ext4fs_instance_t *inst;
[9c0c0e1]242 rc = ext4fs_instance_get(service_id, &inst);
243 if (rc != EOK) {
244 return rc;
245 }
246
247 return ext4fs_node_get_core(rfn, inst, index);
[d3a9ae74]248}
249
[3711e7e]250
[6c501f8]251int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
252 fs_index_t index)
253{
[3711e7e]254 int rc;
255
256 fibril_mutex_lock(&open_nodes_lock);
257
258 /* Check if the node is not already open */
259 unsigned long key[] = {
260 [OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
261 [OPEN_NODES_INODE_KEY] = index,
262 };
263
[5614c7f]264 link_t *already_open = hash_table_find(&open_nodes, key);
265 ext4fs_node_t *enode = NULL;
[3711e7e]266 if (already_open) {
267 enode = hash_table_get_instance(already_open, ext4fs_node_t, link);
268 *rfn = enode->fs_node;
269 enode->references++;
270
271 fibril_mutex_unlock(&open_nodes_lock);
272 return EOK;
273 }
274
275 enode = malloc(sizeof(ext4fs_node_t));
276 if (enode == NULL) {
277 fibril_mutex_unlock(&open_nodes_lock);
278 return ENOMEM;
279 }
280
[5614c7f]281 fs_node_t *fs_node = malloc(sizeof(fs_node_t));
282 if (fs_node == NULL) {
[3711e7e]283 free(enode);
284 fibril_mutex_unlock(&open_nodes_lock);
285 return ENOMEM;
286 }
[5614c7f]287 fs_node_initialize(fs_node);
[3711e7e]288
[5614c7f]289 ext4_inode_ref_t *inode_ref;
[3711e7e]290 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
291 if (rc != EOK) {
292 free(enode);
[5614c7f]293 free(fs_node);
[3711e7e]294 fibril_mutex_unlock(&open_nodes_lock);
295 return rc;
296 }
297
298 enode->inode_ref = inode_ref;
299 enode->instance = inst;
300 enode->references = 1;
[5614c7f]301 enode->fs_node = fs_node;
[3711e7e]302 link_initialize(&enode->link);
303
[5614c7f]304 fs_node->data = enode;
305 *rfn = fs_node;
[3711e7e]306
307 hash_table_insert(&open_nodes, key, &enode->link);
308 inst->open_nodes_count++;
309
310 fibril_mutex_unlock(&open_nodes_lock);
311
[6c501f8]312 return EOK;
313}
314
[3711e7e]315
[9b9d37bb]316int ext4fs_node_put_core(ext4fs_node_t *enode)
317{
318 int rc;
319 unsigned long key[] = {
320 [OPEN_NODES_DEV_HANDLE_KEY] = enode->instance->service_id,
321 [OPEN_NODES_INODE_KEY] = enode->inode_ref->index,
322 };
323
324 hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
325 assert(enode->instance->open_nodes_count > 0);
326 enode->instance->open_nodes_count--;
327
328 rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
329 if (rc != EOK) {
330 return rc;
331 }
332
333 free(enode->fs_node);
334 free(enode);
335
[9c0c0e1]336 return EOK;
337}
338
[3711e7e]339
[d3a9ae74]340int ext4fs_node_open(fs_node_t *fn)
341{
[2b9e142]342 // Stateless operation
[d3a9ae74]343 return EOK;
344}
345
346int ext4fs_node_put(fs_node_t *fn)
347{
[9c0c0e1]348 int rc;
349
350 fibril_mutex_lock(&open_nodes_lock);
351
[5614c7f]352 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[9c0c0e1]353 assert(enode->references > 0);
354 enode->references--;
355 if (enode->references == 0) {
356 rc = ext4fs_node_put_core(enode);
357 if (rc != EOK) {
358 fibril_mutex_unlock(&open_nodes_lock);
359 return rc;
360 }
361 }
362
363 fibril_mutex_unlock(&open_nodes_lock);
364
[d3a9ae74]365 return EOK;
366}
367
[3711e7e]368
[d3a9ae74]369int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
370{
[47a89fe]371 EXT4FS_DBG("");
372 int rc;
373
374 ext4fs_node_t *enode;
375 enode = malloc(sizeof(ext4fs_node_t));
376 if (enode == NULL) {
377 return ENOMEM;
378 }
379
380 fs_node_t *fs_node;
381 fs_node = malloc(sizeof(fs_node_t));
382 if (fs_node == NULL) {
383 free(enode);
384 return ENOMEM;
385 }
386
387 ext4fs_instance_t *inst;
388 rc = ext4fs_instance_get(service_id, &inst);
389 if (rc != EOK) {
390 free(enode);
391 free(fs_node);
392 return rc;
393 }
394
395 ext4_inode_ref_t *inode_ref;
[304faab]396 rc = ext4_filesystem_alloc_inode(inst->filesystem, &inode_ref, flags);
[47a89fe]397 if (rc != EOK) {
398 free(enode);
399 free(fs_node);
400 return rc;
401 }
[9b9d37bb]402
[cd1cc4e6]403 EXT4FS_DBG("allocated");
404
[47a89fe]405 enode->inode_ref = inode_ref;
406 enode->instance = inst;
407 enode->references = 1;
408
409 link_initialize(&enode->link);
410
411 unsigned long key[] = {
412 [OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
413 [OPEN_NODES_INODE_KEY] = inode_ref->index,
414 };
415
416 fibril_mutex_lock(&open_nodes_lock);
417 hash_table_insert(&open_nodes, key, &enode->link);
418 fibril_mutex_unlock(&open_nodes_lock);
419 inst->open_nodes_count++;
420
421 enode->inode_ref->dirty = true;
422
423 fs_node_initialize(fs_node);
424 fs_node->data = enode;
425 enode->fs_node = fs_node;
426 *rfn = fs_node;
427
[cd1cc4e6]428 EXT4FS_DBG("finished");
429
[47a89fe]430 // TODO
431 return EOK;
[d3a9ae74]432}
433
[3711e7e]434
[d3a9ae74]435int ext4fs_destroy_node(fs_node_t *fn)
436{
[cd1cc4e6]437 EXT4FS_DBG("");
[3d4fd2c]438 int rc;
[9b9d37bb]439
[3d4fd2c]440 bool has_children;
441 rc = ext4fs_has_children(&has_children, fn);
442 if (rc != EOK) {
443 ext4fs_node_put(fn);
444 return rc;
445 }
446
447 if (has_children) {
448 EXT4FS_DBG("destroying non-empty node");
449 ext4fs_node_put(fn);
450 return EINVAL;
451 }
452
453 ext4fs_node_t *enode = EXT4FS_NODE(fn);
454 ext4_filesystem_t *fs = enode->instance->filesystem;
455 ext4_inode_ref_t *inode_ref = enode->inode_ref;
456
457 rc = ext4_filesystem_truncate_inode(fs, inode_ref, 0);
458 if (rc != EOK) {
459 ext4fs_node_put(fn);
460 return rc;
461 }
462
[07fd4cd1]463 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
464 if (rev_level > 0) {
465 ext4_filesystem_delete_orphan(fs, inode_ref);
466 }
467
468 // TODO set real deletion time
469// time_t now = time(NULL);
470 time_t now = ext4_inode_get_change_inode_time(inode_ref->inode);
471 ext4_inode_set_deletion_time(inode_ref->inode, (uint32_t)now);
472 inode_ref->dirty = true;
473
[3d4fd2c]474 rc = ext4_filesystem_free_inode(fs, inode_ref);
475 if (rc != EOK) {
476 ext4fs_node_put(fn);
477 return rc;
478 }
479
480 ext4fs_node_put(fn);
481 return EOK;
[d3a9ae74]482}
483
[3711e7e]484
[d3a9ae74]485int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
486{
[cd1cc4e6]487 EXT4FS_DBG("");
488
[47a89fe]489 int rc;
[9b9d37bb]490
[47a89fe]491 // Check maximum name length
492 if (strlen(name) > EXT4_DIRECTORY_FILENAME_LEN) {
493 return ENAMETOOLONG;
494 }
495
[cd1cc4e6]496 EXT4FS_DBG("name checked");
497
[47a89fe]498 ext4fs_node_t *parent = EXT4FS_NODE(pfn);
499 ext4fs_node_t *child = EXT4FS_NODE(cfn);
500 ext4_filesystem_t *fs = parent->instance->filesystem;
501
502 // Add entry to parent directory
[cd1cc4e6]503 rc = ext4_directory_add_entry(fs, parent->inode_ref, name, child->inode_ref);
[47a89fe]504 if (rc != EOK) {
505 return rc;
506 }
507
[cd1cc4e6]508 EXT4FS_DBG("dentry added");
509
[47a89fe]510 // Fill new dir -> add '.' and '..' entries
511 if (ext4_inode_is_type(fs->superblock, child->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
512
[cd1cc4e6]513 rc = ext4_directory_add_entry(fs, child->inode_ref, ".", child->inode_ref);
[47a89fe]514 if (rc != EOK) {
515 ext4_directory_remove_entry(fs, parent->inode_ref, name);
516 return rc;
517 }
518
[cd1cc4e6]519 EXT4FS_DBG("added dot");
520
521 rc = ext4_directory_add_entry(fs, child->inode_ref, "..", parent->inode_ref);
[47a89fe]522 if (rc != EOK) {
523 ext4_directory_remove_entry(fs, parent->inode_ref, name);
524 ext4_directory_remove_entry(fs, child->inode_ref, ".");
525 return rc;
526 }
527
[cd1cc4e6]528 EXT4FS_DBG("added dotdot");
529
[47a89fe]530 uint16_t parent_links = ext4_inode_get_links_count(parent->inode_ref->inode);
531 parent_links++;
532 ext4_inode_set_links_count(parent->inode_ref->inode, parent_links);
533
534 parent->inode_ref->dirty = true;
535
536 }
537
538 uint16_t child_links = ext4_inode_get_links_count(child->inode_ref->inode);
539 child_links++;
540 ext4_inode_set_links_count(child->inode_ref->inode, child_links);
541
542 child->inode_ref->dirty = true;
543
544 return EOK;
[d3a9ae74]545}
546
[3711e7e]547
[ebeaaa06]548int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[d3a9ae74]549{
[d4d2954]550// EXT4FS_DBG("unlinking \%s", name);
[3d4fd2c]551
[ebeaaa06]552 int rc;
553
554 bool has_children;
555 rc = ext4fs_has_children(&has_children, cfn);
556 if (rc != EOK) {
[82d7816]557 EXT4FS_DBG("\%s error: \%u", name, rc);
[ebeaaa06]558 return rc;
559 }
560
561 // Cannot unlink non-empty node
562 if (has_children) {
563 return ENOTEMPTY;
564 }
565
566 // Remove entry from parent directory
[f49638e]567 ext4_inode_ref_t *parent = EXT4FS_NODE(pfn)->inode_ref;
568 ext4_filesystem_t *fs = EXT4FS_NODE(pfn)->instance->filesystem;
569 rc = ext4_directory_remove_entry(fs, parent, name);
570 if (rc != EOK) {
[82d7816]571 EXT4FS_DBG("\%s removing entry failed: \%u", name, rc);
[f49638e]572 return rc;
573 }
[ebeaaa06]574
575 // Decrement links count
576 ext4_inode_ref_t * child_inode_ref = EXT4FS_NODE(cfn)->inode_ref;
577
578 uint32_t lnk_count = ext4_inode_get_links_count(child_inode_ref->inode);
579 lnk_count--;
580
581 // If directory - handle links from parent
582 if (lnk_count <= 1 && ext4fs_is_directory(cfn)) {
[1e48a07e]583
584 assert(lnk_count == 1);
585 lnk_count--;
586
[82d7816]587 ext4_inode_ref_t *parent_inode_ref = EXT4FS_NODE(pfn)->inode_ref;
588
589 uint32_t parent_lnk_count = ext4_inode_get_links_count(
590 parent_inode_ref->inode);
591
592 parent_lnk_count--;
593 ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count);
594
[07fd4cd1]595 parent->dirty = true;
596 }
597
598 uint32_t rev_level = ext4_superblock_get_rev_level(fs->superblock);
599 if ((rev_level > 0) && (lnk_count == 0)) {
600 ext4_filesystem_add_orphan(fs, child_inode_ref);
[ebeaaa06]601 }
602
[07fd4cd1]603 // TODO set timestamps for parent (when we have wall-clock time)
604// time_t now = time(NULL);
605// ext4_inode_set_change_inode_time(parent->inode, (uint32_t)now);
606// ext4_inode_set_modification_time(parent->inode, (uint32_t)now);
607// parent->dirty = true;
608
609 // TODO set timestamp for inode
610// ext4_inode_set_change_inode_time(child_inode_ref->inode, (uint32_t)now);
[82d7816]611 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
612 child_inode_ref->dirty = true;
613
[ebeaaa06]614 return EOK;
[d3a9ae74]615}
616
[3711e7e]617
[d3a9ae74]618int ext4fs_has_children(bool *has_children, fs_node_t *fn)
619{
[e68c834]620 int rc;
621
[5614c7f]622 ext4fs_node_t *enode = EXT4FS_NODE(fn);
623 ext4_filesystem_t *fs = enode->instance->filesystem;
[e68c834]624
625 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
626 EXT4_INODE_MODE_DIRECTORY)) {
627 *has_children = false;
628 return EOK;
629 }
630
[5614c7f]631 ext4_directory_iterator_t it;
[e68c834]632 rc = ext4_directory_iterator_init(&it, fs, enode->inode_ref, 0);
633 if (rc != EOK) {
634 return rc;
635 }
636
637 /* Find a non-empty directory entry */
[5614c7f]638 bool found = false;
[e68c834]639 while (it.current != NULL) {
640 if (it.current->inode != 0) {
[5614c7f]641 uint16_t name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
[e68c834]642 it.current);
[2ea6392]643 if (!ext4fs_is_dots(it.current->name, name_size)) {
[e68c834]644 found = true;
645 break;
646 }
647 }
648
649 rc = ext4_directory_iterator_next(&it);
650 if (rc != EOK) {
651 ext4_directory_iterator_fini(&it);
652 return rc;
653 }
654 }
655
656 rc = ext4_directory_iterator_fini(&it);
657 if (rc != EOK) {
658 return rc;
659 }
660
661 *has_children = found;
[9b9d37bb]662
[d3a9ae74]663 return EOK;
664}
665
666
667fs_index_t ext4fs_index_get(fs_node_t *fn)
668{
[9b9d37bb]669 ext4fs_node_t *enode = EXT4FS_NODE(fn);
670 return enode->inode_ref->index;
[d3a9ae74]671}
672
[3711e7e]673
[d3a9ae74]674aoff64_t ext4fs_size_get(fs_node_t *fn)
675{
[9b9d37bb]676 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]677 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
678 return ext4_inode_get_size(sb, enode->inode_ref->inode);
[d3a9ae74]679}
680
[3711e7e]681
[d3a9ae74]682unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
683{
[d4d2954]684 ext4fs_node_t *enode = EXT4FS_NODE(fn);
685 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
686
[e5f8762]687 if (ext4fs_is_directory(fn)) {
688 if (lnkcnt > 1) {
689 return 1;
690 } else {
691 return 0;
692 }
693 }
694
695 // For regular files return real links count
[d4d2954]696 return lnkcnt;
[d3a9ae74]697}
698
[3711e7e]699
[d3a9ae74]700bool ext4fs_is_directory(fs_node_t *fn)
701{
[e68c834]702 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]703 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
704 return ext4_inode_is_type(
705 sb, enode->inode_ref->inode, EXT4_INODE_MODE_DIRECTORY);
[d3a9ae74]706}
707
[3711e7e]708
[d3a9ae74]709bool ext4fs_is_file(fs_node_t *fn)
710{
[9b9d37bb]711 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[5614c7f]712 ext4_superblock_t *sb = enode->instance->filesystem->superblock;
713 return ext4_inode_is_type(
714 sb, enode->inode_ref->inode, EXT4_INODE_MODE_FILE);
[d3a9ae74]715}
716
[3711e7e]717
[d3a9ae74]718service_id_t ext4fs_service_get(fs_node_t *fn)
719{
[e68c834]720 ext4fs_node_t *enode = EXT4FS_NODE(fn);
721 return enode->instance->service_id;
[d3a9ae74]722}
723
724/*
725 * libfs operations.
726 */
727libfs_ops_t ext4fs_libfs_ops = {
728 .root_get = ext4fs_root_get,
729 .match = ext4fs_match,
730 .node_get = ext4fs_node_get,
731 .node_open = ext4fs_node_open,
732 .node_put = ext4fs_node_put,
733 .create = ext4fs_create_node,
734 .destroy = ext4fs_destroy_node,
735 .link = ext4fs_link,
736 .unlink = ext4fs_unlink,
737 .has_children = ext4fs_has_children,
738 .index_get = ext4fs_index_get,
739 .size_get = ext4fs_size_get,
740 .lnkcnt_get = ext4fs_lnkcnt_get,
741 .is_directory = ext4fs_is_directory,
742 .is_file = ext4fs_is_file,
743 .service_get = ext4fs_service_get
744};
745
746
747/*
748 * VFS operations.
749 */
750
751static int ext4fs_mounted(service_id_t service_id, const char *opts,
752 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
753{
[6c501f8]754 int rc;
755
756 /* Allocate libext4 filesystem structure */
[5614c7f]757 ext4_filesystem_t *fs;
[6c501f8]758 fs = (ext4_filesystem_t *) malloc(sizeof(ext4_filesystem_t));
759 if (fs == NULL) {
760 return ENOMEM;
761 }
762
763 /* Allocate instance structure */
[5614c7f]764 ext4fs_instance_t *inst;
[6c501f8]765 inst = (ext4fs_instance_t *) malloc(sizeof(ext4fs_instance_t));
766 if (inst == NULL) {
767 free(fs);
768 return ENOMEM;
769 }
770
[9c0c0e1]771 /* Initialize the filesystem */
[6c501f8]772 rc = ext4_filesystem_init(fs, service_id);
773 if (rc != EOK) {
774 free(fs);
775 free(inst);
776 return rc;
777 }
778
779 /* Do some sanity checking */
780 rc = ext4_filesystem_check_sanity(fs);
781 if (rc != EOK) {
[ae3d4f8]782 ext4_filesystem_fini(fs, false);
[6c501f8]783 free(fs);
784 free(inst);
785 return rc;
786 }
787
788 /* Check flags */
[5614c7f]789 bool read_only;
[9c0c0e1]790 rc = ext4_filesystem_check_features(fs, &read_only);
[6c501f8]791 if (rc != EOK) {
[ae3d4f8]792 ext4_filesystem_fini(fs, false);
[6c501f8]793 free(fs);
794 free(inst);
795 return rc;
796 }
797
798 /* Initialize instance */
799 link_initialize(&inst->link);
800 inst->service_id = service_id;
801 inst->filesystem = fs;
802 inst->open_nodes_count = 0;
803
804 /* Read root node */
805 fs_node_t *root_node;
[3711e7e]806 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
[6c501f8]807 if (rc != EOK) {
[ae3d4f8]808 ext4_filesystem_fini(fs, false);
[6c501f8]809 free(fs);
810 free(inst);
811 return rc;
812 }
813
814 /* Add instance to the list */
815 fibril_mutex_lock(&instance_list_mutex);
816 list_append(&inst->link, &instance_list);
817 fibril_mutex_unlock(&instance_list_mutex);
818
819 *index = EXT4_INODE_ROOT_INDEX;
820 *size = 0;
[e5f8762]821 *lnkcnt = 1;
[6c501f8]822
823 ext4fs_node_put(root_node);
824
[d3a9ae74]825 return EOK;
826}
827
[3711e7e]828
[d3a9ae74]829static int ext4fs_unmounted(service_id_t service_id)
830{
[6c501f8]831 int rc;
832
[5614c7f]833 ext4fs_instance_t *inst;
[6c501f8]834 rc = ext4fs_instance_get(service_id, &inst);
835 if (rc != EOK) {
836 return rc;
837 }
838
839 fibril_mutex_lock(&open_nodes_lock);
840
841 if (inst->open_nodes_count != 0) {
842 fibril_mutex_unlock(&open_nodes_lock);
843 return EBUSY;
844 }
845
846 /* Remove the instance from the list */
847 fibril_mutex_lock(&instance_list_mutex);
848 list_remove(&inst->link);
849 fibril_mutex_unlock(&instance_list_mutex);
850
851 fibril_mutex_unlock(&open_nodes_lock);
852
[ae3d4f8]853 return ext4_filesystem_fini(inst->filesystem, true);
[d3a9ae74]854}
855
[3711e7e]856
[5f6cb14]857static int ext4fs_read(service_id_t service_id, fs_index_t index,
858 aoff64_t pos, size_t *rbytes)
[d3a9ae74]859{
[9b9d37bb]860 int rc;
861
862 /*
863 * Receive the read request.
864 */
865 ipc_callid_t callid;
866 size_t size;
867 if (!async_data_read_receive(&callid, &size)) {
868 async_answer_0(callid, EINVAL);
869 return EINVAL;
870 }
871
[5614c7f]872 ext4fs_instance_t *inst;
[9b9d37bb]873 rc = ext4fs_instance_get(service_id, &inst);
874 if (rc != EOK) {
875 async_answer_0(callid, rc);
876 return rc;
877 }
878
[5614c7f]879 ext4_inode_ref_t *inode_ref;
[9b9d37bb]880 rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
881 if (rc != EOK) {
882 async_answer_0(callid, rc);
883 return rc;
884 }
885
886 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
887 EXT4_INODE_MODE_FILE)) {
888 rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
889 rbytes);
890 } else if (ext4_inode_is_type(inst->filesystem->superblock,
891 inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
892 rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
893 rbytes);
894 } else {
895 /* Other inode types not supported */
896 async_answer_0(callid, ENOTSUP);
897 rc = ENOTSUP;
898 }
899
900 ext4_filesystem_put_inode_ref(inode_ref);
[8958a26]901
[9b9d37bb]902 return rc;
903}
904
[8958a26]905bool ext4fs_is_dots(const uint8_t *name, size_t name_size)
906{
[9b9d37bb]907 if (name_size == 1 && name[0] == '.') {
908 return true;
909 }
910
911 if (name_size == 2 && name[0] == '.' && name[1] == '.') {
912 return true;
913 }
914
915 return false;
916}
917
918int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
919 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
920{
921 int rc;
922
[5614c7f]923 ext4_directory_iterator_t it;
[9b9d37bb]924 rc = ext4_directory_iterator_init(&it, inst->filesystem, inode_ref, pos);
925 if (rc != EOK) {
926 async_answer_0(callid, rc);
927 return rc;
928 }
929
930 /* Find next interesting directory entry.
931 * We want to skip . and .. entries
932 * as these are not used in HelenOS
933 */
[5614c7f]934 bool found = false;
[9b9d37bb]935 while (it.current != NULL) {
[e68c834]936
[9b9d37bb]937 if (it.current->inode == 0) {
938 goto skip;
939 }
940
[5614c7f]941 uint16_t name_size = ext4_directory_entry_ll_get_name_length(
[9b9d37bb]942 inst->filesystem->superblock, it.current);
943
944 /* skip . and .. */
[2ea6392]945 if (ext4fs_is_dots(it.current->name, name_size)) {
[9b9d37bb]946 goto skip;
947 }
948
949 /* The on-disk entry does not contain \0 at the end
950 * end of entry name, so we copy it to new buffer
951 * and add the \0 at the end
952 */
[5614c7f]953 uint8_t *buf = malloc(name_size+1);
[9b9d37bb]954 if (buf == NULL) {
955 ext4_directory_iterator_fini(&it);
956 async_answer_0(callid, ENOMEM);
957 return ENOMEM;
958 }
959 memcpy(buf, &it.current->name, name_size);
960 *(buf + name_size) = 0;
961 found = true;
962 (void) async_data_read_finalize(callid, buf, name_size + 1);
963 free(buf);
964 break;
965
966skip:
967 rc = ext4_directory_iterator_next(&it);
968 if (rc != EOK) {
969 ext4_directory_iterator_fini(&it);
970 async_answer_0(callid, rc);
971 return rc;
972 }
973 }
974
[5614c7f]975 uint64_t next;
[9b9d37bb]976 if (found) {
977 rc = ext4_directory_iterator_next(&it);
[8958a26]978 if (rc != EOK) {
[9b9d37bb]979 return rc;
[8958a26]980 }
[9b9d37bb]981 next = it.current_offset;
982 }
983
984 rc = ext4_directory_iterator_fini(&it);
[8958a26]985 if (rc != EOK) {
[9b9d37bb]986 return rc;
[8958a26]987 }
[9b9d37bb]988
989 if (found) {
990 *rbytes = next - pos;
991 return EOK;
992 } else {
993 async_answer_0(callid, ENOENT);
994 return ENOENT;
995 }
[d3a9ae74]996}
997
[9b9d37bb]998int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
999 ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
1000{
1001 int rc;
1002
[5614c7f]1003 ext4_superblock_t *sb = inst->filesystem->superblock;
1004 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode);
[9b9d37bb]1005
1006 if (pos >= file_size) {
1007 /* Read 0 bytes successfully */
1008 async_data_read_finalize(callid, NULL, 0);
1009 *rbytes = 0;
1010 return EOK;
1011 }
1012
1013 /* For now, we only read data from one block at a time */
[5614c7f]1014 uint32_t block_size = ext4_superblock_get_block_size(sb);
1015 aoff64_t file_block = pos / block_size;
1016 uint32_t offset_in_block = pos % block_size;
1017 uint32_t bytes = min(block_size - offset_in_block, size);
[9b9d37bb]1018
1019 /* Handle end of file */
1020 if (pos + bytes > file_size) {
1021 bytes = file_size - pos;
1022 }
1023
1024 /* Get the real block number */
[5614c7f]1025 uint32_t fs_block;
[9b9d37bb]1026 rc = ext4_filesystem_get_inode_data_block_index(inst->filesystem,
1027 inode_ref->inode, file_block, &fs_block);
1028 if (rc != EOK) {
1029 async_answer_0(callid, rc);
1030 return rc;
1031 }
1032
1033 /* Check for sparse file
[2b9e142]1034 * If ext4_filesystem_get_inode_data_block_index returned
[9b9d37bb]1035 * fs_block == 0, it means that the given block is not allocated for the
1036 * file and we need to return a buffer of zeros
1037 */
[5614c7f]1038 uint8_t *buffer;
[9b9d37bb]1039 if (fs_block == 0) {
1040 buffer = malloc(bytes);
1041 if (buffer == NULL) {
1042 async_answer_0(callid, ENOMEM);
1043 return ENOMEM;
1044 }
1045
1046 memset(buffer, 0, bytes);
1047
1048 async_data_read_finalize(callid, buffer, bytes);
1049 *rbytes = bytes;
1050
1051 free(buffer);
1052
1053 return EOK;
1054 }
1055
1056 /* Usual case - we need to read a block from device */
[5614c7f]1057 block_t *block;
[9b9d37bb]1058 rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
1059 if (rc != EOK) {
1060 async_answer_0(callid, rc);
1061 return rc;
1062 }
1063
1064 assert(offset_in_block + bytes <= block_size);
1065 async_data_read_finalize(callid, block->data + offset_in_block, bytes);
1066
1067 rc = block_put(block);
[8958a26]1068 if (rc != EOK) {
[9b9d37bb]1069 return rc;
[8958a26]1070 }
[9b9d37bb]1071
1072 *rbytes = bytes;
1073 return EOK;
1074}
[3711e7e]1075
[5f6cb14]1076static int ext4fs_write(service_id_t service_id, fs_index_t index,
1077 aoff64_t pos, size_t *wbytes, aoff64_t *nsize)
[d3a9ae74]1078{
[1c1c736]1079 int rc;
1080
[5614c7f]1081 fs_node_t *fn;
[1c1c736]1082 rc = ext4fs_node_get(&fn, service_id, index);
1083 if (rc != EOK) {
[6088193]1084 EXT4FS_DBG("node get error");
[1c1c736]1085 return rc;
1086 }
1087
[5614c7f]1088 ipc_callid_t callid;
1089 size_t len;
[1c1c736]1090 if (!async_data_write_receive(&callid, &len)) {
1091 rc = EINVAL;
1092 ext4fs_node_put(fn);
1093 async_answer_0(callid, rc);
[6088193]1094 EXT4FS_DBG("data write recv");
[1c1c736]1095 return rc;
1096 }
1097
1098
[5614c7f]1099 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1100 ext4_filesystem_t *fs = enode->instance->filesystem;
[1c1c736]1101
[5614c7f]1102 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[1c1c736]1103
1104 // Prevent writing to more than one block
[5614c7f]1105 uint32_t bytes = min(len, block_size - (pos % block_size));
[1c1c736]1106
[5614c7f]1107 int flags = BLOCK_FLAGS_NONE;
[35f48f2]1108 if (bytes == block_size) {
1109 flags = BLOCK_FLAGS_NOREAD;
1110 }
1111
[5614c7f]1112 uint32_t iblock = pos / block_size;
1113 uint32_t fblock;
[1c1c736]1114
[5614c7f]1115 ext4_inode_ref_t *inode_ref = enode->inode_ref;
[1c1c736]1116 rc = ext4_filesystem_get_inode_data_block_index(fs, inode_ref->inode, iblock, &fblock);
[6088193]1117 if (rc != EOK) {
1118 // TODO error
1119 ext4fs_node_put(fn);
1120 EXT4FS_DBG("error loading block addr");
1121 return rc;
1122 }
[1c1c736]1123
1124 if (fblock == 0) {
[b12ca16]1125 rc = ext4_balloc_alloc_block(fs, inode_ref, &fblock);
[35f48f2]1126 if (rc != EOK) {
1127 ext4fs_node_put(fn);
1128 async_answer_0(callid, rc);
1129 return rc;
1130 }
1131
[b12ca16]1132 rc = ext4_filesystem_set_inode_data_block_index(fs, inode_ref, iblock, fblock);
1133 if (rc != EOK) {
1134 EXT4FS_DBG("ERROR: setting index failed");
1135 }
[35f48f2]1136 inode_ref->dirty = true;
1137
1138 flags = BLOCK_FLAGS_NOREAD;
[1c1c736]1139 }
1140
[5614c7f]1141 block_t *write_block;
[35f48f2]1142 rc = block_get(&write_block, service_id, fblock, flags);
[1c1c736]1143 if (rc != EOK) {
[6088193]1144 EXT4FS_DBG("error in loading block \%d", rc);
[1c1c736]1145 ext4fs_node_put(fn);
1146 async_answer_0(callid, rc);
1147 return rc;
1148 }
1149
[35f48f2]1150 if (flags == BLOCK_FLAGS_NOREAD) {
1151 memset(write_block->data, 0, block_size);
1152 }
1153
1154 rc = async_data_write_finalize(callid, write_block->data + (pos % block_size), bytes);
1155 if (rc != EOK) {
[1e65444]1156 // TODO error
[35f48f2]1157 EXT4FS_DBG("error in write finalize \%d", rc);
1158 }
1159
[1c1c736]1160 write_block->dirty = true;
1161
1162 rc = block_put(write_block);
1163 if (rc != EOK) {
[6088193]1164 EXT4FS_DBG("error in writing block \%d", rc);
[1c1c736]1165 ext4fs_node_put(fn);
1166 return rc;
1167 }
1168
[5614c7f]1169 uint32_t old_inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[35f48f2]1170 if (pos + bytes > old_inode_size) {
1171 ext4_inode_set_size(inode_ref->inode, pos + bytes);
1172 inode_ref->dirty = true;
[1c1c736]1173 }
[35f48f2]1174
1175 *nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
[1c1c736]1176 *wbytes = bytes;
[35f48f2]1177
[1c1c736]1178 return ext4fs_node_put(fn);
[d3a9ae74]1179}
1180
[3711e7e]1181
[5f6cb14]1182static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
1183 aoff64_t new_size)
[d3a9ae74]1184{
[d5a78e28]1185 int rc;
[e68c834]1186
[5614c7f]1187 fs_node_t *fn;
[d5a78e28]1188 rc = ext4fs_node_get(&fn, service_id, index);
1189 if (rc != EOK) {
1190 return rc;
1191 }
1192
[3d4fd2c]1193 ext4fs_node_t *enode = EXT4FS_NODE(fn);
1194 ext4_inode_ref_t *inode_ref = enode->inode_ref;
1195 ext4_filesystem_t *fs = enode->instance->filesystem;
[d5a78e28]1196
[3d4fd2c]1197 rc = ext4_filesystem_truncate_inode(fs, inode_ref, new_size);
[d5a78e28]1198 ext4fs_node_put(fn);
[1c1c736]1199
[3d4fd2c]1200 return rc;
[d3a9ae74]1201}
1202
[3711e7e]1203
[d3a9ae74]1204static int ext4fs_close(service_id_t service_id, fs_index_t index)
1205{
1206 return EOK;
1207}
1208
[3711e7e]1209
[d3a9ae74]1210static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
1211{
[8be96a0]1212 int rc;
[e68c834]1213
[5614c7f]1214 fs_node_t *fn;
[8be96a0]1215 rc = ext4fs_node_get(&fn, service_id, index);
1216 if (rc != EOK) {
1217 return rc;
1218 }
1219
1220 /* Destroy the inode */
1221 return ext4fs_destroy_node(fn);
[d3a9ae74]1222}
1223
[3711e7e]1224
[d3a9ae74]1225static int ext4fs_sync(service_id_t service_id, fs_index_t index)
1226{
[35f48f2]1227 int rc;
1228
[5614c7f]1229 fs_node_t *fn;
[35f48f2]1230 rc = ext4fs_node_get(&fn, service_id, index);
1231 if (rc != EOK) {
1232 return rc;
1233 }
1234
[5614c7f]1235 ext4fs_node_t *enode = EXT4FS_NODE(fn);
[35f48f2]1236 enode->inode_ref->dirty = true;
1237
1238 return ext4fs_node_put(fn);
[d3a9ae74]1239}
1240
1241vfs_out_ops_t ext4fs_ops = {
1242 .mounted = ext4fs_mounted,
1243 .unmounted = ext4fs_unmounted,
1244 .read = ext4fs_read,
1245 .write = ext4fs_write,
1246 .truncate = ext4fs_truncate,
1247 .close = ext4fs_close,
1248 .destroy = ext4fs_destroy,
1249 .sync = ext4fs_sync,
1250};
1251
1252/**
1253 * @}
[2b9e142]1254 */
Note: See TracBrowser for help on using the repository browser.