source: mainline/uspace/srv/fs/mfs/mfs_ops.c@ 5e801dc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5e801dc was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

  • Property mode set to 100644
File size: 27.5 KB
Line 
1/*
2 * Copyright (c) 2011 Maurizio Lombardi
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 mfs
30 * @{
31 */
32
33#include <stdlib.h>
34#include <fibril_synch.h>
35#include <align.h>
36#include <adt/hash_table.h>
37#include <adt/hash.h>
38#include <str.h>
39#include "mfs.h"
40
41static bool check_magic_number(uint16_t magic, bool *native,
42 mfs_version_t *version, bool *longfilenames);
43static errno_t mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
44 fs_index_t index);
45static errno_t mfs_node_put(fs_node_t *fsnode);
46static errno_t mfs_node_open(fs_node_t *fsnode);
47static fs_index_t mfs_index_get(fs_node_t *fsnode);
48static unsigned mfs_lnkcnt_get(fs_node_t *fsnode);
49static bool mfs_is_directory(fs_node_t *fsnode);
50static bool mfs_is_file(fs_node_t *fsnode);
51static errno_t mfs_has_children(bool *has_children, fs_node_t *fsnode);
52static errno_t mfs_root_get(fs_node_t **rfn, service_id_t service_id);
53static service_id_t mfs_service_get(fs_node_t *fsnode);
54static aoff64_t mfs_size_get(fs_node_t *node);
55static errno_t mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
56static errno_t mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags);
57static errno_t mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
58static errno_t mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
59static errno_t mfs_destroy_node(fs_node_t *fn);
60static errno_t mfs_node_get(fs_node_t **rfn, service_id_t service_id,
61 fs_index_t index);
62static errno_t mfs_instance_get(service_id_t service_id,
63 struct mfs_instance **instance);
64static errno_t mfs_check_sanity(struct mfs_sb_info *sbi);
65static bool is_power_of_two(uint32_t n);
66static errno_t mfs_size_block(service_id_t service_id, uint32_t *size);
67static errno_t mfs_total_block_count(service_id_t service_id, uint64_t *count);
68static errno_t mfs_free_block_count(service_id_t service_id, uint64_t *count);
69
70static hash_table_t open_nodes;
71static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
72
73libfs_ops_t mfs_libfs_ops = {
74 .size_get = mfs_size_get,
75 .root_get = mfs_root_get,
76 .service_get = mfs_service_get,
77 .is_directory = mfs_is_directory,
78 .is_file = mfs_is_file,
79 .node_get = mfs_node_get,
80 .node_put = mfs_node_put,
81 .node_open = mfs_node_open,
82 .index_get = mfs_index_get,
83 .match = mfs_match,
84 .create = mfs_create_node,
85 .link = mfs_link,
86 .unlink = mfs_unlink,
87 .destroy = mfs_destroy_node,
88 .has_children = mfs_has_children,
89 .lnkcnt_get = mfs_lnkcnt_get,
90 .size_block = mfs_size_block,
91 .total_block_count = mfs_total_block_count,
92 .free_block_count = mfs_free_block_count
93};
94
95/* Hash table interface for open nodes hash table */
96typedef struct {
97 service_id_t service_id;
98 fs_index_t index;
99} node_key_t;
100
101static size_t
102open_nodes_key_hash(const void *key)
103{
104 const node_key_t *node_key = key;
105 return hash_combine(node_key->service_id, node_key->index);
106}
107
108static size_t
109open_nodes_hash(const ht_link_t *item)
110{
111 struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
112 return hash_combine(m->instance->service_id, m->ino_i->index);
113}
114
115static bool
116open_nodes_key_equal(const void *key, const ht_link_t *item)
117{
118 const node_key_t *node_key = key;
119 struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
120
121 return node_key->service_id == mnode->instance->service_id &&
122 node_key->index == mnode->ino_i->index;
123}
124
125static hash_table_ops_t open_nodes_ops = {
126 .hash = open_nodes_hash,
127 .key_hash = open_nodes_key_hash,
128 .key_equal = open_nodes_key_equal,
129 .equal = NULL,
130 .remove_callback = NULL,
131};
132
133errno_t
134mfs_global_init(void)
135{
136 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
137 return ENOMEM;
138 }
139 return EOK;
140}
141
142/** Read the superblock.
143 */
144static errno_t mfs_read_sb(service_id_t service_id, struct mfs_sb_info **rsbi)
145{
146 struct mfs_superblock *sb = NULL;
147 struct mfs3_superblock *sb3 = NULL;
148 struct mfs_sb_info *sbi;
149 size_t bsize;
150 bool native, longnames;
151 mfs_version_t version;
152 uint16_t magic;
153 errno_t rc;
154
155 /* Allocate space for generic MFS superblock */
156 sbi = malloc(sizeof(*sbi));
157 if (!sbi) {
158 rc = ENOMEM;
159 goto out_error;
160 }
161
162 sb = malloc(MFS_SUPERBLOCK_SIZE);
163 if (!sb) {
164 rc = ENOMEM;
165 goto out_error;
166 }
167
168 rc = block_get_bsize(service_id, &bsize);
169 if (rc != EOK) {
170 rc = EIO;
171 goto out_error;
172 }
173
174 /* We don't support other block size than 512 */
175 if (bsize != 512) {
176 rc = ENOTSUP;
177 goto out_error;
178 }
179
180 /* Read the superblock */
181 rc = block_read_direct(service_id, MFS_SUPERBLOCK << 1, 2, sb);
182 if (rc != EOK)
183 goto out_error;
184
185 sb3 = (struct mfs3_superblock *) sb;
186
187 if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
188 /* This is a V1 or V2 Minix filesystem */
189 magic = sb->s_magic;
190 } else if (check_magic_number(sb3->s_magic, &native,
191 &version, &longnames)) {
192 /* This is a V3 Minix filesystem */
193 magic = sb3->s_magic;
194 } else {
195 /* Not recognized */
196 mfsdebug("magic number not recognized\n");
197 rc = ENOTSUP;
198 goto out_error;
199 }
200
201 mfsdebug("magic number recognized = %04x\n", magic);
202
203 /* Fill superblock info structure */
204
205 sbi->fs_version = version;
206 sbi->long_names = longnames;
207 sbi->native = native;
208 sbi->magic = magic;
209 sbi->isearch = 0;
210 sbi->zsearch = 0;
211 sbi->nfree_zones_valid = false;
212 sbi->nfree_zones = 0;
213
214 if (version == MFS_VERSION_V3) {
215 sbi->ninodes = conv32(native, sb3->s_ninodes);
216 sbi->ibmap_blocks = conv16(native, sb3->s_ibmap_blocks);
217 sbi->zbmap_blocks = conv16(native, sb3->s_zbmap_blocks);
218 sbi->firstdatazone = conv16(native, sb3->s_first_data_zone);
219 sbi->log2_zone_size = conv16(native, sb3->s_log2_zone_size);
220 sbi->max_file_size = conv32(native, sb3->s_max_file_size);
221 sbi->nzones = conv32(native, sb3->s_nzones);
222 sbi->block_size = conv16(native, sb3->s_block_size);
223 sbi->ino_per_block = V3_INODES_PER_BLOCK(sbi->block_size);
224 sbi->dirsize = MFS3_DIRSIZE;
225 sbi->max_name_len = MFS3_MAX_NAME_LEN;
226 } else {
227 sbi->ninodes = conv16(native, sb->s_ninodes);
228 sbi->ibmap_blocks = conv16(native, sb->s_ibmap_blocks);
229 sbi->zbmap_blocks = conv16(native, sb->s_zbmap_blocks);
230 sbi->firstdatazone = conv16(native, sb->s_first_data_zone);
231 sbi->log2_zone_size = conv16(native, sb->s_log2_zone_size);
232 sbi->max_file_size = conv32(native, sb->s_max_file_size);
233 sbi->block_size = MFS_BLOCKSIZE;
234 if (version == MFS_VERSION_V2) {
235 sbi->nzones = conv32(native, sb->s_nzones2);
236 sbi->ino_per_block = V2_INODES_PER_BLOCK;
237 } else {
238 sbi->nzones = conv16(native, sb->s_nzones);
239 sbi->ino_per_block = V1_INODES_PER_BLOCK;
240 }
241 sbi->dirsize = longnames ? MFSL_DIRSIZE : MFS_DIRSIZE;
242 sbi->max_name_len = longnames ? MFS_L_MAX_NAME_LEN :
243 MFS_MAX_NAME_LEN;
244 }
245
246 if (sbi->log2_zone_size != 0) {
247 /*
248 * In MFS, file space is allocated per zones.
249 * Zones are a collection of consecutive blocks on disk.
250 *
251 * The current MFS implementation supports only filesystems
252 * where the size of a zone is equal to the
253 * size of a block.
254 */
255 rc = ENOTSUP;
256 goto out_error;
257 }
258
259 sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
260 if ((rc = mfs_check_sanity(sbi)) != EOK) {
261 fprintf(stderr, "Filesystem corrupted, invalid superblock");
262 goto out_error;
263 }
264
265 mfsdebug("read superblock successful\n");
266
267 free(sb);
268 *rsbi = sbi;
269 return EOK;
270
271out_error:
272 if (sb)
273 free(sb);
274 if (sbi)
275 free(sbi);
276 return rc;
277}
278
279static errno_t mfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
280{
281 struct mfs_sb_info *sbi = NULL;
282 errno_t rc;
283
284 /* Initialize libblock */
285 rc = block_init(service_id, 4096);
286 if (rc != EOK)
287 return rc;
288
289 /* Read the superblock */
290 rc = mfs_read_sb(service_id, &sbi);
291 block_fini(service_id);
292
293 return rc;
294}
295
296static errno_t
297mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
298 aoff64_t *size)
299{
300 enum cache_mode cmode;
301 struct mfs_sb_info *sbi = NULL;
302 struct mfs_instance *instance = NULL;
303 errno_t rc;
304
305 /* Check for option enabling write through. */
306 if (str_cmp(opts, "wtcache") == 0)
307 cmode = CACHE_MODE_WT;
308 else
309 cmode = CACHE_MODE_WB;
310
311 /* Initialize libblock */
312 rc = block_init(service_id, 4096);
313 if (rc != EOK)
314 return rc;
315
316 /* Allocate space for filesystem instance */
317 instance = malloc(sizeof(*instance));
318 if (!instance) {
319 rc = ENOMEM;
320 goto out_error;
321 }
322
323 /* Read the superblock */
324 rc = mfs_read_sb(service_id, &sbi);
325 if (rc != EOK)
326 goto out_error;
327
328 rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
329 if (rc != EOK) {
330 mfsdebug("block cache initialization failed\n");
331 rc = EINVAL;
332 goto out_error;
333 }
334
335 /* Initialize the instance structure and remember it */
336 instance->service_id = service_id;
337 instance->sbi = sbi;
338 instance->open_nodes_cnt = 0;
339 rc = fs_instance_create(service_id, instance);
340 if (rc != EOK) {
341 block_cache_fini(service_id);
342 mfsdebug("fs instance creation failed\n");
343 goto out_error;
344 }
345
346 mfsdebug("mount successful\n");
347
348 fs_node_t *fn;
349 mfs_node_get(&fn, service_id, MFS_ROOT_INO);
350
351 struct mfs_node *mroot = fn->data;
352
353 *index = mroot->ino_i->index;
354 *size = mroot->ino_i->i_size;
355
356 return mfs_node_put(fn);
357
358out_error:
359 block_fini(service_id);
360 if (sbi)
361 free(sbi);
362 if (instance)
363 free(instance);
364 return rc;
365}
366
367static errno_t
368mfs_unmounted(service_id_t service_id)
369{
370 struct mfs_instance *inst;
371
372 mfsdebug("%s()\n", __FUNCTION__);
373
374 errno_t r = mfs_instance_get(service_id, &inst);
375 if (r != EOK)
376 return r;
377
378 if (inst->open_nodes_cnt != 0)
379 return EBUSY;
380
381 (void) block_cache_fini(service_id);
382 block_fini(service_id);
383
384 /* Remove and destroy the instance */
385 (void) fs_instance_destroy(service_id);
386 free(inst->sbi);
387 free(inst);
388 return EOK;
389}
390
391service_id_t
392mfs_service_get(fs_node_t *fsnode)
393{
394 struct mfs_node *node = fsnode->data;
395 return node->instance->service_id;
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.