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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca7286 was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

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