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

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

We don't need to call block_cache_fini() if block_cache_init() failed.

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