source: mainline/uspace/srv/fs/mfs/mfs_ops.c@ 25a179e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 25a179e was 4f30222, checked in by Jakub Jermar <jakub@…>, 8 years ago

VFS_OUT_MOUNTED does not need to return the link count

  • Property mode set to 100644
File size: 27.2 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 fs
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 "mfs.h"
39
40
41static bool check_magic_number(uint16_t magic, bool *native,
42 mfs_version_t *version, bool *longfilenames);
43static int mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
44 fs_index_t index);
45static int mfs_node_put(fs_node_t *fsnode);
46static int 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 int mfs_has_children(bool *has_children, fs_node_t *fsnode);
52static int 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 int mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
56static int mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags);
57static int mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
58static int mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
59static int mfs_destroy_node(fs_node_t *fn);
60static int mfs_node_get(fs_node_t **rfn, service_id_t service_id,
61 fs_index_t index);
62static int mfs_instance_get(service_id_t service_id,
63 struct mfs_instance **instance);
64static int mfs_check_sanity(struct mfs_sb_info *sbi);
65static bool is_power_of_two(uint32_t n);
66static int mfs_size_block(service_id_t service_id, uint32_t *size);
67static int mfs_total_block_count(service_id_t service_id, uint64_t *count);
68static int 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(void *key)
103{
104 node_key_t *node_key = (node_key_t*)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(void *key, const ht_link_t *item)
117{
118 node_key_t *node_key = (node_key_t*)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
133int
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 int 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 int 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 /* In MFS, file space is allocated per zones.
248 * Zones are a collection of consecutive blocks on disk.
249 *
250 * The current MFS implementation supports only filesystems
251 * where the size of a zone is equal to the
252 * size of a block.
253 */
254 rc = ENOTSUP;
255 goto out_error;
256 }
257
258 sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
259 if ((rc = mfs_check_sanity(sbi)) != EOK) {
260 fprintf(stderr, "Filesystem corrupted, invalid superblock");
261 goto out_error;
262 }
263
264 mfsdebug("read superblock successful\n");
265
266 free(sb);
267 *rsbi = sbi;
268 return EOK;
269
270out_error:
271 if (sb)
272 free(sb);
273 if (sbi)
274 free(sbi);
275 return rc;
276}
277
278
279static int mfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
280{
281 struct mfs_sb_info *sbi = NULL;
282 int 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 int
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 int 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 int
368mfs_unmounted(service_id_t service_id)
369{
370 struct mfs_instance *inst;
371
372 mfsdebug("%s()\n", __FUNCTION__);
373
374 int 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 int
399mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
400{
401 int 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 int
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 int 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 int
533mfs_node_get(fs_node_t **rfn, service_id_t service_id,
534 fs_index_t index)
535{
536 int 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 int
547mfs_node_put(fs_node_t *fsnode)
548{
549 int 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 int
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 int
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 int 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 int
691mfs_root_get(fs_node_t **rfn, service_id_t service_id)
692{
693 int rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
694 return rc;
695}
696
697static int
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 int 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 int 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 int
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 int 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 /* The child directory will be destroyed, decrease the
778 * parent hard links counter.
779 */
780 parent->ino_i->i_nlinks--;
781 parent->ino_i->dirty = true;
782 }
783
784 chino->dirty = true;
785
786 return r;
787}
788
789static int
790mfs_has_children(bool *has_children, fs_node_t *fsnode)
791{
792 struct mfs_node *mnode = fsnode->data;
793 struct mfs_sb_info *sbi = mnode->instance->sbi;
794 int r;
795
796 *has_children = false;
797
798 if (!S_ISDIR(mnode->ino_i->i_mode))
799 goto out;
800
801 struct mfs_dentry_info d_info;
802
803 /* The first two dentries are always . and .. */
804 unsigned i;
805 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
806 r = mfs_read_dentry(mnode, &d_info, i);
807 if (r != EOK)
808 return r;
809
810 if (d_info.d_inum) {
811 /* A valid entry has been found */
812 *has_children = true;
813 break;
814 }
815 }
816out:
817
818 return EOK;
819}
820
821static int
822mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
823 size_t *rbytes)
824{
825 int rc;
826 fs_node_t *fn = NULL;
827
828 rc = mfs_node_get(&fn, service_id, index);
829 if (rc != EOK)
830 return rc;
831 if (!fn)
832 return ENOENT;
833
834 struct mfs_node *mnode;
835 struct mfs_ino_info *ino_i;
836 size_t len, bytes = 0;
837 ipc_callid_t callid;
838
839 mnode = fn->data;
840 ino_i = mnode->ino_i;
841
842 if (!async_data_read_receive(&callid, &len)) {
843 rc = EINVAL;
844 goto out_error;
845 }
846
847 if (S_ISDIR(ino_i->i_mode)) {
848 aoff64_t spos = pos;
849 struct mfs_dentry_info d_info;
850 struct mfs_sb_info *sbi = mnode->instance->sbi;
851
852 if (pos < 2) {
853 /* Skip the first two dentries ('.' and '..') */
854 pos = 2;
855 }
856
857 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
858 rc = mfs_read_dentry(mnode, &d_info, pos);
859 if (rc != EOK)
860 goto out_error;
861
862 if (d_info.d_inum) {
863 /* Dentry found! */
864 goto found;
865 }
866 }
867
868 rc = mfs_node_put(fn);
869 async_answer_0(callid, rc != EOK ? rc : ENOENT);
870 return rc;
871found:
872 async_data_read_finalize(callid, d_info.d_name,
873 str_size(d_info.d_name) + 1);
874 bytes = ((pos - spos) + 1);
875 } else {
876 struct mfs_sb_info *sbi = mnode->instance->sbi;
877
878 if (pos >= (size_t) ino_i->i_size) {
879 /* Trying to read beyond the end of file */
880 bytes = 0;
881 (void) async_data_read_finalize(callid, NULL, 0);
882 goto out_success;
883 }
884
885 bytes = min(len, sbi->block_size - pos % sbi->block_size);
886 bytes = min(bytes, ino_i->i_size - pos);
887
888 uint32_t zone;
889 block_t *b;
890
891 rc = mfs_read_map(&zone, mnode, pos);
892 if (rc != EOK)
893 goto out_error;
894
895 if (zone == 0) {
896 /* sparse file */
897 uint8_t *buf = malloc(sbi->block_size);
898 if (!buf) {
899 rc = ENOMEM;
900 goto out_error;
901 }
902 memset(buf, 0, sizeof(sbi->block_size));
903 async_data_read_finalize(callid,
904 buf + pos % sbi->block_size, bytes);
905 free(buf);
906 goto out_success;
907 }
908
909 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
910 if (rc != EOK)
911 goto out_error;
912
913 async_data_read_finalize(callid, b->data +
914 pos % sbi->block_size, bytes);
915
916 rc = block_put(b);
917 if (rc != EOK) {
918 mfs_node_put(fn);
919 return rc;
920 }
921 }
922out_success:
923 rc = mfs_node_put(fn);
924 *rbytes = bytes;
925 return rc;
926out_error:
927 ;
928 int tmp = mfs_node_put(fn);
929 async_answer_0(callid, tmp != EOK ? tmp : rc);
930 return tmp != EOK ? tmp : rc;
931}
932
933static int
934mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
935 size_t *wbytes, aoff64_t *nsize)
936{
937 fs_node_t *fn;
938 int r;
939 int flags = BLOCK_FLAGS_NONE;
940
941 r = mfs_node_get(&fn, service_id, index);
942 if (r != EOK)
943 return r;
944 if (!fn)
945 return ENOENT;
946
947 ipc_callid_t callid;
948 size_t len;
949
950 if (!async_data_write_receive(&callid, &len)) {
951 r = EINVAL;
952 goto out_err;
953 }
954
955 struct mfs_node *mnode = fn->data;
956 struct mfs_sb_info *sbi = mnode->instance->sbi;
957 struct mfs_ino_info *ino_i = mnode->ino_i;
958 const size_t bs = sbi->block_size;
959 size_t bytes = min(len, bs - (pos % bs));
960 uint32_t block;
961
962 if (bytes == bs)
963 flags = BLOCK_FLAGS_NOREAD;
964
965 r = mfs_read_map(&block, mnode, pos);
966 if (r != EOK)
967 goto out_err;
968
969 if (block == 0) {
970 uint32_t dummy;
971
972 r = mfs_alloc_zone(mnode->instance, &block);
973 if (r != EOK)
974 goto out_err;
975
976 r = mfs_write_map(mnode, pos, block, &dummy);
977 if (r != EOK) {
978 mfs_free_zone(mnode->instance, block);
979 goto out_err;
980 }
981
982 flags = BLOCK_FLAGS_NOREAD;
983 }
984
985 block_t *b;
986 r = block_get(&b, service_id, block, flags);
987 if (r != EOK)
988 goto out_err;
989
990 if (flags == BLOCK_FLAGS_NOREAD)
991 memset(b->data, 0, sbi->block_size);
992
993 async_data_write_finalize(callid, b->data + (pos % bs), bytes);
994 b->dirty = true;
995
996 r = block_put(b);
997 if (r != EOK) {
998 mfs_node_put(fn);
999 return r;
1000 }
1001
1002 if (pos + bytes > ino_i->i_size) {
1003 ino_i->i_size = pos + bytes;
1004 ino_i->dirty = true;
1005 }
1006 r = mfs_node_put(fn);
1007 *nsize = ino_i->i_size;
1008 *wbytes = bytes;
1009 return r;
1010
1011out_err:
1012 mfs_node_put(fn);
1013 async_answer_0(callid, r);
1014 return r;
1015}
1016
1017static int
1018mfs_destroy(service_id_t service_id, fs_index_t index)
1019{
1020 fs_node_t *fn = NULL;
1021 int r;
1022
1023 r = mfs_node_get(&fn, service_id, index);
1024 if (r != EOK)
1025 return r;
1026 if (!fn)
1027 return ENOENT;
1028
1029 /* Destroy the inode */
1030 return mfs_destroy_node(fn);
1031}
1032
1033static int
1034mfs_destroy_node(fs_node_t *fn)
1035{
1036 struct mfs_node *mnode = fn->data;
1037 bool has_children;
1038 int r;
1039
1040 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
1041
1042 r = mfs_has_children(&has_children, fn);
1043 if (r != EOK)
1044 goto out;
1045
1046 assert(!has_children);
1047
1048 /* Free the entire inode content */
1049 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
1050 if (r != EOK)
1051 goto out;
1052
1053 /* Mark the inode as free in the bitmap */
1054 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
1055
1056out:
1057 mfs_node_put(fn);
1058 return r;
1059}
1060
1061static int
1062mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1063{
1064 fs_node_t *fn;
1065 int r;
1066
1067 r = mfs_node_get(&fn, service_id, index);
1068 if (r != EOK)
1069 return r;
1070 if (!fn)
1071 return r;
1072
1073 struct mfs_node *mnode = fn->data;
1074 struct mfs_ino_info *ino_i = mnode->ino_i;
1075
1076 if (ino_i->i_size == size)
1077 r = EOK;
1078 else
1079 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1080
1081 mfs_node_put(fn);
1082 return r;
1083}
1084
1085static int
1086mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1087{
1088 void *data;
1089 int rc;
1090
1091 rc = fs_instance_get(service_id, &data);
1092 if (rc == EOK)
1093 *instance = (struct mfs_instance *) data;
1094 else {
1095 mfsdebug("instance not found\n");
1096 }
1097
1098 return rc;
1099}
1100
1101static bool
1102check_magic_number(uint16_t magic, bool *native,
1103 mfs_version_t *version, bool *longfilenames)
1104{
1105 bool rc = true;
1106 *longfilenames = false;
1107
1108 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1109 *native = magic == MFS_MAGIC_V1;
1110 *version = MFS_VERSION_V1;
1111 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1112 *native = magic == MFS_MAGIC_V1L;
1113 *version = MFS_VERSION_V1;
1114 *longfilenames = true;
1115 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1116 *native = magic == MFS_MAGIC_V2;
1117 *version = MFS_VERSION_V2;
1118 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1119 *native = magic == MFS_MAGIC_V2L;
1120 *version = MFS_VERSION_V2;
1121 *longfilenames = true;
1122 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1123 *native = magic == MFS_MAGIC_V3;
1124 *version = MFS_VERSION_V3;
1125 } else
1126 rc = false;
1127
1128 return rc;
1129}
1130
1131/** Filesystem sanity check
1132 *
1133 * @param Pointer to the MFS superblock.
1134 *
1135 * @return EOK on success, ENOTSUP otherwise.
1136 */
1137static int
1138mfs_check_sanity(struct mfs_sb_info *sbi)
1139{
1140 if (!is_power_of_two(sbi->block_size) ||
1141 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1142 sbi->block_size > MFS_MAX_BLOCKSIZE)
1143 return ENOTSUP;
1144 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1145 return ENOTSUP;
1146 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1147 return ENOTSUP;
1148 else if (sbi->firstdatazone == 0)
1149 return ENOTSUP;
1150
1151 return EOK;
1152}
1153
1154static int
1155mfs_close(service_id_t service_id, fs_index_t index)
1156{
1157 return 0;
1158}
1159
1160static int
1161mfs_sync(service_id_t service_id, fs_index_t index)
1162{
1163 fs_node_t *fn = NULL;
1164 int rc = mfs_node_get(&fn, service_id, index);
1165 if (rc != EOK)
1166 return rc;
1167 if (!fn)
1168 return ENOENT;
1169
1170 struct mfs_node *mnode = fn->data;
1171 mnode->ino_i->dirty = true;
1172
1173 return mfs_node_put(fn);
1174}
1175
1176/** Check if a given number is a power of two.
1177 *
1178 * @param n The number to check.
1179 *
1180 * @return true if it is a power of two, false otherwise.
1181 */
1182static bool
1183is_power_of_two(uint32_t n)
1184{
1185 if (n == 0)
1186 return false;
1187
1188 return (n & (n - 1)) == 0;
1189}
1190
1191static int
1192mfs_size_block(service_id_t service_id, uint32_t *size)
1193{
1194 struct mfs_instance *inst;
1195 int rc;
1196
1197 rc = mfs_instance_get(service_id, &inst);
1198 if (rc != EOK)
1199 return rc;
1200
1201 if (NULL == inst)
1202 return ENOENT;
1203
1204 *size = inst->sbi->block_size;
1205
1206 return EOK;
1207}
1208
1209static int
1210mfs_total_block_count(service_id_t service_id, uint64_t *count)
1211{
1212 struct mfs_instance *inst;
1213 int rc;
1214
1215 rc = mfs_instance_get(service_id, &inst);
1216 if (rc != EOK)
1217 return rc;
1218
1219 if (NULL == inst)
1220 return ENOENT;
1221
1222 *count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
1223
1224 return EOK;
1225}
1226
1227static int
1228mfs_free_block_count(service_id_t service_id, uint64_t *count)
1229{
1230 uint32_t block_free;
1231
1232 struct mfs_instance *inst;
1233 int rc = mfs_instance_get(service_id, &inst);
1234 if (rc != EOK)
1235 return rc;
1236
1237 struct mfs_sb_info *sbi = inst->sbi;
1238
1239 if (!sbi->nfree_zones_valid) {
1240 /* The cached number of free zones is not valid,
1241 * we need to scan the bitmap to retrieve the
1242 * current value.
1243 */
1244
1245 rc = mfs_count_free_zones(inst, &block_free);
1246 if (rc != EOK)
1247 return rc;
1248
1249 sbi->nfree_zones = block_free;
1250 sbi->nfree_zones_valid = true;
1251 }
1252
1253 *count = sbi->nfree_zones;
1254
1255 return rc;
1256}
1257
1258vfs_out_ops_t mfs_ops = {
1259 .fsprobe = mfs_fsprobe,
1260 .mounted = mfs_mounted,
1261 .unmounted = mfs_unmounted,
1262 .read = mfs_read,
1263 .write = mfs_write,
1264 .truncate = mfs_truncate,
1265 .close = mfs_close,
1266 .destroy = mfs_destroy,
1267 .sync = mfs_sync,
1268};
1269
1270/**
1271 * @}
1272 */
1273
Note: See TracBrowser for help on using the repository browser.