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

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

Implement statfs operation.

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