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

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

Use NULL instead of 0 as a hash_table_ops_t member initializer.

  • Property mode set to 100644
File size: 24.9 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 */
90
91typedef struct {
92 service_id_t service_id;
93 fs_index_t index;
94} node_key_t;
95
96static size_t
97open_nodes_key_hash(void *key)
98{
99 node_key_t *node_key = (node_key_t*)key;
100 return hash_combine(node_key->service_id, node_key->index);
101}
102
103static size_t
104open_nodes_hash(const ht_link_t *item)
105{
106 struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
107 return hash_combine(m->instance->service_id, m->ino_i->index);
108}
109
110static bool
111open_nodes_key_equal(void *key, const ht_link_t *item)
112{
113 node_key_t *node_key = (node_key_t*)key;
114 struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
115
116 return node_key->service_id == mnode->instance->service_id
117 && node_key->index == mnode->ino_i->index;
118}
119
120static hash_table_ops_t open_nodes_ops = {
121 .hash = open_nodes_hash,
122 .key_hash = open_nodes_key_hash,
123 .key_equal = open_nodes_key_equal,
124 .equal = NULL,
125 .remove_callback = NULL,
126};
127
128int
129mfs_global_init(void)
130{
131 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
132 return ENOMEM;
133 }
134 return EOK;
135}
136
137static int
138mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
139 aoff64_t *size, unsigned *linkcnt)
140{
141 enum cache_mode cmode;
142 struct mfs_superblock *sb = NULL;
143 struct mfs3_superblock *sb3 = NULL;
144 struct mfs_sb_info *sbi = NULL;
145 struct mfs_instance *instance = NULL;
146 bool native, longnames;
147 mfs_version_t version;
148 uint16_t magic;
149 int rc;
150
151 /* Check for option enabling write through. */
152 if (str_cmp(opts, "wtcache") == 0)
153 cmode = CACHE_MODE_WT;
154 else
155 cmode = CACHE_MODE_WB;
156
157 /* initialize libblock */
158 rc = block_init(EXCHANGE_SERIALIZE, service_id, 4096);
159 if (rc != EOK)
160 return rc;
161
162 /* Allocate space for generic MFS superblock */
163 sbi = malloc(sizeof(*sbi));
164 if (!sbi) {
165 rc = ENOMEM;
166 goto out_error;
167 }
168
169 /* Allocate space for filesystem instance */
170 instance = malloc(sizeof(*instance));
171 if (!instance) {
172 rc = ENOMEM;
173 goto out_error;
174 }
175
176 sb = malloc(MFS_SUPERBLOCK_SIZE);
177 if (!sb) {
178 rc = ENOMEM;
179 goto out_error;
180 }
181
182 /* Read the superblock */
183 rc = block_read_direct(service_id, MFS_SUPERBLOCK << 1, 2, sb);
184 if (rc != EOK)
185 goto out_error;
186
187 sb3 = (struct mfs3_superblock *) sb;
188
189 if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
190 /* This is a V1 or V2 Minix filesystem */
191 magic = sb->s_magic;
192 } else if (check_magic_number(sb3->s_magic, &native, &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 return r;
422}
423
424static int
425mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
426{
427 struct mfs_node *mnode = pfn->data;
428 struct mfs_ino_info *ino_i = mnode->ino_i;
429 struct mfs_dentry_info d_info;
430 int r;
431
432 mfsdebug("%s()\n", __FUNCTION__);
433
434 if (!S_ISDIR(ino_i->i_mode))
435 return ENOTDIR;
436
437 struct mfs_sb_info *sbi = mnode->instance->sbi;
438 const size_t comp_size = str_size(component);
439
440 unsigned i;
441 for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
442 r = mfs_read_dentry(mnode, &d_info, i);
443 if (r != EOK)
444 return r;
445
446 if (!d_info.d_inum) {
447 /* This entry is not used */
448 continue;
449 }
450
451 const size_t dentry_name_size = str_size(d_info.d_name);
452
453 if (comp_size == dentry_name_size &&
454 !bcmp(component, d_info.d_name, dentry_name_size)) {
455 /* Hit! */
456 mfs_node_core_get(rfn, mnode->instance,
457 d_info.d_inum);
458 goto found;
459 }
460 }
461 *rfn = NULL;
462found:
463 return EOK;
464}
465
466static aoff64_t
467mfs_size_get(fs_node_t *node)
468{
469 const struct mfs_node *mnode = node->data;
470 return mnode->ino_i->i_size;
471}
472
473static int
474mfs_node_get(fs_node_t **rfn, service_id_t service_id,
475 fs_index_t index)
476{
477 int rc;
478 struct mfs_instance *instance;
479
480 mfsdebug("%s()\n", __FUNCTION__);
481
482 rc = mfs_instance_get(service_id, &instance);
483 if (rc != EOK)
484 return rc;
485
486 return mfs_node_core_get(rfn, instance, index);
487}
488
489static int
490mfs_node_put(fs_node_t *fsnode)
491{
492 int rc = EOK;
493 struct mfs_node *mnode = fsnode->data;
494
495 mfsdebug("%s()\n", __FUNCTION__);
496
497 fibril_mutex_lock(&open_nodes_lock);
498
499 assert(mnode->refcnt > 0);
500 mnode->refcnt--;
501 if (mnode->refcnt == 0) {
502 hash_table_remove_item(&open_nodes, &mnode->link);
503 assert(mnode->instance->open_nodes_cnt > 0);
504 mnode->instance->open_nodes_cnt--;
505 rc = mfs_put_inode(mnode);
506 free(mnode->ino_i);
507 free(mnode);
508 free(fsnode);
509 }
510
511 fibril_mutex_unlock(&open_nodes_lock);
512 return rc;
513}
514
515static int
516mfs_node_open(fs_node_t *fsnode)
517{
518 /*
519 * Opening a file is stateless, nothing
520 * to be done here.
521 */
522 return EOK;
523}
524
525static fs_index_t
526mfs_index_get(fs_node_t *fsnode)
527{
528 struct mfs_node *mnode = fsnode->data;
529 return mnode->ino_i->index;
530}
531
532static unsigned
533mfs_lnkcnt_get(fs_node_t *fsnode)
534{
535 struct mfs_node *mnode = fsnode->data;
536
537 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
538
539 if (S_ISDIR(mnode->ino_i->i_mode)) {
540 if (mnode->ino_i->i_nlinks > 1)
541 return 1;
542 else
543 return 0;
544 } else
545 return mnode->ino_i->i_nlinks;
546}
547
548static int
549mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
550 fs_index_t index)
551{
552 fs_node_t *node = NULL;
553 struct mfs_node *mnode = NULL;
554 int rc;
555
556 mfsdebug("%s()\n", __FUNCTION__);
557
558 fibril_mutex_lock(&open_nodes_lock);
559
560 /* Check if the node is not already open */
561 node_key_t key = {
562 .service_id = inst->service_id,
563 .index = index
564 };
565
566 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
567
568 if (already_open) {
569 mnode = hash_table_get_inst(already_open, struct mfs_node, link);
570 *rfn = mnode->fsnode;
571 mnode->refcnt++;
572
573 fibril_mutex_unlock(&open_nodes_lock);
574 return EOK;
575 }
576
577 node = malloc(sizeof(fs_node_t));
578 if (!node) {
579 rc = ENOMEM;
580 goto out_err;
581 }
582
583 fs_node_initialize(node);
584
585 mnode = malloc(sizeof(*mnode));
586 if (!mnode) {
587 rc = ENOMEM;
588 goto out_err;
589 }
590
591 struct mfs_ino_info *ino_i;
592
593 rc = mfs_get_inode(inst, &ino_i, index);
594 if (rc != EOK)
595 goto out_err;
596
597 ino_i->index = index;
598 mnode->ino_i = ino_i;
599 mnode->refcnt = 1;
600
601 mnode->instance = inst;
602 node->data = mnode;
603 mnode->fsnode = node;
604 *rfn = node;
605
606 hash_table_insert(&open_nodes, &mnode->link);
607 inst->open_nodes_cnt++;
608
609 fibril_mutex_unlock(&open_nodes_lock);
610
611 return EOK;
612
613out_err:
614 if (node)
615 free(node);
616 if (mnode)
617 free(mnode);
618 fibril_mutex_unlock(&open_nodes_lock);
619 return rc;
620}
621
622static bool
623mfs_is_directory(fs_node_t *fsnode)
624{
625 const struct mfs_node *node = fsnode->data;
626 return S_ISDIR(node->ino_i->i_mode);
627}
628
629static bool
630mfs_is_file(fs_node_t *fsnode)
631{
632 struct mfs_node *node = fsnode->data;
633 return S_ISREG(node->ino_i->i_mode);
634}
635
636static int
637mfs_root_get(fs_node_t **rfn, service_id_t service_id)
638{
639 int rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
640 return rc;
641}
642
643static int
644mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
645{
646 struct mfs_node *parent = pfn->data;
647 struct mfs_node *child = cfn->data;
648 struct mfs_sb_info *sbi = parent->instance->sbi;
649 bool destroy_dentry = false;
650
651 mfsdebug("%s()\n", __FUNCTION__);
652
653 if (str_size(name) > sbi->max_name_len)
654 return ENAMETOOLONG;
655
656 int r = mfs_insert_dentry(parent, name, child->ino_i->index);
657 if (r != EOK)
658 return r;
659
660 if (S_ISDIR(child->ino_i->i_mode)) {
661 if (child->ino_i->i_nlinks != 1) {
662 /* It's not possible to hardlink directories in MFS */
663 destroy_dentry = true;
664 r = EMLINK;
665 goto exit;
666 }
667 r = mfs_insert_dentry(child, ".", child->ino_i->index);
668 if (r != EOK) {
669 destroy_dentry = true;
670 goto exit;
671 }
672
673 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
674 if (r != EOK) {
675 destroy_dentry = true;
676 goto exit;
677 }
678
679 parent->ino_i->i_nlinks++;
680 parent->ino_i->dirty = true;
681 }
682
683exit:
684 if (destroy_dentry) {
685 int r2 = mfs_remove_dentry(parent, name);
686 if (r2 != EOK)
687 r = r2;
688 } else {
689 child->ino_i->i_nlinks++;
690 child->ino_i->dirty = true;
691 }
692 return r;
693}
694
695static int
696mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
697{
698 struct mfs_node *parent = pfn->data;
699 struct mfs_node *child = cfn->data;
700 bool has_children;
701 int r;
702
703 mfsdebug("%s()\n", __FUNCTION__);
704
705 if (!parent)
706 return EBUSY;
707
708 r = mfs_has_children(&has_children, cfn);
709 if (r != EOK)
710 return r;
711
712 if (has_children)
713 return ENOTEMPTY;
714
715 r = mfs_remove_dentry(parent, name);
716 if (r != EOK)
717 return r;
718
719 struct mfs_ino_info *chino = child->ino_i;
720
721 assert(chino->i_nlinks >= 1);
722 chino->i_nlinks--;
723 mfsdebug("Links: %d\n", chino->i_nlinks);
724
725 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
726 /* The child directory will be destroyed, decrease the
727 * parent hard links counter.
728 */
729 parent->ino_i->i_nlinks--;
730 parent->ino_i->dirty = true;
731 }
732
733 chino->dirty = true;
734
735 return r;
736}
737
738static int
739mfs_has_children(bool *has_children, fs_node_t *fsnode)
740{
741 struct mfs_node *mnode = fsnode->data;
742 struct mfs_sb_info *sbi = mnode->instance->sbi;
743 int r;
744
745 *has_children = false;
746
747 if (!S_ISDIR(mnode->ino_i->i_mode))
748 goto out;
749
750 struct mfs_dentry_info d_info;
751
752 /* The first two dentries are always . and .. */
753 unsigned i;
754 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
755 r = mfs_read_dentry(mnode, &d_info, i);
756 if (r != EOK)
757 return r;
758
759 if (d_info.d_inum) {
760 /* A valid entry has been found */
761 *has_children = true;
762 break;
763 }
764 }
765out:
766
767 return EOK;
768}
769
770static int
771mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
772 size_t *rbytes)
773{
774 int rc;
775 fs_node_t *fn;
776
777 rc = mfs_node_get(&fn, service_id, index);
778 if (rc != EOK)
779 return rc;
780 if (!fn)
781 return ENOENT;
782
783 struct mfs_node *mnode;
784 struct mfs_ino_info *ino_i;
785 size_t len, bytes = 0;
786 ipc_callid_t callid;
787
788 mnode = fn->data;
789 ino_i = mnode->ino_i;
790
791 if (!async_data_read_receive(&callid, &len)) {
792 rc = EINVAL;
793 goto out_error;
794 }
795
796 if (S_ISDIR(ino_i->i_mode)) {
797 aoff64_t spos = pos;
798 struct mfs_dentry_info d_info;
799 struct mfs_sb_info *sbi = mnode->instance->sbi;
800
801 if (pos < 2) {
802 /* Skip the first two dentries ('.' and '..') */
803 pos = 2;
804 }
805
806 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
807 rc = mfs_read_dentry(mnode, &d_info, pos);
808 if (rc != EOK)
809 goto out_error;
810
811 if (d_info.d_inum) {
812 /* Dentry found! */
813 goto found;
814 }
815 }
816
817 rc = mfs_node_put(fn);
818 async_answer_0(callid, rc != EOK ? rc : ENOENT);
819 return rc;
820found:
821 async_data_read_finalize(callid, d_info.d_name,
822 str_size(d_info.d_name) + 1);
823 bytes = ((pos - spos) + 1);
824 } else {
825 struct mfs_sb_info *sbi = mnode->instance->sbi;
826
827 if (pos >= (size_t) ino_i->i_size) {
828 /* Trying to read beyond the end of file */
829 bytes = 0;
830 (void) async_data_read_finalize(callid, NULL, 0);
831 goto out_success;
832 }
833
834 bytes = min(len, sbi->block_size - pos % sbi->block_size);
835 bytes = min(bytes, ino_i->i_size - pos);
836
837 uint32_t zone;
838 block_t *b;
839
840 rc = mfs_read_map(&zone, mnode, pos);
841 if (rc != EOK)
842 goto out_error;
843
844 if (zone == 0) {
845 /* sparse file */
846 uint8_t *buf = malloc(sbi->block_size);
847 if (!buf) {
848 rc = ENOMEM;
849 goto out_error;
850 }
851 memset(buf, 0, sizeof(sbi->block_size));
852 async_data_read_finalize(callid,
853 buf + pos % sbi->block_size, bytes);
854 free(buf);
855 goto out_success;
856 }
857
858 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
859 if (rc != EOK)
860 goto out_error;
861
862 async_data_read_finalize(callid, b->data +
863 pos % sbi->block_size, bytes);
864
865 rc = block_put(b);
866 if (rc != EOK) {
867 mfs_node_put(fn);
868 return rc;
869 }
870 }
871out_success:
872 rc = mfs_node_put(fn);
873 *rbytes = bytes;
874 return rc;
875out_error:
876 ;
877 int tmp = mfs_node_put(fn);
878 async_answer_0(callid, tmp != EOK ? tmp : rc);
879 return tmp != EOK ? tmp : rc;
880}
881
882static int
883mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
884 size_t *wbytes, aoff64_t *nsize)
885{
886 fs_node_t *fn;
887 int r;
888 int flags = BLOCK_FLAGS_NONE;
889
890 r = mfs_node_get(&fn, service_id, index);
891 if (r != EOK)
892 return r;
893 if (!fn)
894 return ENOENT;
895
896 ipc_callid_t callid;
897 size_t len;
898
899 if (!async_data_write_receive(&callid, &len)) {
900 r = EINVAL;
901 goto out_err;
902 }
903
904 struct mfs_node *mnode = fn->data;
905 struct mfs_sb_info *sbi = mnode->instance->sbi;
906 struct mfs_ino_info *ino_i = mnode->ino_i;
907 const size_t bs = sbi->block_size;
908 size_t bytes = min(len, bs - (pos % bs));
909 uint32_t block;
910
911 if (bytes == bs)
912 flags = BLOCK_FLAGS_NOREAD;
913
914 r = mfs_read_map(&block, mnode, pos);
915 if (r != EOK)
916 goto out_err;
917
918 if (block == 0) {
919 uint32_t dummy;
920
921 r = mfs_alloc_zone(mnode->instance, &block);
922 if (r != EOK)
923 goto out_err;
924
925 r = mfs_write_map(mnode, pos, block, &dummy);
926 if (r != EOK)
927 goto out_err;
928
929 flags = BLOCK_FLAGS_NOREAD;
930 }
931
932 block_t *b;
933 r = block_get(&b, service_id, block, flags);
934 if (r != EOK)
935 goto out_err;
936
937 if (flags == BLOCK_FLAGS_NOREAD)
938 memset(b->data, 0, sbi->block_size);
939
940 async_data_write_finalize(callid, b->data + (pos % bs), bytes);
941 b->dirty = true;
942
943 r = block_put(b);
944 if (r != EOK) {
945 mfs_node_put(fn);
946 return r;
947 }
948
949 if (pos + bytes > ino_i->i_size) {
950 ino_i->i_size = pos + bytes;
951 ino_i->dirty = true;
952 }
953 r = mfs_node_put(fn);
954 *nsize = ino_i->i_size;
955 *wbytes = bytes;
956 return r;
957
958out_err:
959 mfs_node_put(fn);
960 async_answer_0(callid, r);
961 return r;
962}
963
964static int
965mfs_destroy(service_id_t service_id, fs_index_t index)
966{
967 fs_node_t *fn;
968 int r;
969
970 r = mfs_node_get(&fn, service_id, index);
971 if (r != EOK)
972 return r;
973 if (!fn)
974 return ENOENT;
975
976 /* Destroy the inode */
977 return mfs_destroy_node(fn);
978}
979
980static int
981mfs_destroy_node(fs_node_t *fn)
982{
983 struct mfs_node *mnode = fn->data;
984 bool has_children;
985 int r;
986
987 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
988
989 r = mfs_has_children(&has_children, fn);
990 if (r != EOK)
991 goto out;
992
993 assert(!has_children);
994
995 /* Free the entire inode content */
996 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
997 if (r != EOK)
998 goto out;
999
1000 /* Mark the inode as free in the bitmap */
1001 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
1002
1003out:
1004 mfs_node_put(fn);
1005 return r;
1006}
1007
1008static int
1009mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1010{
1011 fs_node_t *fn;
1012 int r;
1013
1014 r = mfs_node_get(&fn, service_id, index);
1015 if (r != EOK)
1016 return r;
1017 if (!fn)
1018 return r;
1019
1020 struct mfs_node *mnode = fn->data;
1021 struct mfs_ino_info *ino_i = mnode->ino_i;
1022
1023 if (ino_i->i_size == size)
1024 r = EOK;
1025 else
1026 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1027
1028 mfs_node_put(fn);
1029 return r;
1030}
1031
1032static int
1033mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1034{
1035 void *data;
1036 int rc;
1037
1038 rc = fs_instance_get(service_id, &data);
1039 if (rc == EOK)
1040 *instance = (struct mfs_instance *) data;
1041 else {
1042 mfsdebug("instance not found\n");
1043 }
1044
1045 return rc;
1046}
1047
1048static bool
1049check_magic_number(uint16_t magic, bool *native,
1050 mfs_version_t *version, bool *longfilenames)
1051{
1052 bool rc = true;
1053 *longfilenames = false;
1054
1055 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1056 *native = magic == MFS_MAGIC_V1;
1057 *version = MFS_VERSION_V1;
1058 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1059 *native = magic == MFS_MAGIC_V1L;
1060 *version = MFS_VERSION_V1;
1061 *longfilenames = true;
1062 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1063 *native = magic == MFS_MAGIC_V2;
1064 *version = MFS_VERSION_V2;
1065 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1066 *native = magic == MFS_MAGIC_V2L;
1067 *version = MFS_VERSION_V2;
1068 *longfilenames = true;
1069 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1070 *native = magic == MFS_MAGIC_V3;
1071 *version = MFS_VERSION_V3;
1072 } else
1073 rc = false;
1074
1075 return rc;
1076}
1077
1078/** Filesystem sanity check
1079 *
1080 * @param Pointer to the MFS superblock.
1081 *
1082 * @return EOK on success, ENOTSUP otherwise.
1083 */
1084static int
1085mfs_check_sanity(struct mfs_sb_info *sbi)
1086{
1087 if (!is_power_of_two(sbi->block_size) ||
1088 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1089 sbi->block_size > MFS_MAX_BLOCKSIZE)
1090 return ENOTSUP;
1091 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1092 return ENOTSUP;
1093 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1094 return ENOTSUP;
1095 else if (sbi->firstdatazone == 0)
1096 return ENOTSUP;
1097
1098 return EOK;
1099}
1100
1101static int
1102mfs_close(service_id_t service_id, fs_index_t index)
1103{
1104 return 0;
1105}
1106
1107static int
1108mfs_sync(service_id_t service_id, fs_index_t index)
1109{
1110 fs_node_t *fn;
1111 int rc = mfs_node_get(&fn, service_id, index);
1112 if (rc != EOK)
1113 return rc;
1114 if (!fn)
1115 return ENOENT;
1116
1117 struct mfs_node *mnode = fn->data;
1118 mnode->ino_i->dirty = true;
1119
1120 return mfs_node_put(fn);
1121}
1122
1123/** Check if a given number is a power of two.
1124 *
1125 * @param n The number to check.
1126 *
1127 * @return true if it is a power of two, false otherwise.
1128 */
1129static bool
1130is_power_of_two(uint32_t n)
1131{
1132 if (n == 0)
1133 return false;
1134
1135 return (n & (n - 1)) == 0;
1136}
1137
1138vfs_out_ops_t mfs_ops = {
1139 .mounted = mfs_mounted,
1140 .unmounted = mfs_unmounted,
1141 .read = mfs_read,
1142 .write = mfs_write,
1143 .truncate = mfs_truncate,
1144 .close = mfs_close,
1145 .destroy = mfs_destroy,
1146 .sync = mfs_sync,
1147};
1148
1149/**
1150 * @}
1151 */
1152
Note: See TracBrowser for help on using the repository browser.