source: mainline/uspace/srv/fs/mfs/mfs_ops.c

Last change on this file was 0db0df2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Hash table improvements

Implement hash_table_foreach macro, analogous to list_foreach.

Remove superfluous argument to hash_table_find_next().
(If the user needs to recheck the part of the list already
checked by hash_table_find(), they can just rerun that function.)

Add hash argument to hash_table_ops_t::key_equal.
The big change here is that users with big keys can store the hash
value alongside key in their entries, and for the low low cost of
sizeof(size_t) bytes eliminate a bunch of expensive key comparisons.

Also added a hash function for strings and arbitrary data.
Found this one by asking ChatGPT, because the latency of accesses
to my book collection is currently a couple of hours.

+ Some drive-by unused #include removal.

  • Property mode set to 100644
File size: 27.5 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2011 Maurizio Lombardi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup mfs
31 * @{
32 */
33
34#include <stdlib.h>
35#include <fibril_synch.h>
36#include <align.h>
37#include <adt/hash_table.h>
38#include <adt/hash.h>
39#include <str.h>
40#include "mfs.h"
41
42static bool check_magic_number(uint16_t magic, bool *native,
43 mfs_version_t *version, bool *longfilenames);
44static errno_t mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
45 fs_index_t index);
46static errno_t mfs_node_put(fs_node_t *fsnode);
47static errno_t mfs_node_open(fs_node_t *fsnode);
48static fs_index_t mfs_index_get(fs_node_t *fsnode);
49static unsigned mfs_lnkcnt_get(fs_node_t *fsnode);
50static bool mfs_is_directory(fs_node_t *fsnode);
51static bool mfs_is_file(fs_node_t *fsnode);
52static errno_t mfs_has_children(bool *has_children, fs_node_t *fsnode);
53static errno_t mfs_root_get(fs_node_t **rfn, service_id_t service_id);
54static service_id_t mfs_service_get(fs_node_t *fsnode);
55static aoff64_t mfs_size_get(fs_node_t *node);
56static errno_t mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
57static errno_t mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags);
58static errno_t mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
59static errno_t mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
60static errno_t mfs_destroy_node(fs_node_t *fn);
61static errno_t mfs_node_get(fs_node_t **rfn, service_id_t service_id,
62 fs_index_t index);
63static errno_t mfs_instance_get(service_id_t service_id,
64 struct mfs_instance **instance);
65static errno_t mfs_check_sanity(struct mfs_sb_info *sbi);
66static bool is_power_of_two(uint32_t n);
67static errno_t mfs_size_block(service_id_t service_id, uint32_t *size);
68static errno_t mfs_total_block_count(service_id_t service_id, uint64_t *count);
69static errno_t mfs_free_block_count(service_id_t service_id, uint64_t *count);
70
71static hash_table_t open_nodes;
72static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
73
74libfs_ops_t mfs_libfs_ops = {
75 .size_get = mfs_size_get,
76 .root_get = mfs_root_get,
77 .service_get = mfs_service_get,
78 .is_directory = mfs_is_directory,
79 .is_file = mfs_is_file,
80 .node_get = mfs_node_get,
81 .node_put = mfs_node_put,
82 .node_open = mfs_node_open,
83 .index_get = mfs_index_get,
84 .match = mfs_match,
85 .create = mfs_create_node,
86 .link = mfs_link,
87 .unlink = mfs_unlink,
88 .destroy = mfs_destroy_node,
89 .has_children = mfs_has_children,
90 .lnkcnt_get = mfs_lnkcnt_get,
91 .size_block = mfs_size_block,
92 .total_block_count = mfs_total_block_count,
93 .free_block_count = mfs_free_block_count
94};
95
96/* Hash table interface for open nodes hash table */
97typedef struct {
98 service_id_t service_id;
99 fs_index_t index;
100} node_key_t;
101
102static size_t
103open_nodes_key_hash(const void *key)
104{
105 const node_key_t *node_key = key;
106 return hash_combine(node_key->service_id, node_key->index);
107}
108
109static size_t
110open_nodes_hash(const ht_link_t *item)
111{
112 struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
113 return hash_combine(m->instance->service_id, m->ino_i->index);
114}
115
116static bool
117open_nodes_key_equal(const void *key, size_t hash, const ht_link_t *item)
118{
119 const node_key_t *node_key = key;
120 struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
121
122 return node_key->service_id == mnode->instance->service_id &&
123 node_key->index == mnode->ino_i->index;
124}
125
126static const hash_table_ops_t open_nodes_ops = {
127 .hash = open_nodes_hash,
128 .key_hash = open_nodes_key_hash,
129 .key_equal = open_nodes_key_equal,
130 .equal = NULL,
131 .remove_callback = NULL,
132};
133
134errno_t
135mfs_global_init(void)
136{
137 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
138 return ENOMEM;
139 }
140 return EOK;
141}
142
143/** Read the superblock.
144 */
145static errno_t mfs_read_sb(service_id_t service_id, struct mfs_sb_info **rsbi)
146{
147 struct mfs_superblock *sb = NULL;
148 struct mfs3_superblock *sb3 = NULL;
149 struct mfs_sb_info *sbi;
150 size_t bsize;
151 bool native, longnames;
152 mfs_version_t version;
153 uint16_t magic;
154 errno_t rc;
155
156 /* Allocate space for generic MFS superblock */
157 sbi = malloc(sizeof(*sbi));
158 if (!sbi) {
159 rc = ENOMEM;
160 goto out_error;
161 }
162
163 sb = malloc(MFS_SUPERBLOCK_SIZE);
164 if (!sb) {
165 rc = ENOMEM;
166 goto out_error;
167 }
168
169 rc = block_get_bsize(service_id, &bsize);
170 if (rc != EOK) {
171 rc = EIO;
172 goto out_error;
173 }
174
175 /* We don't support other block size than 512 */
176 if (bsize != 512) {
177 rc = ENOTSUP;
178 goto out_error;
179 }
180
181 /* Read the superblock */
182 rc = block_read_direct(service_id, MFS_SUPERBLOCK << 1, 2, sb);
183 if (rc != EOK)
184 goto out_error;
185
186 sb3 = (struct mfs3_superblock *) sb;
187
188 if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
189 /* This is a V1 or V2 Minix filesystem */
190 magic = sb->s_magic;
191 } else if (check_magic_number(sb3->s_magic, &native,
192 &version, &longnames)) {
193 /* This is a V3 Minix filesystem */
194 magic = sb3->s_magic;
195 } else {
196 /* Not recognized */
197 mfsdebug("magic number not recognized\n");
198 rc = ENOTSUP;
199 goto out_error;
200 }
201
202 mfsdebug("magic number recognized = %04x\n", magic);
203
204 /* Fill superblock info structure */
205
206 sbi->fs_version = version;
207 sbi->long_names = longnames;
208 sbi->native = native;
209 sbi->magic = magic;
210 sbi->isearch = 0;
211 sbi->zsearch = 0;
212 sbi->nfree_zones_valid = false;
213 sbi->nfree_zones = 0;
214
215 if (version == MFS_VERSION_V3) {
216 sbi->ninodes = conv32(native, sb3->s_ninodes);
217 sbi->ibmap_blocks = conv16(native, sb3->s_ibmap_blocks);
218 sbi->zbmap_blocks = conv16(native, sb3->s_zbmap_blocks);
219 sbi->firstdatazone = conv16(native, sb3->s_first_data_zone);
220 sbi->log2_zone_size = conv16(native, sb3->s_log2_zone_size);
221 sbi->max_file_size = conv32(native, sb3->s_max_file_size);
222 sbi->nzones = conv32(native, sb3->s_nzones);
223 sbi->block_size = conv16(native, sb3->s_block_size);
224 sbi->ino_per_block = V3_INODES_PER_BLOCK(sbi->block_size);
225 sbi->dirsize = MFS3_DIRSIZE;
226 sbi->max_name_len = MFS3_MAX_NAME_LEN;
227 } else {
228 sbi->ninodes = conv16(native, sb->s_ninodes);
229 sbi->ibmap_blocks = conv16(native, sb->s_ibmap_blocks);
230 sbi->zbmap_blocks = conv16(native, sb->s_zbmap_blocks);
231 sbi->firstdatazone = conv16(native, sb->s_first_data_zone);
232 sbi->log2_zone_size = conv16(native, sb->s_log2_zone_size);
233 sbi->max_file_size = conv32(native, sb->s_max_file_size);
234 sbi->block_size = MFS_BLOCKSIZE;
235 if (version == MFS_VERSION_V2) {
236 sbi->nzones = conv32(native, sb->s_nzones2);
237 sbi->ino_per_block = V2_INODES_PER_BLOCK;
238 } else {
239 sbi->nzones = conv16(native, sb->s_nzones);
240 sbi->ino_per_block = V1_INODES_PER_BLOCK;
241 }
242 sbi->dirsize = longnames ? MFSL_DIRSIZE : MFS_DIRSIZE;
243 sbi->max_name_len = longnames ? MFS_L_MAX_NAME_LEN :
244 MFS_MAX_NAME_LEN;
245 }
246
247 if (sbi->log2_zone_size != 0) {
248 /*
249 * In MFS, file space is allocated per zones.
250 * Zones are a collection of consecutive blocks on disk.
251 *
252 * The current MFS implementation supports only filesystems
253 * where the size of a zone is equal to the
254 * size of a block.
255 */
256 rc = ENOTSUP;
257 goto out_error;
258 }
259
260 sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
261 if ((rc = mfs_check_sanity(sbi)) != EOK) {
262 fprintf(stderr, "Filesystem corrupted, invalid superblock");
263 goto out_error;
264 }
265
266 mfsdebug("read superblock successful\n");
267
268 free(sb);
269 *rsbi = sbi;
270 return EOK;
271
272out_error:
273 if (sb)
274 free(sb);
275 if (sbi)
276 free(sbi);
277 return rc;
278}
279
280static errno_t mfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
281{
282 struct mfs_sb_info *sbi = NULL;
283 errno_t rc;
284
285 /* Initialize libblock */
286 rc = block_init(service_id);
287 if (rc != EOK)
288 return rc;
289
290 /* Read the superblock */
291 rc = mfs_read_sb(service_id, &sbi);
292 block_fini(service_id);
293
294 return rc;
295}
296
297static errno_t
298mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
299 aoff64_t *size)
300{
301 enum cache_mode cmode;
302 struct mfs_sb_info *sbi = NULL;
303 struct mfs_instance *instance = NULL;
304 errno_t rc;
305
306 /* Check for option enabling write through. */
307 if (str_cmp(opts, "wtcache") == 0)
308 cmode = CACHE_MODE_WT;
309 else
310 cmode = CACHE_MODE_WB;
311
312 /* Initialize libblock */
313 rc = block_init(service_id);
314 if (rc != EOK)
315 return rc;
316
317 /* Allocate space for filesystem instance */
318 instance = malloc(sizeof(*instance));
319 if (!instance) {
320 rc = ENOMEM;
321 goto out_error;
322 }
323
324 /* Read the superblock */
325 rc = mfs_read_sb(service_id, &sbi);
326 if (rc != EOK)
327 goto out_error;
328
329 rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
330 if (rc != EOK) {
331 mfsdebug("block cache initialization failed\n");
332 rc = EINVAL;
333 goto out_error;
334 }
335
336 /* Initialize the instance structure and remember it */
337 instance->service_id = service_id;
338 instance->sbi = sbi;
339 instance->open_nodes_cnt = 0;
340 rc = fs_instance_create(service_id, instance);
341 if (rc != EOK) {
342 block_cache_fini(service_id);
343 mfsdebug("fs instance creation failed\n");
344 goto out_error;
345 }
346
347 mfsdebug("mount successful\n");
348
349 fs_node_t *fn;
350 mfs_node_get(&fn, service_id, MFS_ROOT_INO);
351
352 struct mfs_node *mroot = fn->data;
353
354 *index = mroot->ino_i->index;
355 *size = mroot->ino_i->i_size;
356
357 return mfs_node_put(fn);
358
359out_error:
360 block_fini(service_id);
361 if (sbi)
362 free(sbi);
363 if (instance)
364 free(instance);
365 return rc;
366}
367
368static errno_t
369mfs_unmounted(service_id_t service_id)
370{
371 struct mfs_instance *inst;
372
373 mfsdebug("%s()\n", __FUNCTION__);
374
375 errno_t r = mfs_instance_get(service_id, &inst);
376 if (r != EOK)
377 return r;
378
379 if (inst->open_nodes_cnt != 0)
380 return EBUSY;
381
382 (void) block_cache_fini(service_id);
383 block_fini(service_id);
384
385 /* Remove and destroy the instance */
386 (void) fs_instance_destroy(service_id);
387 free(inst->sbi);
388 free(inst);
389 return EOK;
390}
391
392service_id_t
393mfs_service_get(fs_node_t *fsnode)
394{
395 return 0;
396}
397
398static errno_t
399mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
400{
401 errno_t r;
402 struct mfs_instance *inst;
403 struct mfs_node *mnode;
404 fs_node_t *fsnode;
405 uint32_t inum;
406
407 r = mfs_instance_get(service_id, &inst);
408 if (r != EOK)
409 return r;
410
411 /* Alloc a new inode */
412 r = mfs_alloc_inode(inst, &inum);
413 if (r != EOK)
414 return r;
415
416 struct mfs_ino_info *ino_i;
417
418 ino_i = malloc(sizeof(*ino_i));
419 if (!ino_i) {
420 r = ENOMEM;
421 goto out_err;
422 }
423
424 mnode = malloc(sizeof(*mnode));
425 if (!mnode) {
426 r = ENOMEM;
427 goto out_err_1;
428 }
429
430 fsnode = malloc(sizeof(fs_node_t));
431 if (!fsnode) {
432 r = ENOMEM;
433 goto out_err_2;
434 }
435
436 if (flags & L_DIRECTORY) {
437 ino_i->i_mode = S_IFDIR;
438 ino_i->i_nlinks = 1; /* This accounts for the '.' dentry */
439 } else {
440 ino_i->i_mode = S_IFREG;
441 ino_i->i_nlinks = 0;
442 }
443
444 ino_i->i_uid = 0;
445 ino_i->i_gid = 0;
446 ino_i->i_size = 0;
447 ino_i->i_atime = 0;
448 ino_i->i_mtime = 0;
449 ino_i->i_ctime = 0;
450
451 memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
452 memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
453
454 mfsdebug("new node idx = %d\n", (int) inum);
455
456 ino_i->index = inum;
457 ino_i->dirty = true;
458 mnode->ino_i = ino_i;
459 mnode->instance = inst;
460 mnode->refcnt = 1;
461
462 fibril_mutex_lock(&open_nodes_lock);
463 hash_table_insert(&open_nodes, &mnode->link);
464 fibril_mutex_unlock(&open_nodes_lock);
465 inst->open_nodes_cnt++;
466
467 mnode->ino_i->dirty = true;
468
469 fs_node_initialize(fsnode);
470 fsnode->data = mnode;
471 mnode->fsnode = fsnode;
472 *rfn = fsnode;
473
474 return EOK;
475
476out_err_2:
477 free(mnode);
478out_err_1:
479 free(ino_i);
480out_err:
481 mfs_free_inode(inst, inum);
482 return r;
483}
484
485static errno_t
486mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
487{
488 struct mfs_node *mnode = pfn->data;
489 struct mfs_ino_info *ino_i = mnode->ino_i;
490 struct mfs_dentry_info d_info;
491 errno_t r;
492
493 if (!S_ISDIR(ino_i->i_mode))
494 return ENOTDIR;
495
496 struct mfs_sb_info *sbi = mnode->instance->sbi;
497 const size_t comp_size = str_size(component);
498
499 unsigned i;
500 for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
501 r = mfs_read_dentry(mnode, &d_info, i);
502 if (r != EOK)
503 return r;
504
505 if (!d_info.d_inum) {
506 /* This entry is not used */
507 continue;
508 }
509
510 const size_t dentry_name_size = str_size(d_info.d_name);
511
512 if (comp_size == dentry_name_size &&
513 memcmp(component, d_info.d_name, dentry_name_size) == 0) {
514 /* Hit! */
515 mfs_node_core_get(rfn, mnode->instance,
516 d_info.d_inum);
517 goto found;
518 }
519 }
520 *rfn = NULL;
521found:
522 return EOK;
523}
524
525static aoff64_t
526mfs_size_get(fs_node_t *node)
527{
528 const struct mfs_node *mnode = node->data;
529 return mnode->ino_i->i_size;
530}
531
532static errno_t
533mfs_node_get(fs_node_t **rfn, service_id_t service_id,
534 fs_index_t index)
535{
536 errno_t rc;
537 struct mfs_instance *instance;
538
539 rc = mfs_instance_get(service_id, &instance);
540 if (rc != EOK)
541 return rc;
542
543 return mfs_node_core_get(rfn, instance, index);
544}
545
546static errno_t
547mfs_node_put(fs_node_t *fsnode)
548{
549 errno_t rc = EOK;
550 struct mfs_node *mnode = fsnode->data;
551
552 fibril_mutex_lock(&open_nodes_lock);
553
554 assert(mnode->refcnt > 0);
555 mnode->refcnt--;
556 if (mnode->refcnt == 0) {
557 hash_table_remove_item(&open_nodes, &mnode->link);
558 assert(mnode->instance->open_nodes_cnt > 0);
559 mnode->instance->open_nodes_cnt--;
560 rc = mfs_put_inode(mnode);
561 free(mnode->ino_i);
562 free(mnode);
563 free(fsnode);
564 }
565
566 fibril_mutex_unlock(&open_nodes_lock);
567 return rc;
568}
569
570static errno_t
571mfs_node_open(fs_node_t *fsnode)
572{
573 /*
574 * Opening a file is stateless, nothing
575 * to be done here.
576 */
577 return EOK;
578}
579
580static fs_index_t
581mfs_index_get(fs_node_t *fsnode)
582{
583 struct mfs_node *mnode = fsnode->data;
584 return mnode->ino_i->index;
585}
586
587static unsigned
588mfs_lnkcnt_get(fs_node_t *fsnode)
589{
590 struct mfs_node *mnode = fsnode->data;
591
592 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
593
594 if (S_ISDIR(mnode->ino_i->i_mode)) {
595 if (mnode->ino_i->i_nlinks > 1)
596 return 1;
597 else
598 return 0;
599 } else
600 return mnode->ino_i->i_nlinks;
601}
602
603static errno_t
604mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
605 fs_index_t index)
606{
607 fs_node_t *node = NULL;
608 struct mfs_node *mnode = NULL;
609 errno_t rc;
610
611 fibril_mutex_lock(&open_nodes_lock);
612
613 /* Check if the node is not already open */
614 node_key_t key = {
615 .service_id = inst->service_id,
616 .index = index
617 };
618
619 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
620
621 if (already_open) {
622 mnode = hash_table_get_inst(already_open, struct mfs_node, link);
623
624 *rfn = mnode->fsnode;
625 mnode->refcnt++;
626
627 fibril_mutex_unlock(&open_nodes_lock);
628 return EOK;
629 }
630
631 node = malloc(sizeof(fs_node_t));
632 if (!node) {
633 rc = ENOMEM;
634 goto out_err;
635 }
636
637 fs_node_initialize(node);
638
639 mnode = malloc(sizeof(*mnode));
640 if (!mnode) {
641 rc = ENOMEM;
642 goto out_err;
643 }
644
645 struct mfs_ino_info *ino_i;
646
647 rc = mfs_get_inode(inst, &ino_i, index);
648 if (rc != EOK)
649 goto out_err;
650
651 ino_i->index = index;
652 mnode->ino_i = ino_i;
653 mnode->refcnt = 1;
654
655 mnode->instance = inst;
656 node->data = mnode;
657 mnode->fsnode = node;
658 *rfn = node;
659
660 hash_table_insert(&open_nodes, &mnode->link);
661 inst->open_nodes_cnt++;
662
663 fibril_mutex_unlock(&open_nodes_lock);
664
665 return EOK;
666
667out_err:
668 if (node)
669 free(node);
670 if (mnode)
671 free(mnode);
672 fibril_mutex_unlock(&open_nodes_lock);
673 return rc;
674}
675
676static bool
677mfs_is_directory(fs_node_t *fsnode)
678{
679 const struct mfs_node *node = fsnode->data;
680 return S_ISDIR(node->ino_i->i_mode);
681}
682
683static bool
684mfs_is_file(fs_node_t *fsnode)
685{
686 struct mfs_node *node = fsnode->data;
687 return S_ISREG(node->ino_i->i_mode);
688}
689
690static errno_t
691mfs_root_get(fs_node_t **rfn, service_id_t service_id)
692{
693 errno_t rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
694 return rc;
695}
696
697static errno_t
698mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
699{
700 struct mfs_node *parent = pfn->data;
701 struct mfs_node *child = cfn->data;
702 struct mfs_sb_info *sbi = parent->instance->sbi;
703 bool destroy_dentry = false;
704
705 if (str_size(name) > sbi->max_name_len)
706 return ENAMETOOLONG;
707
708 errno_t r = mfs_insert_dentry(parent, name, child->ino_i->index);
709 if (r != EOK)
710 return r;
711
712 if (S_ISDIR(child->ino_i->i_mode)) {
713 if (child->ino_i->i_nlinks != 1) {
714 /* It's not possible to hardlink directories in MFS */
715 destroy_dentry = true;
716 r = EMLINK;
717 goto exit;
718 }
719 r = mfs_insert_dentry(child, ".", child->ino_i->index);
720 if (r != EOK) {
721 destroy_dentry = true;
722 goto exit;
723 }
724
725 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
726 if (r != EOK) {
727 mfs_remove_dentry(child, ".");
728 destroy_dentry = true;
729 goto exit;
730 }
731
732 parent->ino_i->i_nlinks++;
733 parent->ino_i->dirty = true;
734 }
735
736exit:
737 if (destroy_dentry) {
738 errno_t r2 = mfs_remove_dentry(parent, name);
739 if (r2 != EOK)
740 r = r2;
741 } else {
742 child->ino_i->i_nlinks++;
743 child->ino_i->dirty = true;
744 }
745 return r;
746}
747
748static errno_t
749mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
750{
751 struct mfs_node *parent = pfn->data;
752 struct mfs_node *child = cfn->data;
753 bool has_children;
754 errno_t r;
755
756 if (!parent)
757 return EBUSY;
758
759 r = mfs_has_children(&has_children, cfn);
760 if (r != EOK)
761 return r;
762
763 if (has_children)
764 return ENOTEMPTY;
765
766 r = mfs_remove_dentry(parent, name);
767 if (r != EOK)
768 return r;
769
770 struct mfs_ino_info *chino = child->ino_i;
771
772 assert(chino->i_nlinks >= 1);
773 chino->i_nlinks--;
774 mfsdebug("Links: %d\n", chino->i_nlinks);
775
776 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
777 /*
778 * The child directory will be destroyed, decrease the
779 * parent hard links counter.
780 */
781 parent->ino_i->i_nlinks--;
782 parent->ino_i->dirty = true;
783 }
784
785 chino->dirty = true;
786
787 return r;
788}
789
790static errno_t
791mfs_has_children(bool *has_children, fs_node_t *fsnode)
792{
793 struct mfs_node *mnode = fsnode->data;
794 struct mfs_sb_info *sbi = mnode->instance->sbi;
795 errno_t r;
796
797 *has_children = false;
798
799 if (!S_ISDIR(mnode->ino_i->i_mode))
800 goto out;
801
802 struct mfs_dentry_info d_info;
803
804 /* The first two dentries are always . and .. */
805 unsigned i;
806 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
807 r = mfs_read_dentry(mnode, &d_info, i);
808 if (r != EOK)
809 return r;
810
811 if (d_info.d_inum) {
812 /* A valid entry has been found */
813 *has_children = true;
814 break;
815 }
816 }
817out:
818
819 return EOK;
820}
821
822static errno_t
823mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
824 size_t *rbytes)
825{
826 errno_t rc;
827 errno_t tmp;
828 fs_node_t *fn = NULL;
829
830 rc = mfs_node_get(&fn, service_id, index);
831 if (rc != EOK)
832 return rc;
833 if (!fn)
834 return ENOENT;
835
836 struct mfs_node *mnode;
837 struct mfs_ino_info *ino_i;
838 size_t len, bytes = 0;
839 ipc_call_t call;
840
841 mnode = fn->data;
842 ino_i = mnode->ino_i;
843
844 if (!async_data_read_receive(&call, &len)) {
845 rc = EINVAL;
846 goto out_error;
847 }
848
849 if (S_ISDIR(ino_i->i_mode)) {
850 aoff64_t spos = pos;
851 struct mfs_dentry_info d_info;
852 struct mfs_sb_info *sbi = mnode->instance->sbi;
853
854 if (pos < 2) {
855 /* Skip the first two dentries ('.' and '..') */
856 pos = 2;
857 }
858
859 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
860 rc = mfs_read_dentry(mnode, &d_info, pos);
861 if (rc != EOK)
862 goto out_error;
863
864 if (d_info.d_inum) {
865 /* Dentry found! */
866 goto found;
867 }
868 }
869
870 rc = mfs_node_put(fn);
871 async_answer_0(&call, rc != EOK ? rc : ENOENT);
872 return rc;
873 found:
874 async_data_read_finalize(&call, d_info.d_name,
875 str_size(d_info.d_name) + 1);
876 bytes = ((pos - spos) + 1);
877 } else {
878 struct mfs_sb_info *sbi = mnode->instance->sbi;
879
880 if (pos >= (size_t) ino_i->i_size) {
881 /* Trying to read beyond the end of file */
882 bytes = 0;
883 (void) async_data_read_finalize(&call, NULL, 0);
884 goto out_success;
885 }
886
887 bytes = min(len, sbi->block_size - pos % sbi->block_size);
888 bytes = min(bytes, ino_i->i_size - pos);
889
890 uint32_t zone;
891 block_t *b;
892
893 rc = mfs_read_map(&zone, mnode, pos);
894 if (rc != EOK)
895 goto out_error;
896
897 if (zone == 0) {
898 /* sparse file */
899 uint8_t *buf = malloc(sbi->block_size);
900 if (!buf) {
901 rc = ENOMEM;
902 goto out_error;
903 }
904 memset(buf, 0, sizeof(sbi->block_size));
905 async_data_read_finalize(&call,
906 buf + pos % sbi->block_size, bytes);
907 free(buf);
908 goto out_success;
909 }
910
911 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
912 if (rc != EOK)
913 goto out_error;
914
915 async_data_read_finalize(&call, b->data +
916 pos % sbi->block_size, bytes);
917
918 rc = block_put(b);
919 if (rc != EOK) {
920 mfs_node_put(fn);
921 return rc;
922 }
923 }
924out_success:
925 rc = mfs_node_put(fn);
926 *rbytes = bytes;
927 return rc;
928out_error:
929 tmp = mfs_node_put(fn);
930 async_answer_0(&call, tmp != EOK ? tmp : rc);
931 return tmp != EOK ? tmp : rc;
932}
933
934static errno_t
935mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
936 size_t *wbytes, aoff64_t *nsize)
937{
938 fs_node_t *fn;
939 errno_t r;
940 int flags = BLOCK_FLAGS_NONE;
941
942 r = mfs_node_get(&fn, service_id, index);
943 if (r != EOK)
944 return r;
945 if (!fn)
946 return ENOENT;
947
948 ipc_call_t call;
949 size_t len;
950
951 if (!async_data_write_receive(&call, &len)) {
952 r = EINVAL;
953 goto out_err;
954 }
955
956 struct mfs_node *mnode = fn->data;
957 struct mfs_sb_info *sbi = mnode->instance->sbi;
958 struct mfs_ino_info *ino_i = mnode->ino_i;
959 const size_t bs = sbi->block_size;
960 size_t bytes = min(len, bs - (pos % bs));
961 uint32_t block;
962
963 if (bytes == bs)
964 flags = BLOCK_FLAGS_NOREAD;
965
966 r = mfs_read_map(&block, mnode, pos);
967 if (r != EOK)
968 goto out_err;
969
970 if (block == 0) {
971 uint32_t dummy;
972
973 r = mfs_alloc_zone(mnode->instance, &block);
974 if (r != EOK)
975 goto out_err;
976
977 r = mfs_write_map(mnode, pos, block, &dummy);
978 if (r != EOK) {
979 mfs_free_zone(mnode->instance, block);
980 goto out_err;
981 }
982
983 flags = BLOCK_FLAGS_NOREAD;
984 }
985
986 block_t *b;
987 r = block_get(&b, service_id, block, flags);
988 if (r != EOK)
989 goto out_err;
990
991 if (flags == BLOCK_FLAGS_NOREAD)
992 memset(b->data, 0, sbi->block_size);
993
994 async_data_write_finalize(&call, b->data + (pos % bs), bytes);
995 b->dirty = true;
996
997 r = block_put(b);
998 if (r != EOK) {
999 mfs_node_put(fn);
1000 return r;
1001 }
1002
1003 if (pos + bytes > ino_i->i_size) {
1004 ino_i->i_size = pos + bytes;
1005 ino_i->dirty = true;
1006 }
1007 r = mfs_node_put(fn);
1008 *nsize = ino_i->i_size;
1009 *wbytes = bytes;
1010 return r;
1011
1012out_err:
1013 mfs_node_put(fn);
1014 async_answer_0(&call, r);
1015 return r;
1016}
1017
1018static errno_t
1019mfs_destroy(service_id_t service_id, fs_index_t index)
1020{
1021 fs_node_t *fn = NULL;
1022 errno_t r;
1023
1024 r = mfs_node_get(&fn, service_id, index);
1025 if (r != EOK)
1026 return r;
1027 if (!fn)
1028 return ENOENT;
1029
1030 /* Destroy the inode */
1031 return mfs_destroy_node(fn);
1032}
1033
1034static errno_t
1035mfs_destroy_node(fs_node_t *fn)
1036{
1037 struct mfs_node *mnode = fn->data;
1038 bool has_children;
1039 errno_t r;
1040
1041 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
1042
1043 r = mfs_has_children(&has_children, fn);
1044 if (r != EOK)
1045 goto out;
1046
1047 assert(!has_children);
1048
1049 /* Free the entire inode content */
1050 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
1051 if (r != EOK)
1052 goto out;
1053
1054 /* Mark the inode as free in the bitmap */
1055 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
1056
1057out:
1058 mfs_node_put(fn);
1059 return r;
1060}
1061
1062static errno_t
1063mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1064{
1065 fs_node_t *fn;
1066 errno_t r;
1067
1068 r = mfs_node_get(&fn, service_id, index);
1069 if (r != EOK)
1070 return r;
1071 if (!fn)
1072 return r;
1073
1074 struct mfs_node *mnode = fn->data;
1075 struct mfs_ino_info *ino_i = mnode->ino_i;
1076
1077 if (ino_i->i_size == size)
1078 r = EOK;
1079 else
1080 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1081
1082 mfs_node_put(fn);
1083 return r;
1084}
1085
1086static errno_t
1087mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1088{
1089 void *data;
1090 errno_t rc;
1091
1092 rc = fs_instance_get(service_id, &data);
1093 if (rc == EOK)
1094 *instance = (struct mfs_instance *) data;
1095 else {
1096 mfsdebug("instance not found\n");
1097 }
1098
1099 return rc;
1100}
1101
1102static bool
1103check_magic_number(uint16_t magic, bool *native,
1104 mfs_version_t *version, bool *longfilenames)
1105{
1106 bool rc = true;
1107 *longfilenames = false;
1108
1109 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1110 *native = magic == MFS_MAGIC_V1;
1111 *version = MFS_VERSION_V1;
1112 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1113 *native = magic == MFS_MAGIC_V1L;
1114 *version = MFS_VERSION_V1;
1115 *longfilenames = true;
1116 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1117 *native = magic == MFS_MAGIC_V2;
1118 *version = MFS_VERSION_V2;
1119 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1120 *native = magic == MFS_MAGIC_V2L;
1121 *version = MFS_VERSION_V2;
1122 *longfilenames = true;
1123 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1124 *native = magic == MFS_MAGIC_V3;
1125 *version = MFS_VERSION_V3;
1126 } else
1127 rc = false;
1128
1129 return rc;
1130}
1131
1132/** Filesystem sanity check
1133 *
1134 * @param Pointer to the MFS superblock.
1135 *
1136 * @return EOK on success, ENOTSUP otherwise.
1137 */
1138static errno_t
1139mfs_check_sanity(struct mfs_sb_info *sbi)
1140{
1141 if (!is_power_of_two(sbi->block_size) ||
1142 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1143 sbi->block_size > MFS_MAX_BLOCKSIZE)
1144 return ENOTSUP;
1145 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1146 return ENOTSUP;
1147 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1148 return ENOTSUP;
1149 else if (sbi->firstdatazone == 0)
1150 return ENOTSUP;
1151
1152 return EOK;
1153}
1154
1155static errno_t
1156mfs_close(service_id_t service_id, fs_index_t index)
1157{
1158 return 0;
1159}
1160
1161static errno_t
1162mfs_sync(service_id_t service_id, fs_index_t index)
1163{
1164 fs_node_t *fn = NULL;
1165 errno_t rc = mfs_node_get(&fn, service_id, index);
1166 if (rc != EOK)
1167 return rc;
1168 if (!fn)
1169 return ENOENT;
1170
1171 struct mfs_node *mnode = fn->data;
1172 mnode->ino_i->dirty = true;
1173
1174 return mfs_node_put(fn);
1175}
1176
1177/** Check if a given number is a power of two.
1178 *
1179 * @param n The number to check.
1180 *
1181 * @return true if it is a power of two, false otherwise.
1182 */
1183static bool
1184is_power_of_two(uint32_t n)
1185{
1186 if (n == 0)
1187 return false;
1188
1189 return (n & (n - 1)) == 0;
1190}
1191
1192static errno_t
1193mfs_size_block(service_id_t service_id, uint32_t *size)
1194{
1195 struct mfs_instance *inst;
1196 errno_t rc;
1197
1198 rc = mfs_instance_get(service_id, &inst);
1199 if (rc != EOK)
1200 return rc;
1201
1202 if (NULL == inst)
1203 return ENOENT;
1204
1205 *size = inst->sbi->block_size;
1206
1207 return EOK;
1208}
1209
1210static errno_t
1211mfs_total_block_count(service_id_t service_id, uint64_t *count)
1212{
1213 struct mfs_instance *inst;
1214 errno_t rc;
1215
1216 rc = mfs_instance_get(service_id, &inst);
1217 if (rc != EOK)
1218 return rc;
1219
1220 if (NULL == inst)
1221 return ENOENT;
1222
1223 *count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
1224
1225 return EOK;
1226}
1227
1228static errno_t
1229mfs_free_block_count(service_id_t service_id, uint64_t *count)
1230{
1231 uint32_t block_free;
1232
1233 struct mfs_instance *inst;
1234 errno_t rc = mfs_instance_get(service_id, &inst);
1235 if (rc != EOK)
1236 return rc;
1237
1238 struct mfs_sb_info *sbi = inst->sbi;
1239
1240 if (!sbi->nfree_zones_valid) {
1241 /*
1242 * The cached number of free zones is not valid,
1243 * we need to scan the bitmap to retrieve the
1244 * current value.
1245 */
1246
1247 rc = mfs_count_free_zones(inst, &block_free);
1248 if (rc != EOK)
1249 return rc;
1250
1251 sbi->nfree_zones = block_free;
1252 sbi->nfree_zones_valid = true;
1253 }
1254
1255 *count = sbi->nfree_zones;
1256
1257 return rc;
1258}
1259
1260vfs_out_ops_t mfs_ops = {
1261 .fsprobe = mfs_fsprobe,
1262 .mounted = mfs_mounted,
1263 .unmounted = mfs_unmounted,
1264 .read = mfs_read,
1265 .write = mfs_write,
1266 .truncate = mfs_truncate,
1267 .close = mfs_close,
1268 .destroy = mfs_destroy,
1269 .sync = mfs_sync,
1270};
1271
1272/**
1273 * @}
1274 */
Note: See TracBrowser for help on using the repository browser.