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

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

Categorize the remaining orphan doxygroups

  • 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
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.