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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 64e63ce1 was 64e63ce1, checked in by Maurizio Lombardi <m.lombardi85@…>, 12 years ago

mfs: try to restore a consistent filesystem status in case of error

  • Property mode set to 100644
File size: 25.1 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);
66
67static hash_table_t open_nodes;
68static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
69
70libfs_ops_t mfs_libfs_ops = {
71 .size_get = mfs_size_get,
72 .root_get = mfs_root_get,
73 .service_get = mfs_service_get,
74 .is_directory = mfs_is_directory,
75 .is_file = mfs_is_file,
76 .node_get = mfs_node_get,
77 .node_put = mfs_node_put,
78 .node_open = mfs_node_open,
79 .index_get = mfs_index_get,
80 .match = mfs_match,
81 .create = mfs_create_node,
82 .link = mfs_link,
83 .unlink = mfs_unlink,
84 .destroy = mfs_destroy_node,
85 .has_children = mfs_has_children,
86 .lnkcnt_get = mfs_lnkcnt_get
87};
88
89/* Hash table interface for open nodes hash table */
90typedef struct {
91 service_id_t service_id;
92 fs_index_t index;
93} node_key_t;
94
95static size_t
96open_nodes_key_hash(void *key)
97{
98 node_key_t *node_key = (node_key_t*)key;
99 return hash_combine(node_key->service_id, node_key->index);
100}
101
102static size_t
103open_nodes_hash(const ht_link_t *item)
104{
105 struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
106 return hash_combine(m->instance->service_id, m->ino_i->index);
107}
108
109static bool
110open_nodes_key_equal(void *key, const ht_link_t *item)
111{
112 node_key_t *node_key = (node_key_t*)key;
113 struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
114
115 return node_key->service_id == mnode->instance->service_id
116 && node_key->index == mnode->ino_i->index;
117}
118
119static hash_table_ops_t open_nodes_ops = {
120 .hash = open_nodes_hash,
121 .key_hash = open_nodes_key_hash,
122 .key_equal = open_nodes_key_equal,
123 .equal = NULL,
124 .remove_callback = NULL,
125};
126
127int
128mfs_global_init(void)
129{
130 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
131 return ENOMEM;
132 }
133 return EOK;
134}
135
136static int
137mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
138 aoff64_t *size, unsigned *linkcnt)
139{
140 enum cache_mode cmode;
141 struct mfs_superblock *sb = NULL;
142 struct mfs3_superblock *sb3 = NULL;
143 struct mfs_sb_info *sbi = NULL;
144 struct mfs_instance *instance = NULL;
145 bool native, longnames;
146 mfs_version_t version;
147 uint16_t magic;
148 int rc;
149
150 /* Check for option enabling write through. */
151 if (str_cmp(opts, "wtcache") == 0)
152 cmode = CACHE_MODE_WT;
153 else
154 cmode = CACHE_MODE_WB;
155
156 /* initialize libblock */
157 rc = block_init(EXCHANGE_SERIALIZE, service_id, 4096);
158 if (rc != EOK)
159 return rc;
160
161 /* Allocate space for generic MFS superblock */
162 sbi = malloc(sizeof(*sbi));
163 if (!sbi) {
164 rc = ENOMEM;
165 goto out_error;
166 }
167
168 /* Allocate space for filesystem instance */
169 instance = malloc(sizeof(*instance));
170 if (!instance) {
171 rc = ENOMEM;
172 goto out_error;
173 }
174
175 sb = malloc(MFS_SUPERBLOCK_SIZE);
176 if (!sb) {
177 rc = ENOMEM;
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
213 if (version == MFS_VERSION_V3) {
214 sbi->ninodes = conv32(native, sb3->s_ninodes);
215 sbi->ibmap_blocks = conv16(native, sb3->s_ibmap_blocks);
216 sbi->zbmap_blocks = conv16(native, sb3->s_zbmap_blocks);
217 sbi->firstdatazone = conv16(native, sb3->s_first_data_zone);
218 sbi->log2_zone_size = conv16(native, sb3->s_log2_zone_size);
219 sbi->max_file_size = conv32(native, sb3->s_max_file_size);
220 sbi->nzones = conv32(native, sb3->s_nzones);
221 sbi->block_size = conv16(native, sb3->s_block_size);
222 sbi->ino_per_block = V3_INODES_PER_BLOCK(sbi->block_size);
223 sbi->dirsize = MFS3_DIRSIZE;
224 sbi->max_name_len = MFS3_MAX_NAME_LEN;
225 } else {
226 sbi->ninodes = conv16(native, sb->s_ninodes);
227 sbi->ibmap_blocks = conv16(native, sb->s_ibmap_blocks);
228 sbi->zbmap_blocks = conv16(native, sb->s_zbmap_blocks);
229 sbi->firstdatazone = conv16(native, sb->s_first_data_zone);
230 sbi->log2_zone_size = conv16(native, sb->s_log2_zone_size);
231 sbi->max_file_size = conv32(native, sb->s_max_file_size);
232 sbi->block_size = MFS_BLOCKSIZE;
233 if (version == MFS_VERSION_V2) {
234 sbi->nzones = conv32(native, sb->s_nzones2);
235 sbi->ino_per_block = V2_INODES_PER_BLOCK;
236 } else {
237 sbi->nzones = conv16(native, sb->s_nzones);
238 sbi->ino_per_block = V1_INODES_PER_BLOCK;
239 }
240 sbi->dirsize = longnames ? MFSL_DIRSIZE : MFS_DIRSIZE;
241 sbi->max_name_len = longnames ? MFS_L_MAX_NAME_LEN :
242 MFS_MAX_NAME_LEN;
243 }
244
245 if (sbi->log2_zone_size != 0) {
246 /* In MFS, file space is allocated per zones.
247 * Zones are a collection of consecutive blocks on disk.
248 *
249 * The current MFS implementation supports only filesystems
250 * where the size of a zone is equal to the
251 * size of a block.
252 */
253 rc = ENOTSUP;
254 goto out_error;
255 }
256
257 sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
258 if ((rc = mfs_check_sanity(sbi)) != EOK) {
259 fprintf(stderr, "Filesystem corrupted, invalid superblock");
260 goto out_error;
261 }
262
263 rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
264 if (rc != EOK) {
265 mfsdebug("block cache initialization failed\n");
266 rc = EINVAL;
267 goto out_error;
268 }
269
270 /* Initialize the instance structure and remember it */
271 instance->service_id = service_id;
272 instance->sbi = sbi;
273 instance->open_nodes_cnt = 0;
274 rc = fs_instance_create(service_id, instance);
275 if (rc != EOK) {
276 block_cache_fini(service_id);
277 mfsdebug("fs instance creation failed\n");
278 goto out_error;
279 }
280
281 mfsdebug("mount successful\n");
282
283 fs_node_t *fn;
284 mfs_node_get(&fn, service_id, MFS_ROOT_INO);
285
286 struct mfs_node *mroot = fn->data;
287
288 *index = mroot->ino_i->index;
289 *size = mroot->ino_i->i_size;
290 *linkcnt = 1;
291
292 free(sb);
293
294 return mfs_node_put(fn);
295
296out_error:
297 block_fini(service_id);
298 if (sb)
299 free(sb);
300 if (sbi)
301 free(sbi);
302 if(instance)
303 free(instance);
304 return rc;
305}
306
307static int
308mfs_unmounted(service_id_t service_id)
309{
310 struct mfs_instance *inst;
311
312 mfsdebug("%s()\n", __FUNCTION__);
313
314 int r = mfs_instance_get(service_id, &inst);
315 if (r != EOK)
316 return r;
317
318 if (inst->open_nodes_cnt != 0)
319 return EBUSY;
320
321 (void) block_cache_fini(service_id);
322 block_fini(service_id);
323
324 /* Remove and destroy the instance */
325 (void) fs_instance_destroy(service_id);
326 free(inst->sbi);
327 free(inst);
328 return EOK;
329}
330
331service_id_t
332mfs_service_get(fs_node_t *fsnode)
333{
334 struct mfs_node *node = fsnode->data;
335 return node->instance->service_id;
336}
337
338static int
339mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
340{
341 int r;
342 struct mfs_instance *inst;
343 struct mfs_node *mnode;
344 fs_node_t *fsnode;
345 uint32_t inum;
346
347 mfsdebug("%s()\n", __FUNCTION__);
348
349 r = mfs_instance_get(service_id, &inst);
350 if (r != EOK)
351 return r;
352
353 /* Alloc a new inode */
354 r = mfs_alloc_inode(inst, &inum);
355 if (r != EOK)
356 return r;
357
358 struct mfs_ino_info *ino_i;
359
360 ino_i = malloc(sizeof(*ino_i));
361 if (!ino_i) {
362 r = ENOMEM;
363 goto out_err;
364 }
365
366 mnode = malloc(sizeof(*mnode));
367 if (!mnode) {
368 r = ENOMEM;
369 goto out_err_1;
370 }
371
372 fsnode = malloc(sizeof(fs_node_t));
373 if (!fsnode) {
374 r = ENOMEM;
375 goto out_err_2;
376 }
377
378 if (flags & L_DIRECTORY) {
379 ino_i->i_mode = S_IFDIR;
380 ino_i->i_nlinks = 1; /* This accounts for the '.' dentry */
381 } else
382 ino_i->i_mode = S_IFREG;
383
384 ino_i->i_uid = 0;
385 ino_i->i_gid = 0;
386 ino_i->i_size = 0;
387 ino_i->i_atime = 0;
388 ino_i->i_mtime = 0;
389 ino_i->i_ctime = 0;
390
391 memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
392 memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
393
394 mfsdebug("new node idx = %d\n", (int) inum);
395
396 ino_i->index = inum;
397 ino_i->dirty = true;
398 mnode->ino_i = ino_i;
399 mnode->instance = inst;
400 mnode->refcnt = 1;
401
402 fibril_mutex_lock(&open_nodes_lock);
403 hash_table_insert(&open_nodes, &mnode->link);
404 fibril_mutex_unlock(&open_nodes_lock);
405 inst->open_nodes_cnt++;
406
407 mnode->ino_i->dirty = true;
408
409 fs_node_initialize(fsnode);
410 fsnode->data = mnode;
411 mnode->fsnode = fsnode;
412 *rfn = fsnode;
413
414 return EOK;
415
416out_err_2:
417 free(mnode);
418out_err_1:
419 free(ino_i);
420out_err:
421 mfs_free_inode(inst, inum);
422 return r;
423}
424
425static int
426mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
427{
428 struct mfs_node *mnode = pfn->data;
429 struct mfs_ino_info *ino_i = mnode->ino_i;
430 struct mfs_dentry_info d_info;
431 int r;
432
433 mfsdebug("%s()\n", __FUNCTION__);
434
435 if (!S_ISDIR(ino_i->i_mode))
436 return ENOTDIR;
437
438 struct mfs_sb_info *sbi = mnode->instance->sbi;
439 const size_t comp_size = str_size(component);
440
441 unsigned i;
442 for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
443 r = mfs_read_dentry(mnode, &d_info, i);
444 if (r != EOK)
445 return r;
446
447 if (!d_info.d_inum) {
448 /* This entry is not used */
449 continue;
450 }
451
452 const size_t dentry_name_size = str_size(d_info.d_name);
453
454 if (comp_size == dentry_name_size &&
455 memcmp(component, d_info.d_name, dentry_name_size) == 0) {
456 /* Hit! */
457 mfs_node_core_get(rfn, mnode->instance,
458 d_info.d_inum);
459 goto found;
460 }
461 }
462 *rfn = NULL;
463found:
464 return EOK;
465}
466
467static aoff64_t
468mfs_size_get(fs_node_t *node)
469{
470 const struct mfs_node *mnode = node->data;
471 return mnode->ino_i->i_size;
472}
473
474static int
475mfs_node_get(fs_node_t **rfn, service_id_t service_id,
476 fs_index_t index)
477{
478 int rc;
479 struct mfs_instance *instance;
480
481 mfsdebug("%s()\n", __FUNCTION__);
482
483 rc = mfs_instance_get(service_id, &instance);
484 if (rc != EOK)
485 return rc;
486
487 return mfs_node_core_get(rfn, instance, index);
488}
489
490static int
491mfs_node_put(fs_node_t *fsnode)
492{
493 int rc = EOK;
494 struct mfs_node *mnode = fsnode->data;
495
496 mfsdebug("%s()\n", __FUNCTION__);
497
498 fibril_mutex_lock(&open_nodes_lock);
499
500 assert(mnode->refcnt > 0);
501 mnode->refcnt--;
502 if (mnode->refcnt == 0) {
503 hash_table_remove_item(&open_nodes, &mnode->link);
504 assert(mnode->instance->open_nodes_cnt > 0);
505 mnode->instance->open_nodes_cnt--;
506 rc = mfs_put_inode(mnode);
507 free(mnode->ino_i);
508 free(mnode);
509 free(fsnode);
510 }
511
512 fibril_mutex_unlock(&open_nodes_lock);
513 return rc;
514}
515
516static int
517mfs_node_open(fs_node_t *fsnode)
518{
519 /*
520 * Opening a file is stateless, nothing
521 * to be done here.
522 */
523 return EOK;
524}
525
526static fs_index_t
527mfs_index_get(fs_node_t *fsnode)
528{
529 struct mfs_node *mnode = fsnode->data;
530 return mnode->ino_i->index;
531}
532
533static unsigned
534mfs_lnkcnt_get(fs_node_t *fsnode)
535{
536 struct mfs_node *mnode = fsnode->data;
537
538 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
539
540 if (S_ISDIR(mnode->ino_i->i_mode)) {
541 if (mnode->ino_i->i_nlinks > 1)
542 return 1;
543 else
544 return 0;
545 } else
546 return mnode->ino_i->i_nlinks;
547}
548
549static int
550mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
551 fs_index_t index)
552{
553 fs_node_t *node = NULL;
554 struct mfs_node *mnode = NULL;
555 int rc;
556
557 mfsdebug("%s()\n", __FUNCTION__);
558
559 fibril_mutex_lock(&open_nodes_lock);
560
561 /* Check if the node is not already open */
562 node_key_t key = {
563 .service_id = inst->service_id,
564 .index = index
565 };
566
567 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
568
569 if (already_open) {
570 mnode = hash_table_get_inst(already_open, struct mfs_node, link);
571
572 *rfn = mnode->fsnode;
573 mnode->refcnt++;
574
575 fibril_mutex_unlock(&open_nodes_lock);
576 return EOK;
577 }
578
579 node = malloc(sizeof(fs_node_t));
580 if (!node) {
581 rc = ENOMEM;
582 goto out_err;
583 }
584
585 fs_node_initialize(node);
586
587 mnode = malloc(sizeof(*mnode));
588 if (!mnode) {
589 rc = ENOMEM;
590 goto out_err;
591 }
592
593 struct mfs_ino_info *ino_i;
594
595 rc = mfs_get_inode(inst, &ino_i, index);
596 if (rc != EOK)
597 goto out_err;
598
599 ino_i->index = index;
600 mnode->ino_i = ino_i;
601 mnode->refcnt = 1;
602
603 mnode->instance = inst;
604 node->data = mnode;
605 mnode->fsnode = node;
606 *rfn = node;
607
608 hash_table_insert(&open_nodes, &mnode->link);
609 inst->open_nodes_cnt++;
610
611 fibril_mutex_unlock(&open_nodes_lock);
612
613 return EOK;
614
615out_err:
616 if (node)
617 free(node);
618 if (mnode)
619 free(mnode);
620 fibril_mutex_unlock(&open_nodes_lock);
621 return rc;
622}
623
624static bool
625mfs_is_directory(fs_node_t *fsnode)
626{
627 const struct mfs_node *node = fsnode->data;
628 return S_ISDIR(node->ino_i->i_mode);
629}
630
631static bool
632mfs_is_file(fs_node_t *fsnode)
633{
634 struct mfs_node *node = fsnode->data;
635 return S_ISREG(node->ino_i->i_mode);
636}
637
638static int
639mfs_root_get(fs_node_t **rfn, service_id_t service_id)
640{
641 int rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
642 return rc;
643}
644
645static int
646mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
647{
648 struct mfs_node *parent = pfn->data;
649 struct mfs_node *child = cfn->data;
650 struct mfs_sb_info *sbi = parent->instance->sbi;
651 bool destroy_dentry = false;
652
653 mfsdebug("%s()\n", __FUNCTION__);
654
655 if (str_size(name) > sbi->max_name_len)
656 return ENAMETOOLONG;
657
658 int r = mfs_insert_dentry(parent, name, child->ino_i->index);
659 if (r != EOK)
660 return r;
661
662 if (S_ISDIR(child->ino_i->i_mode)) {
663 if (child->ino_i->i_nlinks != 1) {
664 /* It's not possible to hardlink directories in MFS */
665 destroy_dentry = true;
666 r = EMLINK;
667 goto exit;
668 }
669 r = mfs_insert_dentry(child, ".", child->ino_i->index);
670 if (r != EOK) {
671 destroy_dentry = true;
672 goto exit;
673 }
674
675 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
676 if (r != EOK) {
677 mfs_remove_dentry(child, ".");
678 destroy_dentry = true;
679 goto exit;
680 }
681
682 parent->ino_i->i_nlinks++;
683 parent->ino_i->dirty = true;
684 }
685
686exit:
687 if (destroy_dentry) {
688 int r2 = mfs_remove_dentry(parent, name);
689 if (r2 != EOK)
690 r = r2;
691 } else {
692 child->ino_i->i_nlinks++;
693 child->ino_i->dirty = true;
694 }
695 return r;
696}
697
698static int
699mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
700{
701 struct mfs_node *parent = pfn->data;
702 struct mfs_node *child = cfn->data;
703 bool has_children;
704 int r;
705
706 mfsdebug("%s()\n", __FUNCTION__);
707
708 if (!parent)
709 return EBUSY;
710
711 r = mfs_has_children(&has_children, cfn);
712 if (r != EOK)
713 return r;
714
715 if (has_children)
716 return ENOTEMPTY;
717
718 r = mfs_remove_dentry(parent, name);
719 if (r != EOK)
720 return r;
721
722 struct mfs_ino_info *chino = child->ino_i;
723
724 assert(chino->i_nlinks >= 1);
725 chino->i_nlinks--;
726 mfsdebug("Links: %d\n", chino->i_nlinks);
727
728 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
729 /* The child directory will be destroyed, decrease the
730 * parent hard links counter.
731 */
732 parent->ino_i->i_nlinks--;
733 parent->ino_i->dirty = true;
734 }
735
736 chino->dirty = true;
737
738 return r;
739}
740
741static int
742mfs_has_children(bool *has_children, fs_node_t *fsnode)
743{
744 struct mfs_node *mnode = fsnode->data;
745 struct mfs_sb_info *sbi = mnode->instance->sbi;
746 int r;
747
748 *has_children = false;
749
750 if (!S_ISDIR(mnode->ino_i->i_mode))
751 goto out;
752
753 struct mfs_dentry_info d_info;
754
755 /* The first two dentries are always . and .. */
756 unsigned i;
757 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
758 r = mfs_read_dentry(mnode, &d_info, i);
759 if (r != EOK)
760 return r;
761
762 if (d_info.d_inum) {
763 /* A valid entry has been found */
764 *has_children = true;
765 break;
766 }
767 }
768out:
769
770 return EOK;
771}
772
773static int
774mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
775 size_t *rbytes)
776{
777 int rc;
778 fs_node_t *fn = NULL;
779
780 rc = mfs_node_get(&fn, service_id, index);
781 if (rc != EOK)
782 return rc;
783 if (!fn)
784 return ENOENT;
785
786 struct mfs_node *mnode;
787 struct mfs_ino_info *ino_i;
788 size_t len, bytes = 0;
789 ipc_callid_t callid;
790
791 mnode = fn->data;
792 ino_i = mnode->ino_i;
793
794 if (!async_data_read_receive(&callid, &len)) {
795 rc = EINVAL;
796 goto out_error;
797 }
798
799 if (S_ISDIR(ino_i->i_mode)) {
800 aoff64_t spos = pos;
801 struct mfs_dentry_info d_info;
802 struct mfs_sb_info *sbi = mnode->instance->sbi;
803
804 if (pos < 2) {
805 /* Skip the first two dentries ('.' and '..') */
806 pos = 2;
807 }
808
809 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
810 rc = mfs_read_dentry(mnode, &d_info, pos);
811 if (rc != EOK)
812 goto out_error;
813
814 if (d_info.d_inum) {
815 /* Dentry found! */
816 goto found;
817 }
818 }
819
820 rc = mfs_node_put(fn);
821 async_answer_0(callid, rc != EOK ? rc : ENOENT);
822 return rc;
823found:
824 async_data_read_finalize(callid, d_info.d_name,
825 str_size(d_info.d_name) + 1);
826 bytes = ((pos - spos) + 1);
827 } else {
828 struct mfs_sb_info *sbi = mnode->instance->sbi;
829
830 if (pos >= (size_t) ino_i->i_size) {
831 /* Trying to read beyond the end of file */
832 bytes = 0;
833 (void) async_data_read_finalize(callid, NULL, 0);
834 goto out_success;
835 }
836
837 bytes = min(len, sbi->block_size - pos % sbi->block_size);
838 bytes = min(bytes, ino_i->i_size - pos);
839
840 uint32_t zone;
841 block_t *b;
842
843 rc = mfs_read_map(&zone, mnode, pos);
844 if (rc != EOK)
845 goto out_error;
846
847 if (zone == 0) {
848 /* sparse file */
849 uint8_t *buf = malloc(sbi->block_size);
850 if (!buf) {
851 rc = ENOMEM;
852 goto out_error;
853 }
854 memset(buf, 0, sizeof(sbi->block_size));
855 async_data_read_finalize(callid,
856 buf + pos % sbi->block_size, bytes);
857 free(buf);
858 goto out_success;
859 }
860
861 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
862 if (rc != EOK)
863 goto out_error;
864
865 async_data_read_finalize(callid, b->data +
866 pos % sbi->block_size, bytes);
867
868 rc = block_put(b);
869 if (rc != EOK) {
870 mfs_node_put(fn);
871 return rc;
872 }
873 }
874out_success:
875 rc = mfs_node_put(fn);
876 *rbytes = bytes;
877 return rc;
878out_error:
879 ;
880 int tmp = mfs_node_put(fn);
881 async_answer_0(callid, tmp != EOK ? tmp : rc);
882 return tmp != EOK ? tmp : rc;
883}
884
885static int
886mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
887 size_t *wbytes, aoff64_t *nsize)
888{
889 fs_node_t *fn;
890 int r;
891 int flags = BLOCK_FLAGS_NONE;
892
893 r = mfs_node_get(&fn, service_id, index);
894 if (r != EOK)
895 return r;
896 if (!fn)
897 return ENOENT;
898
899 ipc_callid_t callid;
900 size_t len;
901
902 if (!async_data_write_receive(&callid, &len)) {
903 r = EINVAL;
904 goto out_err;
905 }
906
907 struct mfs_node *mnode = fn->data;
908 struct mfs_sb_info *sbi = mnode->instance->sbi;
909 struct mfs_ino_info *ino_i = mnode->ino_i;
910 const size_t bs = sbi->block_size;
911 size_t bytes = min(len, bs - (pos % bs));
912 uint32_t block;
913
914 if (bytes == bs)
915 flags = BLOCK_FLAGS_NOREAD;
916
917 r = mfs_read_map(&block, mnode, pos);
918 if (r != EOK)
919 goto out_err;
920
921 if (block == 0) {
922 uint32_t dummy;
923
924 r = mfs_alloc_zone(mnode->instance, &block);
925 if (r != EOK)
926 goto out_err;
927
928 r = mfs_write_map(mnode, pos, block, &dummy);
929 if (r != EOK) {
930 mfs_free_zone(mnode->instance, block);
931 goto out_err;
932 }
933
934 flags = BLOCK_FLAGS_NOREAD;
935 }
936
937 block_t *b;
938 r = block_get(&b, service_id, block, flags);
939 if (r != EOK)
940 goto out_err;
941
942 if (flags == BLOCK_FLAGS_NOREAD)
943 memset(b->data, 0, sbi->block_size);
944
945 async_data_write_finalize(callid, b->data + (pos % bs), bytes);
946 b->dirty = true;
947
948 r = block_put(b);
949 if (r != EOK) {
950 mfs_node_put(fn);
951 return r;
952 }
953
954 if (pos + bytes > ino_i->i_size) {
955 ino_i->i_size = pos + bytes;
956 ino_i->dirty = true;
957 }
958 r = mfs_node_put(fn);
959 *nsize = ino_i->i_size;
960 *wbytes = bytes;
961 return r;
962
963out_err:
964 mfs_node_put(fn);
965 async_answer_0(callid, r);
966 return r;
967}
968
969static int
970mfs_destroy(service_id_t service_id, fs_index_t index)
971{
972 fs_node_t *fn;
973 int r;
974
975 r = mfs_node_get(&fn, service_id, index);
976 if (r != EOK)
977 return r;
978 if (!fn)
979 return ENOENT;
980
981 /* Destroy the inode */
982 return mfs_destroy_node(fn);
983}
984
985static int
986mfs_destroy_node(fs_node_t *fn)
987{
988 struct mfs_node *mnode = fn->data;
989 bool has_children;
990 int r;
991
992 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
993
994 r = mfs_has_children(&has_children, fn);
995 if (r != EOK)
996 goto out;
997
998 assert(!has_children);
999
1000 /* Free the entire inode content */
1001 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
1002 if (r != EOK)
1003 goto out;
1004
1005 /* Mark the inode as free in the bitmap */
1006 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
1007
1008out:
1009 mfs_node_put(fn);
1010 return r;
1011}
1012
1013static int
1014mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1015{
1016 fs_node_t *fn;
1017 int r;
1018
1019 r = mfs_node_get(&fn, service_id, index);
1020 if (r != EOK)
1021 return r;
1022 if (!fn)
1023 return r;
1024
1025 struct mfs_node *mnode = fn->data;
1026 struct mfs_ino_info *ino_i = mnode->ino_i;
1027
1028 if (ino_i->i_size == size)
1029 r = EOK;
1030 else
1031 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1032
1033 mfs_node_put(fn);
1034 return r;
1035}
1036
1037static int
1038mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1039{
1040 void *data;
1041 int rc;
1042
1043 rc = fs_instance_get(service_id, &data);
1044 if (rc == EOK)
1045 *instance = (struct mfs_instance *) data;
1046 else {
1047 mfsdebug("instance not found\n");
1048 }
1049
1050 return rc;
1051}
1052
1053static bool
1054check_magic_number(uint16_t magic, bool *native,
1055 mfs_version_t *version, bool *longfilenames)
1056{
1057 bool rc = true;
1058 *longfilenames = false;
1059
1060 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1061 *native = magic == MFS_MAGIC_V1;
1062 *version = MFS_VERSION_V1;
1063 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1064 *native = magic == MFS_MAGIC_V1L;
1065 *version = MFS_VERSION_V1;
1066 *longfilenames = true;
1067 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1068 *native = magic == MFS_MAGIC_V2;
1069 *version = MFS_VERSION_V2;
1070 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1071 *native = magic == MFS_MAGIC_V2L;
1072 *version = MFS_VERSION_V2;
1073 *longfilenames = true;
1074 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1075 *native = magic == MFS_MAGIC_V3;
1076 *version = MFS_VERSION_V3;
1077 } else
1078 rc = false;
1079
1080 return rc;
1081}
1082
1083/** Filesystem sanity check
1084 *
1085 * @param Pointer to the MFS superblock.
1086 *
1087 * @return EOK on success, ENOTSUP otherwise.
1088 */
1089static int
1090mfs_check_sanity(struct mfs_sb_info *sbi)
1091{
1092 if (!is_power_of_two(sbi->block_size) ||
1093 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1094 sbi->block_size > MFS_MAX_BLOCKSIZE)
1095 return ENOTSUP;
1096 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1097 return ENOTSUP;
1098 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1099 return ENOTSUP;
1100 else if (sbi->firstdatazone == 0)
1101 return ENOTSUP;
1102
1103 return EOK;
1104}
1105
1106static int
1107mfs_close(service_id_t service_id, fs_index_t index)
1108{
1109 return 0;
1110}
1111
1112static int
1113mfs_sync(service_id_t service_id, fs_index_t index)
1114{
1115 fs_node_t *fn = NULL;
1116 int rc = mfs_node_get(&fn, service_id, index);
1117 if (rc != EOK)
1118 return rc;
1119 if (!fn)
1120 return ENOENT;
1121
1122 struct mfs_node *mnode = fn->data;
1123 mnode->ino_i->dirty = true;
1124
1125 return mfs_node_put(fn);
1126}
1127
1128/** Check if a given number is a power of two.
1129 *
1130 * @param n The number to check.
1131 *
1132 * @return true if it is a power of two, false otherwise.
1133 */
1134static bool
1135is_power_of_two(uint32_t n)
1136{
1137 if (n == 0)
1138 return false;
1139
1140 return (n & (n - 1)) == 0;
1141}
1142
1143vfs_out_ops_t mfs_ops = {
1144 .mounted = mfs_mounted,
1145 .unmounted = mfs_unmounted,
1146 .read = mfs_read,
1147 .write = mfs_write,
1148 .truncate = mfs_truncate,
1149 .close = mfs_close,
1150 .destroy = mfs_destroy,
1151 .sync = mfs_sync,
1152};
1153
1154/**
1155 * @}
1156 */
1157
Note: See TracBrowser for help on using the repository browser.