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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f787c8e was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • 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 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 <str.h>
39#include "mfs.h"
40
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(void *key)
104{
105 node_key_t *node_key = (node_key_t *)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(void *key, const ht_link_t *item)
118{
119 node_key_t *node_key = (node_key_t *)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 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
280
281static errno_t mfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
282{
283 struct mfs_sb_info *sbi = NULL;
284 errno_t rc;
285
286 /* Initialize libblock */
287 rc = block_init(service_id, 4096);
288 if (rc != EOK)
289 return rc;
290
291 /* Read the superblock */
292 rc = mfs_read_sb(service_id, &sbi);
293 block_fini(service_id);
294
295 return rc;
296}
297
298static errno_t
299mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
300 aoff64_t *size)
301{
302 enum cache_mode cmode;
303 struct mfs_sb_info *sbi = NULL;
304 struct mfs_instance *instance = NULL;
305 errno_t rc;
306
307 /* Check for option enabling write through. */
308 if (str_cmp(opts, "wtcache") == 0)
309 cmode = CACHE_MODE_WT;
310 else
311 cmode = CACHE_MODE_WB;
312
313 /* Initialize libblock */
314 rc = block_init(service_id, 4096);
315 if (rc != EOK)
316 return rc;
317
318 /* Allocate space for filesystem instance */
319 instance = malloc(sizeof(*instance));
320 if (!instance) {
321 rc = ENOMEM;
322 goto out_error;
323 }
324
325 /* Read the superblock */
326 rc = mfs_read_sb(service_id, &sbi);
327 if (rc != EOK)
328 goto out_error;
329
330 rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
331 if (rc != EOK) {
332 mfsdebug("block cache initialization failed\n");
333 rc = EINVAL;
334 goto out_error;
335 }
336
337 /* Initialize the instance structure and remember it */
338 instance->service_id = service_id;
339 instance->sbi = sbi;
340 instance->open_nodes_cnt = 0;
341 rc = fs_instance_create(service_id, instance);
342 if (rc != EOK) {
343 block_cache_fini(service_id);
344 mfsdebug("fs instance creation failed\n");
345 goto out_error;
346 }
347
348 mfsdebug("mount successful\n");
349
350 fs_node_t *fn;
351 mfs_node_get(&fn, service_id, MFS_ROOT_INO);
352
353 struct mfs_node *mroot = fn->data;
354
355 *index = mroot->ino_i->index;
356 *size = mroot->ino_i->i_size;
357
358 return mfs_node_put(fn);
359
360out_error:
361 block_fini(service_id);
362 if (sbi)
363 free(sbi);
364 if (instance)
365 free(instance);
366 return rc;
367}
368
369static errno_t
370mfs_unmounted(service_id_t service_id)
371{
372 struct mfs_instance *inst;
373
374 mfsdebug("%s()\n", __FUNCTION__);
375
376 errno_t r = mfs_instance_get(service_id, &inst);
377 if (r != EOK)
378 return r;
379
380 if (inst->open_nodes_cnt != 0)
381 return EBUSY;
382
383 (void) block_cache_fini(service_id);
384 block_fini(service_id);
385
386 /* Remove and destroy the instance */
387 (void) fs_instance_destroy(service_id);
388 free(inst->sbi);
389 free(inst);
390 return EOK;
391}
392
393service_id_t
394mfs_service_get(fs_node_t *fsnode)
395{
396 struct mfs_node *node = fsnode->data;
397 return node->instance->service_id;
398}
399
400static errno_t
401mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
402{
403 errno_t r;
404 struct mfs_instance *inst;
405 struct mfs_node *mnode;
406 fs_node_t *fsnode;
407 uint32_t inum;
408
409 r = mfs_instance_get(service_id, &inst);
410 if (r != EOK)
411 return r;
412
413 /* Alloc a new inode */
414 r = mfs_alloc_inode(inst, &inum);
415 if (r != EOK)
416 return r;
417
418 struct mfs_ino_info *ino_i;
419
420 ino_i = malloc(sizeof(*ino_i));
421 if (!ino_i) {
422 r = ENOMEM;
423 goto out_err;
424 }
425
426 mnode = malloc(sizeof(*mnode));
427 if (!mnode) {
428 r = ENOMEM;
429 goto out_err_1;
430 }
431
432 fsnode = malloc(sizeof(fs_node_t));
433 if (!fsnode) {
434 r = ENOMEM;
435 goto out_err_2;
436 }
437
438 if (flags & L_DIRECTORY) {
439 ino_i->i_mode = S_IFDIR;
440 ino_i->i_nlinks = 1; /* This accounts for the '.' dentry */
441 } else {
442 ino_i->i_mode = S_IFREG;
443 ino_i->i_nlinks = 0;
444 }
445
446 ino_i->i_uid = 0;
447 ino_i->i_gid = 0;
448 ino_i->i_size = 0;
449 ino_i->i_atime = 0;
450 ino_i->i_mtime = 0;
451 ino_i->i_ctime = 0;
452
453 memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
454 memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
455
456 mfsdebug("new node idx = %d\n", (int) inum);
457
458 ino_i->index = inum;
459 ino_i->dirty = true;
460 mnode->ino_i = ino_i;
461 mnode->instance = inst;
462 mnode->refcnt = 1;
463
464 fibril_mutex_lock(&open_nodes_lock);
465 hash_table_insert(&open_nodes, &mnode->link);
466 fibril_mutex_unlock(&open_nodes_lock);
467 inst->open_nodes_cnt++;
468
469 mnode->ino_i->dirty = true;
470
471 fs_node_initialize(fsnode);
472 fsnode->data = mnode;
473 mnode->fsnode = fsnode;
474 *rfn = fsnode;
475
476 return EOK;
477
478out_err_2:
479 free(mnode);
480out_err_1:
481 free(ino_i);
482out_err:
483 mfs_free_inode(inst, inum);
484 return r;
485}
486
487static errno_t
488mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
489{
490 struct mfs_node *mnode = pfn->data;
491 struct mfs_ino_info *ino_i = mnode->ino_i;
492 struct mfs_dentry_info d_info;
493 errno_t r;
494
495 if (!S_ISDIR(ino_i->i_mode))
496 return ENOTDIR;
497
498 struct mfs_sb_info *sbi = mnode->instance->sbi;
499 const size_t comp_size = str_size(component);
500
501 unsigned i;
502 for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
503 r = mfs_read_dentry(mnode, &d_info, i);
504 if (r != EOK)
505 return r;
506
507 if (!d_info.d_inum) {
508 /* This entry is not used */
509 continue;
510 }
511
512 const size_t dentry_name_size = str_size(d_info.d_name);
513
514 if (comp_size == dentry_name_size &&
515 memcmp(component, d_info.d_name, dentry_name_size) == 0) {
516 /* Hit! */
517 mfs_node_core_get(rfn, mnode->instance,
518 d_info.d_inum);
519 goto found;
520 }
521 }
522 *rfn = NULL;
523found:
524 return EOK;
525}
526
527static aoff64_t
528mfs_size_get(fs_node_t *node)
529{
530 const struct mfs_node *mnode = node->data;
531 return mnode->ino_i->i_size;
532}
533
534static errno_t
535mfs_node_get(fs_node_t **rfn, service_id_t service_id,
536 fs_index_t index)
537{
538 errno_t rc;
539 struct mfs_instance *instance;
540
541 rc = mfs_instance_get(service_id, &instance);
542 if (rc != EOK)
543 return rc;
544
545 return mfs_node_core_get(rfn, instance, index);
546}
547
548static errno_t
549mfs_node_put(fs_node_t *fsnode)
550{
551 errno_t rc = EOK;
552 struct mfs_node *mnode = fsnode->data;
553
554 fibril_mutex_lock(&open_nodes_lock);
555
556 assert(mnode->refcnt > 0);
557 mnode->refcnt--;
558 if (mnode->refcnt == 0) {
559 hash_table_remove_item(&open_nodes, &mnode->link);
560 assert(mnode->instance->open_nodes_cnt > 0);
561 mnode->instance->open_nodes_cnt--;
562 rc = mfs_put_inode(mnode);
563 free(mnode->ino_i);
564 free(mnode);
565 free(fsnode);
566 }
567
568 fibril_mutex_unlock(&open_nodes_lock);
569 return rc;
570}
571
572static errno_t
573mfs_node_open(fs_node_t *fsnode)
574{
575 /*
576 * Opening a file is stateless, nothing
577 * to be done here.
578 */
579 return EOK;
580}
581
582static fs_index_t
583mfs_index_get(fs_node_t *fsnode)
584{
585 struct mfs_node *mnode = fsnode->data;
586 return mnode->ino_i->index;
587}
588
589static unsigned
590mfs_lnkcnt_get(fs_node_t *fsnode)
591{
592 struct mfs_node *mnode = fsnode->data;
593
594 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
595
596 if (S_ISDIR(mnode->ino_i->i_mode)) {
597 if (mnode->ino_i->i_nlinks > 1)
598 return 1;
599 else
600 return 0;
601 } else
602 return mnode->ino_i->i_nlinks;
603}
604
605static errno_t
606mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
607 fs_index_t index)
608{
609 fs_node_t *node = NULL;
610 struct mfs_node *mnode = NULL;
611 errno_t rc;
612
613 fibril_mutex_lock(&open_nodes_lock);
614
615 /* Check if the node is not already open */
616 node_key_t key = {
617 .service_id = inst->service_id,
618 .index = index
619 };
620
621 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
622
623 if (already_open) {
624 mnode = hash_table_get_inst(already_open, struct mfs_node, link);
625
626 *rfn = mnode->fsnode;
627 mnode->refcnt++;
628
629 fibril_mutex_unlock(&open_nodes_lock);
630 return EOK;
631 }
632
633 node = malloc(sizeof(fs_node_t));
634 if (!node) {
635 rc = ENOMEM;
636 goto out_err;
637 }
638
639 fs_node_initialize(node);
640
641 mnode = malloc(sizeof(*mnode));
642 if (!mnode) {
643 rc = ENOMEM;
644 goto out_err;
645 }
646
647 struct mfs_ino_info *ino_i;
648
649 rc = mfs_get_inode(inst, &ino_i, index);
650 if (rc != EOK)
651 goto out_err;
652
653 ino_i->index = index;
654 mnode->ino_i = ino_i;
655 mnode->refcnt = 1;
656
657 mnode->instance = inst;
658 node->data = mnode;
659 mnode->fsnode = node;
660 *rfn = node;
661
662 hash_table_insert(&open_nodes, &mnode->link);
663 inst->open_nodes_cnt++;
664
665 fibril_mutex_unlock(&open_nodes_lock);
666
667 return EOK;
668
669out_err:
670 if (node)
671 free(node);
672 if (mnode)
673 free(mnode);
674 fibril_mutex_unlock(&open_nodes_lock);
675 return rc;
676}
677
678static bool
679mfs_is_directory(fs_node_t *fsnode)
680{
681 const struct mfs_node *node = fsnode->data;
682 return S_ISDIR(node->ino_i->i_mode);
683}
684
685static bool
686mfs_is_file(fs_node_t *fsnode)
687{
688 struct mfs_node *node = fsnode->data;
689 return S_ISREG(node->ino_i->i_mode);
690}
691
692static errno_t
693mfs_root_get(fs_node_t **rfn, service_id_t service_id)
694{
695 errno_t rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
696 return rc;
697}
698
699static errno_t
700mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
701{
702 struct mfs_node *parent = pfn->data;
703 struct mfs_node *child = cfn->data;
704 struct mfs_sb_info *sbi = parent->instance->sbi;
705 bool destroy_dentry = false;
706
707 if (str_size(name) > sbi->max_name_len)
708 return ENAMETOOLONG;
709
710 errno_t r = mfs_insert_dentry(parent, name, child->ino_i->index);
711 if (r != EOK)
712 return r;
713
714 if (S_ISDIR(child->ino_i->i_mode)) {
715 if (child->ino_i->i_nlinks != 1) {
716 /* It's not possible to hardlink directories in MFS */
717 destroy_dentry = true;
718 r = EMLINK;
719 goto exit;
720 }
721 r = mfs_insert_dentry(child, ".", child->ino_i->index);
722 if (r != EOK) {
723 destroy_dentry = true;
724 goto exit;
725 }
726
727 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
728 if (r != EOK) {
729 mfs_remove_dentry(child, ".");
730 destroy_dentry = true;
731 goto exit;
732 }
733
734 parent->ino_i->i_nlinks++;
735 parent->ino_i->dirty = true;
736 }
737
738exit:
739 if (destroy_dentry) {
740 errno_t r2 = mfs_remove_dentry(parent, name);
741 if (r2 != EOK)
742 r = r2;
743 } else {
744 child->ino_i->i_nlinks++;
745 child->ino_i->dirty = true;
746 }
747 return r;
748}
749
750static errno_t
751mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
752{
753 struct mfs_node *parent = pfn->data;
754 struct mfs_node *child = cfn->data;
755 bool has_children;
756 errno_t r;
757
758 if (!parent)
759 return EBUSY;
760
761 r = mfs_has_children(&has_children, cfn);
762 if (r != EOK)
763 return r;
764
765 if (has_children)
766 return ENOTEMPTY;
767
768 r = mfs_remove_dentry(parent, name);
769 if (r != EOK)
770 return r;
771
772 struct mfs_ino_info *chino = child->ino_i;
773
774 assert(chino->i_nlinks >= 1);
775 chino->i_nlinks--;
776 mfsdebug("Links: %d\n", chino->i_nlinks);
777
778 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
779 /*
780 * The child directory will be destroyed, decrease the
781 * parent hard links counter.
782 */
783 parent->ino_i->i_nlinks--;
784 parent->ino_i->dirty = true;
785 }
786
787 chino->dirty = true;
788
789 return r;
790}
791
792static errno_t
793mfs_has_children(bool *has_children, fs_node_t *fsnode)
794{
795 struct mfs_node *mnode = fsnode->data;
796 struct mfs_sb_info *sbi = mnode->instance->sbi;
797 errno_t r;
798
799 *has_children = false;
800
801 if (!S_ISDIR(mnode->ino_i->i_mode))
802 goto out;
803
804 struct mfs_dentry_info d_info;
805
806 /* The first two dentries are always . and .. */
807 unsigned i;
808 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
809 r = mfs_read_dentry(mnode, &d_info, i);
810 if (r != EOK)
811 return r;
812
813 if (d_info.d_inum) {
814 /* A valid entry has been found */
815 *has_children = true;
816 break;
817 }
818 }
819out:
820
821 return EOK;
822}
823
824static errno_t
825mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
826 size_t *rbytes)
827{
828 errno_t rc;
829 errno_t tmp;
830 fs_node_t *fn = NULL;
831
832 rc = mfs_node_get(&fn, service_id, index);
833 if (rc != EOK)
834 return rc;
835 if (!fn)
836 return ENOENT;
837
838 struct mfs_node *mnode;
839 struct mfs_ino_info *ino_i;
840 size_t len, bytes = 0;
841 ipc_call_t call;
842
843 mnode = fn->data;
844 ino_i = mnode->ino_i;
845
846 if (!async_data_read_receive(&call, &len)) {
847 rc = EINVAL;
848 goto out_error;
849 }
850
851 if (S_ISDIR(ino_i->i_mode)) {
852 aoff64_t spos = pos;
853 struct mfs_dentry_info d_info;
854 struct mfs_sb_info *sbi = mnode->instance->sbi;
855
856 if (pos < 2) {
857 /* Skip the first two dentries ('.' and '..') */
858 pos = 2;
859 }
860
861 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
862 rc = mfs_read_dentry(mnode, &d_info, pos);
863 if (rc != EOK)
864 goto out_error;
865
866 if (d_info.d_inum) {
867 /* Dentry found! */
868 goto found;
869 }
870 }
871
872 rc = mfs_node_put(fn);
873 async_answer_0(&call, rc != EOK ? rc : ENOENT);
874 return rc;
875 found:
876 async_data_read_finalize(&call, d_info.d_name,
877 str_size(d_info.d_name) + 1);
878 bytes = ((pos - spos) + 1);
879 } else {
880 struct mfs_sb_info *sbi = mnode->instance->sbi;
881
882 if (pos >= (size_t) ino_i->i_size) {
883 /* Trying to read beyond the end of file */
884 bytes = 0;
885 (void) async_data_read_finalize(&call, NULL, 0);
886 goto out_success;
887 }
888
889 bytes = min(len, sbi->block_size - pos % sbi->block_size);
890 bytes = min(bytes, ino_i->i_size - pos);
891
892 uint32_t zone;
893 block_t *b;
894
895 rc = mfs_read_map(&zone, mnode, pos);
896 if (rc != EOK)
897 goto out_error;
898
899 if (zone == 0) {
900 /* sparse file */
901 uint8_t *buf = malloc(sbi->block_size);
902 if (!buf) {
903 rc = ENOMEM;
904 goto out_error;
905 }
906 memset(buf, 0, sizeof(sbi->block_size));
907 async_data_read_finalize(&call,
908 buf + pos % sbi->block_size, bytes);
909 free(buf);
910 goto out_success;
911 }
912
913 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
914 if (rc != EOK)
915 goto out_error;
916
917 async_data_read_finalize(&call, b->data +
918 pos % sbi->block_size, bytes);
919
920 rc = block_put(b);
921 if (rc != EOK) {
922 mfs_node_put(fn);
923 return rc;
924 }
925 }
926out_success:
927 rc = mfs_node_put(fn);
928 *rbytes = bytes;
929 return rc;
930out_error:
931 tmp = mfs_node_put(fn);
932 async_answer_0(&call, tmp != EOK ? tmp : rc);
933 return tmp != EOK ? tmp : rc;
934}
935
936static errno_t
937mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
938 size_t *wbytes, aoff64_t *nsize)
939{
940 fs_node_t *fn;
941 errno_t r;
942 int flags = BLOCK_FLAGS_NONE;
943
944 r = mfs_node_get(&fn, service_id, index);
945 if (r != EOK)
946 return r;
947 if (!fn)
948 return ENOENT;
949
950 ipc_call_t call;
951 size_t len;
952
953 if (!async_data_write_receive(&call, &len)) {
954 r = EINVAL;
955 goto out_err;
956 }
957
958 struct mfs_node *mnode = fn->data;
959 struct mfs_sb_info *sbi = mnode->instance->sbi;
960 struct mfs_ino_info *ino_i = mnode->ino_i;
961 const size_t bs = sbi->block_size;
962 size_t bytes = min(len, bs - (pos % bs));
963 uint32_t block;
964
965 if (bytes == bs)
966 flags = BLOCK_FLAGS_NOREAD;
967
968 r = mfs_read_map(&block, mnode, pos);
969 if (r != EOK)
970 goto out_err;
971
972 if (block == 0) {
973 uint32_t dummy;
974
975 r = mfs_alloc_zone(mnode->instance, &block);
976 if (r != EOK)
977 goto out_err;
978
979 r = mfs_write_map(mnode, pos, block, &dummy);
980 if (r != EOK) {
981 mfs_free_zone(mnode->instance, block);
982 goto out_err;
983 }
984
985 flags = BLOCK_FLAGS_NOREAD;
986 }
987
988 block_t *b;
989 r = block_get(&b, service_id, block, flags);
990 if (r != EOK)
991 goto out_err;
992
993 if (flags == BLOCK_FLAGS_NOREAD)
994 memset(b->data, 0, sbi->block_size);
995
996 async_data_write_finalize(&call, b->data + (pos % bs), bytes);
997 b->dirty = true;
998
999 r = block_put(b);
1000 if (r != EOK) {
1001 mfs_node_put(fn);
1002 return r;
1003 }
1004
1005 if (pos + bytes > ino_i->i_size) {
1006 ino_i->i_size = pos + bytes;
1007 ino_i->dirty = true;
1008 }
1009 r = mfs_node_put(fn);
1010 *nsize = ino_i->i_size;
1011 *wbytes = bytes;
1012 return r;
1013
1014out_err:
1015 mfs_node_put(fn);
1016 async_answer_0(&call, r);
1017 return r;
1018}
1019
1020static errno_t
1021mfs_destroy(service_id_t service_id, fs_index_t index)
1022{
1023 fs_node_t *fn = NULL;
1024 errno_t r;
1025
1026 r = mfs_node_get(&fn, service_id, index);
1027 if (r != EOK)
1028 return r;
1029 if (!fn)
1030 return ENOENT;
1031
1032 /* Destroy the inode */
1033 return mfs_destroy_node(fn);
1034}
1035
1036static errno_t
1037mfs_destroy_node(fs_node_t *fn)
1038{
1039 struct mfs_node *mnode = fn->data;
1040 bool has_children;
1041 errno_t r;
1042
1043 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
1044
1045 r = mfs_has_children(&has_children, fn);
1046 if (r != EOK)
1047 goto out;
1048
1049 assert(!has_children);
1050
1051 /* Free the entire inode content */
1052 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
1053 if (r != EOK)
1054 goto out;
1055
1056 /* Mark the inode as free in the bitmap */
1057 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
1058
1059out:
1060 mfs_node_put(fn);
1061 return r;
1062}
1063
1064static errno_t
1065mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1066{
1067 fs_node_t *fn;
1068 errno_t r;
1069
1070 r = mfs_node_get(&fn, service_id, index);
1071 if (r != EOK)
1072 return r;
1073 if (!fn)
1074 return r;
1075
1076 struct mfs_node *mnode = fn->data;
1077 struct mfs_ino_info *ino_i = mnode->ino_i;
1078
1079 if (ino_i->i_size == size)
1080 r = EOK;
1081 else
1082 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1083
1084 mfs_node_put(fn);
1085 return r;
1086}
1087
1088static errno_t
1089mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1090{
1091 void *data;
1092 errno_t rc;
1093
1094 rc = fs_instance_get(service_id, &data);
1095 if (rc == EOK)
1096 *instance = (struct mfs_instance *) data;
1097 else {
1098 mfsdebug("instance not found\n");
1099 }
1100
1101 return rc;
1102}
1103
1104static bool
1105check_magic_number(uint16_t magic, bool *native,
1106 mfs_version_t *version, bool *longfilenames)
1107{
1108 bool rc = true;
1109 *longfilenames = false;
1110
1111 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1112 *native = magic == MFS_MAGIC_V1;
1113 *version = MFS_VERSION_V1;
1114 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1115 *native = magic == MFS_MAGIC_V1L;
1116 *version = MFS_VERSION_V1;
1117 *longfilenames = true;
1118 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1119 *native = magic == MFS_MAGIC_V2;
1120 *version = MFS_VERSION_V2;
1121 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1122 *native = magic == MFS_MAGIC_V2L;
1123 *version = MFS_VERSION_V2;
1124 *longfilenames = true;
1125 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1126 *native = magic == MFS_MAGIC_V3;
1127 *version = MFS_VERSION_V3;
1128 } else
1129 rc = false;
1130
1131 return rc;
1132}
1133
1134/** Filesystem sanity check
1135 *
1136 * @param Pointer to the MFS superblock.
1137 *
1138 * @return EOK on success, ENOTSUP otherwise.
1139 */
1140static errno_t
1141mfs_check_sanity(struct mfs_sb_info *sbi)
1142{
1143 if (!is_power_of_two(sbi->block_size) ||
1144 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1145 sbi->block_size > MFS_MAX_BLOCKSIZE)
1146 return ENOTSUP;
1147 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1148 return ENOTSUP;
1149 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1150 return ENOTSUP;
1151 else if (sbi->firstdatazone == 0)
1152 return ENOTSUP;
1153
1154 return EOK;
1155}
1156
1157static errno_t
1158mfs_close(service_id_t service_id, fs_index_t index)
1159{
1160 return 0;
1161}
1162
1163static errno_t
1164mfs_sync(service_id_t service_id, fs_index_t index)
1165{
1166 fs_node_t *fn = NULL;
1167 errno_t rc = mfs_node_get(&fn, service_id, index);
1168 if (rc != EOK)
1169 return rc;
1170 if (!fn)
1171 return ENOENT;
1172
1173 struct mfs_node *mnode = fn->data;
1174 mnode->ino_i->dirty = true;
1175
1176 return mfs_node_put(fn);
1177}
1178
1179/** Check if a given number is a power of two.
1180 *
1181 * @param n The number to check.
1182 *
1183 * @return true if it is a power of two, false otherwise.
1184 */
1185static bool
1186is_power_of_two(uint32_t n)
1187{
1188 if (n == 0)
1189 return false;
1190
1191 return (n & (n - 1)) == 0;
1192}
1193
1194static errno_t
1195mfs_size_block(service_id_t service_id, uint32_t *size)
1196{
1197 struct mfs_instance *inst;
1198 errno_t rc;
1199
1200 rc = mfs_instance_get(service_id, &inst);
1201 if (rc != EOK)
1202 return rc;
1203
1204 if (NULL == inst)
1205 return ENOENT;
1206
1207 *size = inst->sbi->block_size;
1208
1209 return EOK;
1210}
1211
1212static errno_t
1213mfs_total_block_count(service_id_t service_id, uint64_t *count)
1214{
1215 struct mfs_instance *inst;
1216 errno_t rc;
1217
1218 rc = mfs_instance_get(service_id, &inst);
1219 if (rc != EOK)
1220 return rc;
1221
1222 if (NULL == inst)
1223 return ENOENT;
1224
1225 *count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
1226
1227 return EOK;
1228}
1229
1230static errno_t
1231mfs_free_block_count(service_id_t service_id, uint64_t *count)
1232{
1233 uint32_t block_free;
1234
1235 struct mfs_instance *inst;
1236 errno_t rc = mfs_instance_get(service_id, &inst);
1237 if (rc != EOK)
1238 return rc;
1239
1240 struct mfs_sb_info *sbi = inst->sbi;
1241
1242 if (!sbi->nfree_zones_valid) {
1243 /*
1244 * The cached number of free zones is not valid,
1245 * we need to scan the bitmap to retrieve the
1246 * current value.
1247 */
1248
1249 rc = mfs_count_free_zones(inst, &block_free);
1250 if (rc != EOK)
1251 return rc;
1252
1253 sbi->nfree_zones = block_free;
1254 sbi->nfree_zones_valid = true;
1255 }
1256
1257 *count = sbi->nfree_zones;
1258
1259 return rc;
1260}
1261
1262vfs_out_ops_t mfs_ops = {
1263 .fsprobe = mfs_fsprobe,
1264 .mounted = mfs_mounted,
1265 .unmounted = mfs_unmounted,
1266 .read = mfs_read,
1267 .write = mfs_write,
1268 .truncate = mfs_truncate,
1269 .close = mfs_close,
1270 .destroy = mfs_destroy,
1271 .sync = mfs_sync,
1272};
1273
1274/**
1275 * @}
1276 */
1277
Note: See TracBrowser for help on using the repository browser.