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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 224174f was 224174f, checked in by Manuele Conti <conti.ma@…>, 12 years ago

merge mainline changes

  • Property mode set to 100644
File size: 25.2 KB
Line 
1/*
2 * Copyright (c) 2011 Maurizio Lombardi
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33#include <stdlib.h>
34#include <fibril_synch.h>
35#include <align.h>
36#include <adt/hash_table.h>
37#include <adt/hash.h>
38#include "mfs.h"
39
40
41static bool check_magic_number(uint16_t magic, bool *native,
42 mfs_version_t *version, bool *longfilenames);
43static int mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
44 fs_index_t index);
45static int mfs_node_put(fs_node_t *fsnode);
46static int mfs_node_open(fs_node_t *fsnode);
47static fs_index_t mfs_index_get(fs_node_t *fsnode);
48static unsigned mfs_lnkcnt_get(fs_node_t *fsnode);
49static bool mfs_is_directory(fs_node_t *fsnode);
50static bool mfs_is_file(fs_node_t *fsnode);
51static int mfs_has_children(bool *has_children, fs_node_t *fsnode);
52static int mfs_root_get(fs_node_t **rfn, service_id_t service_id);
53static service_id_t mfs_service_get(fs_node_t *fsnode);
54static aoff64_t mfs_size_get(fs_node_t *node);
55static int mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
56static int mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags);
57static int mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
58static int mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
59static int mfs_destroy_node(fs_node_t *fn);
60static int mfs_node_get(fs_node_t **rfn, service_id_t service_id,
61 fs_index_t index);
62static int mfs_instance_get(service_id_t service_id,
63 struct mfs_instance **instance);
64static int mfs_check_sanity(struct mfs_sb_info *sbi);
65static bool is_power_of_two(uint32_t n);
66static long mfs_size_block(service_id_t service_id);
67
68static hash_table_t open_nodes;
69static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
70
71libfs_ops_t mfs_libfs_ops = {
72 .size_get = mfs_size_get,
73 .root_get = mfs_root_get,
74 .service_get = mfs_service_get,
75 .is_directory = mfs_is_directory,
76 .is_file = mfs_is_file,
77 .node_get = mfs_node_get,
78 .node_put = mfs_node_put,
79 .node_open = mfs_node_open,
80 .index_get = mfs_index_get,
81 .match = mfs_match,
82 .create = mfs_create_node,
83 .link = mfs_link,
84 .unlink = mfs_unlink,
85 .destroy = mfs_destroy_node,
86 .has_children = mfs_has_children,
87 .lnkcnt_get = mfs_lnkcnt_get,
88 .size_block = mfs_size_block
89};
90
91/* Hash table interface for open nodes hash table */
92typedef struct {
93 service_id_t service_id;
94 fs_index_t index;
95} node_key_t;
96
97static size_t
98open_nodes_key_hash(void *key)
99{
100 node_key_t *node_key = (node_key_t*)key;
101 return hash_combine(node_key->service_id, node_key->index);
102}
103
104static size_t
105open_nodes_hash(const ht_link_t *item)
106{
107 struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
108 return hash_combine(m->instance->service_id, m->ino_i->index);
109}
110
111static bool
112open_nodes_key_equal(void *key, const ht_link_t *item)
113{
114 node_key_t *node_key = (node_key_t*)key;
115 struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
116
117 return node_key->service_id == mnode->instance->service_id
118 && node_key->index == mnode->ino_i->index;
119}
120
121static hash_table_ops_t open_nodes_ops = {
122 .hash = open_nodes_hash,
123 .key_hash = open_nodes_key_hash,
124 .key_equal = open_nodes_key_equal,
125 .equal = NULL,
126 .remove_callback = NULL,
127};
128
129int
130mfs_global_init(void)
131{
132 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
133 return ENOMEM;
134 }
135 return EOK;
136}
137
138static int
139mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
140 aoff64_t *size, unsigned *linkcnt)
141{
142 enum cache_mode cmode;
143 struct mfs_superblock *sb = NULL;
144 struct mfs3_superblock *sb3 = NULL;
145 struct mfs_sb_info *sbi = NULL;
146 struct mfs_instance *instance = NULL;
147 bool native, longnames;
148 mfs_version_t version;
149 uint16_t magic;
150 int rc;
151
152 /* Check for option enabling write through. */
153 if (str_cmp(opts, "wtcache") == 0)
154 cmode = CACHE_MODE_WT;
155 else
156 cmode = CACHE_MODE_WB;
157
158 /* initialize libblock */
159 rc = block_init(EXCHANGE_SERIALIZE, service_id, 4096);
160 if (rc != EOK)
161 return rc;
162
163 /* Allocate space for generic MFS superblock */
164 sbi = malloc(sizeof(*sbi));
165 if (!sbi) {
166 rc = ENOMEM;
167 goto out_error;
168 }
169
170 /* Allocate space for filesystem instance */
171 instance = malloc(sizeof(*instance));
172 if (!instance) {
173 rc = ENOMEM;
174 goto out_error;
175 }
176
177 sb = malloc(MFS_SUPERBLOCK_SIZE);
178 if (!sb) {
179 rc = ENOMEM;
180 goto out_error;
181 }
182
183 /* Read the superblock */
184 rc = block_read_direct(service_id, MFS_SUPERBLOCK << 1, 2, sb);
185 if (rc != EOK)
186 goto out_error;
187
188 sb3 = (struct mfs3_superblock *) sb;
189
190 if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
191 /* This is a V1 or V2 Minix filesystem */
192 magic = sb->s_magic;
193 } else if (check_magic_number(sb3->s_magic, &native,
194 &version, &longnames)) {
195 /* This is a V3 Minix filesystem */
196 magic = sb3->s_magic;
197 } else {
198 /* Not recognized */
199 mfsdebug("magic number not recognized\n");
200 rc = ENOTSUP;
201 goto out_error;
202 }
203
204 mfsdebug("magic number recognized = %04x\n", magic);
205
206 /* Fill superblock info structure */
207
208 sbi->fs_version = version;
209 sbi->long_names = longnames;
210 sbi->native = native;
211 sbi->magic = magic;
212 sbi->isearch = 0;
213 sbi->zsearch = 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 /* In MFS, file space is allocated per zones.
249 * Zones are a collection of consecutive blocks on disk.
250 *
251 * The current MFS implementation supports only filesystems
252 * where the size of a zone is equal to the
253 * size of a block.
254 */
255 rc = ENOTSUP;
256 goto out_error;
257 }
258
259 sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
260 if ((rc = mfs_check_sanity(sbi)) != EOK) {
261 fprintf(stderr, "Filesystem corrupted, invalid superblock");
262 goto out_error;
263 }
264
265 rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
266 if (rc != EOK) {
267 mfsdebug("block cache initialization failed\n");
268 rc = EINVAL;
269 goto out_error;
270 }
271
272 /* Initialize the instance structure and remember it */
273 instance->service_id = service_id;
274 instance->sbi = sbi;
275 instance->open_nodes_cnt = 0;
276 rc = fs_instance_create(service_id, instance);
277 if (rc != EOK) {
278 block_cache_fini(service_id);
279 mfsdebug("fs instance creation failed\n");
280 goto out_error;
281 }
282
283 mfsdebug("mount successful\n");
284
285 fs_node_t *fn;
286 mfs_node_get(&fn, service_id, MFS_ROOT_INO);
287
288 struct mfs_node *mroot = fn->data;
289
290 *index = mroot->ino_i->index;
291 *size = mroot->ino_i->i_size;
292 *linkcnt = 1;
293
294 free(sb);
295
296 return mfs_node_put(fn);
297
298out_error:
299 block_fini(service_id);
300 if (sb)
301 free(sb);
302 if (sbi)
303 free(sbi);
304 if(instance)
305 free(instance);
306 return rc;
307}
308
309static int
310mfs_unmounted(service_id_t service_id)
311{
312 struct mfs_instance *inst;
313
314 mfsdebug("%s()\n", __FUNCTION__);
315
316 int r = mfs_instance_get(service_id, &inst);
317 if (r != EOK)
318 return r;
319
320 if (inst->open_nodes_cnt != 0)
321 return EBUSY;
322
323 (void) block_cache_fini(service_id);
324 block_fini(service_id);
325
326 /* Remove and destroy the instance */
327 (void) fs_instance_destroy(service_id);
328 free(inst->sbi);
329 free(inst);
330 return EOK;
331}
332
333service_id_t
334mfs_service_get(fs_node_t *fsnode)
335{
336 struct mfs_node *node = fsnode->data;
337 return node->instance->service_id;
338}
339
340static int
341mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
342{
343 int r;
344 struct mfs_instance *inst;
345 struct mfs_node *mnode;
346 fs_node_t *fsnode;
347 uint32_t inum;
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 ino_i->i_nlinks = 0;
384 }
385
386 ino_i->i_uid = 0;
387 ino_i->i_gid = 0;
388 ino_i->i_size = 0;
389 ino_i->i_atime = 0;
390 ino_i->i_mtime = 0;
391 ino_i->i_ctime = 0;
392
393 memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
394 memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
395
396 mfsdebug("new node idx = %d\n", (int) inum);
397
398 ino_i->index = inum;
399 ino_i->dirty = true;
400 mnode->ino_i = ino_i;
401 mnode->instance = inst;
402 mnode->refcnt = 1;
403
404 fibril_mutex_lock(&open_nodes_lock);
405 hash_table_insert(&open_nodes, &mnode->link);
406 fibril_mutex_unlock(&open_nodes_lock);
407 inst->open_nodes_cnt++;
408
409 mnode->ino_i->dirty = true;
410
411 fs_node_initialize(fsnode);
412 fsnode->data = mnode;
413 mnode->fsnode = fsnode;
414 *rfn = fsnode;
415
416 return EOK;
417
418out_err_2:
419 free(mnode);
420out_err_1:
421 free(ino_i);
422out_err:
423 mfs_free_inode(inst, inum);
424 return r;
425}
426
427static int
428mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
429{
430 struct mfs_node *mnode = pfn->data;
431 struct mfs_ino_info *ino_i = mnode->ino_i;
432 struct mfs_dentry_info d_info;
433 int r;
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 rc = mfs_instance_get(service_id, &instance);
482 if (rc != EOK)
483 return rc;
484
485 return mfs_node_core_get(rfn, instance, index);
486}
487
488static int
489mfs_node_put(fs_node_t *fsnode)
490{
491 int rc = EOK;
492 struct mfs_node *mnode = fsnode->data;
493
494 fibril_mutex_lock(&open_nodes_lock);
495
496 assert(mnode->refcnt > 0);
497 mnode->refcnt--;
498 if (mnode->refcnt == 0) {
499 hash_table_remove_item(&open_nodes, &mnode->link);
500 assert(mnode->instance->open_nodes_cnt > 0);
501 mnode->instance->open_nodes_cnt--;
502 rc = mfs_put_inode(mnode);
503 free(mnode->ino_i);
504 free(mnode);
505 free(fsnode);
506 }
507
508 fibril_mutex_unlock(&open_nodes_lock);
509 return rc;
510}
511
512static int
513mfs_node_open(fs_node_t *fsnode)
514{
515 /*
516 * Opening a file is stateless, nothing
517 * to be done here.
518 */
519 return EOK;
520}
521
522static fs_index_t
523mfs_index_get(fs_node_t *fsnode)
524{
525 struct mfs_node *mnode = fsnode->data;
526 return mnode->ino_i->index;
527}
528
529static unsigned
530mfs_lnkcnt_get(fs_node_t *fsnode)
531{
532 struct mfs_node *mnode = fsnode->data;
533
534 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
535
536 if (S_ISDIR(mnode->ino_i->i_mode)) {
537 if (mnode->ino_i->i_nlinks > 1)
538 return 1;
539 else
540 return 0;
541 } else
542 return mnode->ino_i->i_nlinks;
543}
544
545static int
546mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
547 fs_index_t index)
548{
549 fs_node_t *node = NULL;
550 struct mfs_node *mnode = NULL;
551 int rc;
552
553 fibril_mutex_lock(&open_nodes_lock);
554
555 /* Check if the node is not already open */
556 node_key_t key = {
557 .service_id = inst->service_id,
558 .index = index
559 };
560
561 ht_link_t *already_open = hash_table_find(&open_nodes, &key);
562
563 if (already_open) {
564 mnode = hash_table_get_inst(already_open, struct mfs_node, link);
565
566 *rfn = mnode->fsnode;
567 mnode->refcnt++;
568
569 fibril_mutex_unlock(&open_nodes_lock);
570 return EOK;
571 }
572
573 node = malloc(sizeof(fs_node_t));
574 if (!node) {
575 rc = ENOMEM;
576 goto out_err;
577 }
578
579 fs_node_initialize(node);
580
581 mnode = malloc(sizeof(*mnode));
582 if (!mnode) {
583 rc = ENOMEM;
584 goto out_err;
585 }
586
587 struct mfs_ino_info *ino_i;
588
589 rc = mfs_get_inode(inst, &ino_i, index);
590 if (rc != EOK)
591 goto out_err;
592
593 ino_i->index = index;
594 mnode->ino_i = ino_i;
595 mnode->refcnt = 1;
596
597 mnode->instance = inst;
598 node->data = mnode;
599 mnode->fsnode = node;
600 *rfn = node;
601
602 hash_table_insert(&open_nodes, &mnode->link);
603 inst->open_nodes_cnt++;
604
605 fibril_mutex_unlock(&open_nodes_lock);
606
607 return EOK;
608
609out_err:
610 if (node)
611 free(node);
612 if (mnode)
613 free(mnode);
614 fibril_mutex_unlock(&open_nodes_lock);
615 return rc;
616}
617
618static bool
619mfs_is_directory(fs_node_t *fsnode)
620{
621 const struct mfs_node *node = fsnode->data;
622 return S_ISDIR(node->ino_i->i_mode);
623}
624
625static bool
626mfs_is_file(fs_node_t *fsnode)
627{
628 struct mfs_node *node = fsnode->data;
629 return S_ISREG(node->ino_i->i_mode);
630}
631
632static int
633mfs_root_get(fs_node_t **rfn, service_id_t service_id)
634{
635 int rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
636 return rc;
637}
638
639static int
640mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
641{
642 struct mfs_node *parent = pfn->data;
643 struct mfs_node *child = cfn->data;
644 struct mfs_sb_info *sbi = parent->instance->sbi;
645 bool destroy_dentry = false;
646
647 if (str_size(name) > sbi->max_name_len)
648 return ENAMETOOLONG;
649
650 int r = mfs_insert_dentry(parent, name, child->ino_i->index);
651 if (r != EOK)
652 return r;
653
654 if (S_ISDIR(child->ino_i->i_mode)) {
655 if (child->ino_i->i_nlinks != 1) {
656 /* It's not possible to hardlink directories in MFS */
657 destroy_dentry = true;
658 r = EMLINK;
659 goto exit;
660 }
661 r = mfs_insert_dentry(child, ".", child->ino_i->index);
662 if (r != EOK) {
663 destroy_dentry = true;
664 goto exit;
665 }
666
667 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
668 if (r != EOK) {
669 mfs_remove_dentry(child, ".");
670 destroy_dentry = true;
671 goto exit;
672 }
673
674 parent->ino_i->i_nlinks++;
675 parent->ino_i->dirty = true;
676 }
677
678exit:
679 if (destroy_dentry) {
680 int r2 = mfs_remove_dentry(parent, name);
681 if (r2 != EOK)
682 r = r2;
683 } else {
684 child->ino_i->i_nlinks++;
685 child->ino_i->dirty = true;
686 }
687 return r;
688}
689
690static int
691mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
692{
693 struct mfs_node *parent = pfn->data;
694 struct mfs_node *child = cfn->data;
695 bool has_children;
696 int r;
697
698 if (!parent)
699 return EBUSY;
700
701 r = mfs_has_children(&has_children, cfn);
702 if (r != EOK)
703 return r;
704
705 if (has_children)
706 return ENOTEMPTY;
707
708 r = mfs_remove_dentry(parent, name);
709 if (r != EOK)
710 return r;
711
712 struct mfs_ino_info *chino = child->ino_i;
713
714 assert(chino->i_nlinks >= 1);
715 chino->i_nlinks--;
716 mfsdebug("Links: %d\n", chino->i_nlinks);
717
718 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
719 /* The child directory will be destroyed, decrease the
720 * parent hard links counter.
721 */
722 parent->ino_i->i_nlinks--;
723 parent->ino_i->dirty = true;
724 }
725
726 chino->dirty = true;
727
728 return r;
729}
730
731static int
732mfs_has_children(bool *has_children, fs_node_t *fsnode)
733{
734 struct mfs_node *mnode = fsnode->data;
735 struct mfs_sb_info *sbi = mnode->instance->sbi;
736 int r;
737
738 *has_children = false;
739
740 if (!S_ISDIR(mnode->ino_i->i_mode))
741 goto out;
742
743 struct mfs_dentry_info d_info;
744
745 /* The first two dentries are always . and .. */
746 unsigned i;
747 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
748 r = mfs_read_dentry(mnode, &d_info, i);
749 if (r != EOK)
750 return r;
751
752 if (d_info.d_inum) {
753 /* A valid entry has been found */
754 *has_children = true;
755 break;
756 }
757 }
758out:
759
760 return EOK;
761}
762
763static int
764mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
765 size_t *rbytes)
766{
767 int rc;
768 fs_node_t *fn = NULL;
769
770 rc = mfs_node_get(&fn, service_id, index);
771 if (rc != EOK)
772 return rc;
773 if (!fn)
774 return ENOENT;
775
776 struct mfs_node *mnode;
777 struct mfs_ino_info *ino_i;
778 size_t len, bytes = 0;
779 ipc_callid_t callid;
780
781 mnode = fn->data;
782 ino_i = mnode->ino_i;
783
784 if (!async_data_read_receive(&callid, &len)) {
785 rc = EINVAL;
786 goto out_error;
787 }
788
789 if (S_ISDIR(ino_i->i_mode)) {
790 aoff64_t spos = pos;
791 struct mfs_dentry_info d_info;
792 struct mfs_sb_info *sbi = mnode->instance->sbi;
793
794 if (pos < 2) {
795 /* Skip the first two dentries ('.' and '..') */
796 pos = 2;
797 }
798
799 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
800 rc = mfs_read_dentry(mnode, &d_info, pos);
801 if (rc != EOK)
802 goto out_error;
803
804 if (d_info.d_inum) {
805 /* Dentry found! */
806 goto found;
807 }
808 }
809
810 rc = mfs_node_put(fn);
811 async_answer_0(callid, rc != EOK ? rc : ENOENT);
812 return rc;
813found:
814 async_data_read_finalize(callid, d_info.d_name,
815 str_size(d_info.d_name) + 1);
816 bytes = ((pos - spos) + 1);
817 } else {
818 struct mfs_sb_info *sbi = mnode->instance->sbi;
819
820 if (pos >= (size_t) ino_i->i_size) {
821 /* Trying to read beyond the end of file */
822 bytes = 0;
823 (void) async_data_read_finalize(callid, NULL, 0);
824 goto out_success;
825 }
826
827 bytes = min(len, sbi->block_size - pos % sbi->block_size);
828 bytes = min(bytes, ino_i->i_size - pos);
829
830 uint32_t zone;
831 block_t *b;
832
833 rc = mfs_read_map(&zone, mnode, pos);
834 if (rc != EOK)
835 goto out_error;
836
837 if (zone == 0) {
838 /* sparse file */
839 uint8_t *buf = malloc(sbi->block_size);
840 if (!buf) {
841 rc = ENOMEM;
842 goto out_error;
843 }
844 memset(buf, 0, sizeof(sbi->block_size));
845 async_data_read_finalize(callid,
846 buf + pos % sbi->block_size, bytes);
847 free(buf);
848 goto out_success;
849 }
850
851 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
852 if (rc != EOK)
853 goto out_error;
854
855 async_data_read_finalize(callid, b->data +
856 pos % sbi->block_size, bytes);
857
858 rc = block_put(b);
859 if (rc != EOK) {
860 mfs_node_put(fn);
861 return rc;
862 }
863 }
864out_success:
865 rc = mfs_node_put(fn);
866 *rbytes = bytes;
867 return rc;
868out_error:
869 ;
870 int tmp = mfs_node_put(fn);
871 async_answer_0(callid, tmp != EOK ? tmp : rc);
872 return tmp != EOK ? tmp : rc;
873}
874
875static int
876mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
877 size_t *wbytes, aoff64_t *nsize)
878{
879 fs_node_t *fn;
880 int r;
881 int flags = BLOCK_FLAGS_NONE;
882
883 r = mfs_node_get(&fn, service_id, index);
884 if (r != EOK)
885 return r;
886 if (!fn)
887 return ENOENT;
888
889 ipc_callid_t callid;
890 size_t len;
891
892 if (!async_data_write_receive(&callid, &len)) {
893 r = EINVAL;
894 goto out_err;
895 }
896
897 struct mfs_node *mnode = fn->data;
898 struct mfs_sb_info *sbi = mnode->instance->sbi;
899 struct mfs_ino_info *ino_i = mnode->ino_i;
900 const size_t bs = sbi->block_size;
901 size_t bytes = min(len, bs - (pos % bs));
902 uint32_t block;
903
904 if (bytes == bs)
905 flags = BLOCK_FLAGS_NOREAD;
906
907 r = mfs_read_map(&block, mnode, pos);
908 if (r != EOK)
909 goto out_err;
910
911 if (block == 0) {
912 uint32_t dummy;
913
914 r = mfs_alloc_zone(mnode->instance, &block);
915 if (r != EOK)
916 goto out_err;
917
918 r = mfs_write_map(mnode, pos, block, &dummy);
919 if (r != EOK) {
920 mfs_free_zone(mnode->instance, block);
921 goto out_err;
922 }
923
924 flags = BLOCK_FLAGS_NOREAD;
925 }
926
927 block_t *b;
928 r = block_get(&b, service_id, block, flags);
929 if (r != EOK)
930 goto out_err;
931
932 if (flags == BLOCK_FLAGS_NOREAD)
933 memset(b->data, 0, sbi->block_size);
934
935 async_data_write_finalize(callid, b->data + (pos % bs), bytes);
936 b->dirty = true;
937
938 r = block_put(b);
939 if (r != EOK) {
940 mfs_node_put(fn);
941 return r;
942 }
943
944 if (pos + bytes > ino_i->i_size) {
945 ino_i->i_size = pos + bytes;
946 ino_i->dirty = true;
947 }
948 r = mfs_node_put(fn);
949 *nsize = ino_i->i_size;
950 *wbytes = bytes;
951 return r;
952
953out_err:
954 mfs_node_put(fn);
955 async_answer_0(callid, r);
956 return r;
957}
958
959static int
960mfs_destroy(service_id_t service_id, fs_index_t index)
961{
962 fs_node_t *fn = NULL;
963 int r;
964
965 r = mfs_node_get(&fn, service_id, index);
966 if (r != EOK)
967 return r;
968 if (!fn)
969 return ENOENT;
970
971 /* Destroy the inode */
972 return mfs_destroy_node(fn);
973}
974
975static int
976mfs_destroy_node(fs_node_t *fn)
977{
978 struct mfs_node *mnode = fn->data;
979 bool has_children;
980 int r;
981
982 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
983
984 r = mfs_has_children(&has_children, fn);
985 if (r != EOK)
986 goto out;
987
988 assert(!has_children);
989
990 /* Free the entire inode content */
991 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
992 if (r != EOK)
993 goto out;
994
995 /* Mark the inode as free in the bitmap */
996 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
997
998out:
999 mfs_node_put(fn);
1000 return r;
1001}
1002
1003static int
1004mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
1005{
1006 fs_node_t *fn;
1007 int r;
1008
1009 r = mfs_node_get(&fn, service_id, index);
1010 if (r != EOK)
1011 return r;
1012 if (!fn)
1013 return r;
1014
1015 struct mfs_node *mnode = fn->data;
1016 struct mfs_ino_info *ino_i = mnode->ino_i;
1017
1018 if (ino_i->i_size == size)
1019 r = EOK;
1020 else
1021 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1022
1023 mfs_node_put(fn);
1024 return r;
1025}
1026
1027static int
1028mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1029{
1030 void *data;
1031 int rc;
1032
1033 rc = fs_instance_get(service_id, &data);
1034 if (rc == EOK)
1035 *instance = (struct mfs_instance *) data;
1036 else {
1037 mfsdebug("instance not found\n");
1038 }
1039
1040 return rc;
1041}
1042
1043static bool
1044check_magic_number(uint16_t magic, bool *native,
1045 mfs_version_t *version, bool *longfilenames)
1046{
1047 bool rc = true;
1048 *longfilenames = false;
1049
1050 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1051 *native = magic == MFS_MAGIC_V1;
1052 *version = MFS_VERSION_V1;
1053 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1054 *native = magic == MFS_MAGIC_V1L;
1055 *version = MFS_VERSION_V1;
1056 *longfilenames = true;
1057 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1058 *native = magic == MFS_MAGIC_V2;
1059 *version = MFS_VERSION_V2;
1060 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1061 *native = magic == MFS_MAGIC_V2L;
1062 *version = MFS_VERSION_V2;
1063 *longfilenames = true;
1064 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1065 *native = magic == MFS_MAGIC_V3;
1066 *version = MFS_VERSION_V3;
1067 } else
1068 rc = false;
1069
1070 return rc;
1071}
1072
1073/** Filesystem sanity check
1074 *
1075 * @param Pointer to the MFS superblock.
1076 *
1077 * @return EOK on success, ENOTSUP otherwise.
1078 */
1079static int
1080mfs_check_sanity(struct mfs_sb_info *sbi)
1081{
1082 if (!is_power_of_two(sbi->block_size) ||
1083 sbi->block_size < MFS_MIN_BLOCKSIZE ||
1084 sbi->block_size > MFS_MAX_BLOCKSIZE)
1085 return ENOTSUP;
1086 else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
1087 return ENOTSUP;
1088 else if (sbi->ninodes == 0 || sbi->nzones == 0)
1089 return ENOTSUP;
1090 else if (sbi->firstdatazone == 0)
1091 return ENOTSUP;
1092
1093 return EOK;
1094}
1095
1096static int
1097mfs_close(service_id_t service_id, fs_index_t index)
1098{
1099 return 0;
1100}
1101
1102static int
1103mfs_sync(service_id_t service_id, fs_index_t index)
1104{
1105 fs_node_t *fn = NULL;
1106 int rc = mfs_node_get(&fn, service_id, index);
1107 if (rc != EOK)
1108 return rc;
1109 if (!fn)
1110 return ENOENT;
1111
1112 struct mfs_node *mnode = fn->data;
1113 mnode->ino_i->dirty = true;
1114
1115 return mfs_node_put(fn);
1116}
1117
1118/** Check if a given number is a power of two.
1119 *
1120 * @param n The number to check.
1121 *
1122 * @return true if it is a power of two, false otherwise.
1123 */
1124static bool
1125is_power_of_two(uint32_t n)
1126{
1127 if (n == 0)
1128 return false;
1129
1130 return (n & (n - 1)) == 0;
1131}
1132
1133static long
1134mfs_size_block(service_id_t service_id)
1135{
1136 long block_size;
1137
1138 struct mfs_instance *inst;
1139 int rc = mfs_instance_get(service_id, &inst);
1140 if (rc != EOK)
1141 return rc;
1142 if (NULL == inst)
1143 return ENOENT;
1144 block_size = inst->sbi->block_size;
1145 return block_size;
1146}
1147
1148vfs_out_ops_t mfs_ops = {
1149 .mounted = mfs_mounted,
1150 .unmounted = mfs_unmounted,
1151 .read = mfs_read,
1152 .write = mfs_write,
1153 .truncate = mfs_truncate,
1154 .close = mfs_close,
1155 .destroy = mfs_destroy,
1156 .sync = mfs_sync,
1157};
1158
1159/**
1160 * @}
1161 */
1162
Note: See TracBrowser for help on using the repository browser.