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

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

Currently MFS does not support minix filesystems where the size of a zone is not equal to the size
of a block.

Refuse to mount the filesystem if the "log2_zone_size" field of the superblock is not zero.

  • Property mode set to 100644
File size: 24.8 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 block_cache_fini(service_id);
270 rc = EINVAL;
271 goto out_error;
272 }
273
274 /*Initialize the instance structure and add it to the list*/
275 link_initialize(&instance->link);
276 instance->service_id = service_id;
277 instance->sbi = sbi;
278
279 fibril_mutex_lock(&inst_list_mutex);
280 list_append(&instance->link, &inst_list);
281 fibril_mutex_unlock(&inst_list_mutex);
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 the instance from the list */
327 fibril_mutex_lock(&inst_list_mutex);
328 list_remove(&inst->link);
329 fibril_mutex_unlock(&inst_list_mutex);
330
331 free(inst->sbi);
332 free(inst);
333 return EOK;
334}
335
336service_id_t mfs_service_get(fs_node_t *fsnode)
337{
338 struct mfs_node *node = fsnode->data;
339 return node->instance->service_id;
340}
341
342static int mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
343{
344 int r;
345 struct mfs_instance *inst;
346 struct mfs_node *mnode;
347 fs_node_t *fsnode;
348 uint32_t inum;
349
350 mfsdebug("%s()\n", __FUNCTION__);
351
352 r = mfs_instance_get(service_id, &inst);
353 if (r != EOK)
354 return r;
355
356 /*Alloc a new inode*/
357 r = mfs_alloc_inode(inst, &inum);
358 if (r != EOK)
359 return r;
360
361 struct mfs_ino_info *ino_i;
362
363 ino_i = malloc(sizeof(*ino_i));
364 if (!ino_i) {
365 r = ENOMEM;
366 goto out_err;
367 }
368
369 mnode = malloc(sizeof(*mnode));
370 if (!mnode) {
371 r = ENOMEM;
372 goto out_err_1;
373 }
374
375 fsnode = malloc(sizeof(fs_node_t));
376 if (!fsnode) {
377 r = ENOMEM;
378 goto out_err_2;
379 }
380
381 if (flags & L_DIRECTORY) {
382 ino_i->i_mode = S_IFDIR;
383 ino_i->i_nlinks = 2; /*This accounts for the '.' dentry*/
384 } else {
385 ino_i->i_mode = S_IFREG;
386 ino_i->i_nlinks = 1;
387 }
388
389 ino_i->i_uid = 0;
390 ino_i->i_gid = 0;
391 ino_i->i_size = 0;
392 ino_i->i_atime = 0;
393 ino_i->i_mtime = 0;
394 ino_i->i_ctime = 0;
395
396 memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
397 memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
398
399 mfsdebug("new node idx = %d\n", (int) inum);
400
401 ino_i->index = inum;
402 ino_i->dirty = true;
403 mnode->ino_i = ino_i;
404 mnode->instance = inst;
405 mnode->refcnt = 1;
406
407 link_initialize(&mnode->link);
408
409 unsigned long key[] = {
410 [OPEN_NODES_SERVICE_KEY] = inst->service_id,
411 [OPEN_NODES_INODE_KEY] = inum,
412 };
413
414 fibril_mutex_lock(&open_nodes_lock);
415 hash_table_insert(&open_nodes, key, &mnode->link);
416 fibril_mutex_unlock(&open_nodes_lock);
417 inst->open_nodes_cnt++;
418
419 mnode->ino_i->dirty = true;
420
421 fs_node_initialize(fsnode);
422 fsnode->data = mnode;
423 mnode->fsnode = fsnode;
424 *rfn = fsnode;
425
426 return EOK;
427
428out_err_2:
429 free(mnode);
430out_err_1:
431 free(ino_i);
432out_err:
433 return r;
434}
435
436static int mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
437{
438 struct mfs_node *mnode = pfn->data;
439 struct mfs_ino_info *ino_i = mnode->ino_i;
440 struct mfs_dentry_info d_info;
441 int r;
442
443 mfsdebug("%s()\n", __FUNCTION__);
444
445 if (!S_ISDIR(ino_i->i_mode))
446 return ENOTDIR;
447
448 struct mfs_sb_info *sbi = mnode->instance->sbi;
449 const size_t comp_size = str_size(component);
450
451 unsigned i;
452 for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
453 r = mfs_read_dentry(mnode, &d_info, i);
454 if (r != EOK)
455 return r;
456
457 if (!d_info.d_inum) {
458 /*This entry is not used*/
459 continue;
460 }
461
462 const size_t dentry_name_size = str_size(d_info.d_name);
463
464 if (comp_size == dentry_name_size &&
465 !bcmp(component, d_info.d_name, dentry_name_size)) {
466 /*Hit!*/
467 mfs_node_core_get(rfn, mnode->instance,
468 d_info.d_inum);
469 goto found;
470 }
471 }
472 *rfn = NULL;
473found:
474 return EOK;
475}
476
477static aoff64_t mfs_size_get(fs_node_t *node)
478{
479 const struct mfs_node *mnode = node->data;
480 return mnode->ino_i->i_size;
481}
482
483static int
484mfs_node_get(fs_node_t **rfn, service_id_t service_id,
485 fs_index_t index)
486{
487 int rc;
488 struct mfs_instance *instance;
489
490 mfsdebug("%s()\n", __FUNCTION__);
491
492 rc = mfs_instance_get(service_id, &instance);
493 if (rc != EOK)
494 return rc;
495
496 return mfs_node_core_get(rfn, instance, index);
497}
498
499static int
500mfs_node_put(fs_node_t *fsnode)
501{
502 int rc = EOK;
503 struct mfs_node *mnode = fsnode->data;
504
505 mfsdebug("%s()\n", __FUNCTION__);
506
507 fibril_mutex_lock(&open_nodes_lock);
508
509 assert(mnode->refcnt > 0);
510 mnode->refcnt--;
511 if (mnode->refcnt == 0) {
512 unsigned long key[] = {
513 [OPEN_NODES_SERVICE_KEY] = mnode->instance->service_id,
514 [OPEN_NODES_INODE_KEY] = mnode->ino_i->index
515 };
516 hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
517 assert(mnode->instance->open_nodes_cnt > 0);
518 mnode->instance->open_nodes_cnt--;
519 rc = mfs_put_inode(mnode);
520 free(mnode->ino_i);
521 free(mnode);
522 free(fsnode);
523 }
524
525 fibril_mutex_unlock(&open_nodes_lock);
526 return rc;
527}
528
529static int mfs_node_open(fs_node_t *fsnode)
530{
531 /*
532 * Opening a file is stateless, nothing
533 * to be done here.
534 */
535 return EOK;
536}
537
538static fs_index_t mfs_index_get(fs_node_t *fsnode)
539{
540 struct mfs_node *mnode = fsnode->data;
541 return mnode->ino_i->index;
542}
543
544static unsigned mfs_lnkcnt_get(fs_node_t *fsnode)
545{
546 struct mfs_node *mnode = fsnode->data;
547
548 mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
549
550 if (S_ISDIR(mnode->ino_i->i_mode)) {
551 if (mnode->ino_i->i_nlinks > 1)
552 return 1;
553 else
554 return 0;
555 } else
556 return mnode->ino_i->i_nlinks;
557}
558
559static int mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
560 fs_index_t index)
561{
562 fs_node_t *node = NULL;
563 struct mfs_node *mnode = NULL;
564 int rc;
565
566 mfsdebug("%s()\n", __FUNCTION__);
567
568 fibril_mutex_lock(&open_nodes_lock);
569
570 /* Check if the node is not already open */
571 unsigned long key[] = {
572 [OPEN_NODES_SERVICE_KEY] = inst->service_id,
573 [OPEN_NODES_INODE_KEY] = index,
574 };
575 link_t *already_open = hash_table_find(&open_nodes, key);
576
577 if (already_open) {
578 mnode = hash_table_get_instance(already_open, struct mfs_node, link);
579 *rfn = mnode->fsnode;
580 mnode->refcnt++;
581
582 fibril_mutex_unlock(&open_nodes_lock);
583 return EOK;
584 }
585
586 node = malloc(sizeof(fs_node_t));
587 if (!node) {
588 rc = ENOMEM;
589 goto out_err;
590 }
591
592 fs_node_initialize(node);
593
594 mnode = malloc(sizeof(*mnode));
595 if (!mnode) {
596 rc = ENOMEM;
597 goto out_err;
598 }
599
600 struct mfs_ino_info *ino_i;
601
602 rc = mfs_get_inode(inst, &ino_i, index);
603 if (rc != EOK)
604 goto out_err;
605
606 ino_i->index = index;
607 mnode->ino_i = ino_i;
608 mnode->refcnt = 1;
609 link_initialize(&mnode->link);
610
611 mnode->instance = inst;
612 node->data = mnode;
613 mnode->fsnode = node;
614 *rfn = node;
615
616 hash_table_insert(&open_nodes, key, &mnode->link);
617 inst->open_nodes_cnt++;
618
619 fibril_mutex_unlock(&open_nodes_lock);
620
621 return EOK;
622
623out_err:
624 if (node)
625 free(node);
626 if (mnode)
627 free(mnode);
628 fibril_mutex_unlock(&open_nodes_lock);
629 return rc;
630}
631
632static bool mfs_is_directory(fs_node_t *fsnode)
633{
634 const struct mfs_node *node = fsnode->data;
635 return S_ISDIR(node->ino_i->i_mode);
636}
637
638static bool mfs_is_file(fs_node_t *fsnode)
639{
640 struct mfs_node *node = fsnode->data;
641 return S_ISREG(node->ino_i->i_mode);
642}
643
644static int mfs_root_get(fs_node_t **rfn, service_id_t service_id)
645{
646 int rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
647 return rc;
648}
649
650static int mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
651{
652 struct mfs_node *parent = pfn->data;
653 struct mfs_node *child = cfn->data;
654 struct mfs_sb_info *sbi = parent->instance->sbi;
655
656 mfsdebug("%s()\n", __FUNCTION__);
657
658 if (str_size(name) > sbi->max_name_len)
659 return ENAMETOOLONG;
660
661 int r = mfs_insert_dentry(parent, name, child->ino_i->index);
662 if (r != EOK)
663 goto exit_error;
664
665 if (S_ISDIR(child->ino_i->i_mode)) {
666 r = mfs_insert_dentry(child, ".", child->ino_i->index);
667 if (r != EOK)
668 goto exit_error;
669
670 r = mfs_insert_dentry(child, "..", parent->ino_i->index);
671 if (r != EOK)
672 goto exit_error;
673
674 parent->ino_i->i_nlinks++;
675 parent->ino_i->dirty = true;
676 }
677
678exit_error:
679 return r;
680}
681
682static int
683mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
684{
685 struct mfs_node *parent = pfn->data;
686 struct mfs_node *child = cfn->data;
687 bool has_children;
688 int r;
689
690 mfsdebug("%s()\n", __FUNCTION__);
691
692 if (!parent)
693 return EBUSY;
694
695 r = mfs_has_children(&has_children, cfn);
696 if (r != EOK)
697 return r;
698
699 if (has_children)
700 return ENOTEMPTY;
701
702 r = mfs_remove_dentry(parent, name);
703 if (r != EOK)
704 return r;
705
706 struct mfs_ino_info *chino = child->ino_i;
707
708 assert(chino->i_nlinks >= 1);
709 chino->i_nlinks--;
710 mfsdebug("Links: %d\n", chino->i_nlinks);
711
712 if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
713 /* The child directory will be destroyed, decrease the
714 * parent hard links counter.
715 */
716 parent->ino_i->i_nlinks--;
717 parent->ino_i->dirty = true;
718 }
719
720 chino->dirty = true;
721
722 return r;
723}
724
725static int mfs_has_children(bool *has_children, fs_node_t *fsnode)
726{
727 struct mfs_node *mnode = fsnode->data;
728 struct mfs_sb_info *sbi = mnode->instance->sbi;
729 int r;
730
731 *has_children = false;
732
733 if (!S_ISDIR(mnode->ino_i->i_mode))
734 goto out;
735
736 struct mfs_dentry_info d_info;
737
738 /* The first two dentries are always . and .. */
739 unsigned i;
740 for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
741 r = mfs_read_dentry(mnode, &d_info, i);
742 if (r != EOK)
743 return r;
744
745 if (d_info.d_inum) {
746 /*A valid entry has been found*/
747 *has_children = true;
748 break;
749 }
750 }
751out:
752
753 return EOK;
754}
755
756static int
757mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
758 size_t *rbytes)
759{
760 int rc;
761 fs_node_t *fn;
762
763 rc = mfs_node_get(&fn, service_id, index);
764 if (rc != EOK)
765 return rc;
766 if (!fn)
767 return ENOENT;
768
769 struct mfs_node *mnode;
770 struct mfs_ino_info *ino_i;
771 size_t len, bytes = 0;
772 ipc_callid_t callid;
773
774 mnode = fn->data;
775 ino_i = mnode->ino_i;
776
777 if (!async_data_read_receive(&callid, &len)) {
778 rc = EINVAL;
779 goto out_error;
780 }
781
782 if (S_ISDIR(ino_i->i_mode)) {
783 aoff64_t spos = pos;
784 struct mfs_dentry_info d_info;
785 struct mfs_sb_info *sbi = mnode->instance->sbi;
786
787 if (pos < 2) {
788 /*Skip the first two dentries ('.' and '..')*/
789 pos = 2;
790 }
791
792 for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
793 rc = mfs_read_dentry(mnode, &d_info, pos);
794 if (rc != EOK)
795 goto out_error;
796
797 if (d_info.d_inum) {
798 /*Dentry found!*/
799 goto found;
800 }
801 }
802
803 rc = mfs_node_put(fn);
804 async_answer_0(callid, rc != EOK ? rc : ENOENT);
805 return rc;
806found:
807 async_data_read_finalize(callid, d_info.d_name,
808 str_size(d_info.d_name) + 1);
809 bytes = ((pos - spos) + 1);
810 } else {
811 struct mfs_sb_info *sbi = mnode->instance->sbi;
812
813 if (pos >= (size_t) ino_i->i_size) {
814 /*Trying to read beyond the end of file*/
815 bytes = 0;
816 (void) async_data_read_finalize(callid, NULL, 0);
817 goto out_success;
818 }
819
820 bytes = min(len, sbi->block_size - pos % sbi->block_size);
821 bytes = min(bytes, ino_i->i_size - pos);
822
823 uint32_t zone;
824 block_t *b;
825
826 rc = mfs_read_map(&zone, mnode, pos);
827 if (rc != EOK)
828 goto out_error;
829
830 if (zone == 0) {
831 /*sparse file*/
832 uint8_t *buf = malloc(sbi->block_size);
833 if (!buf) {
834 rc = ENOMEM;
835 goto out_error;
836 }
837 memset(buf, 0, sizeof(sbi->block_size));
838 async_data_read_finalize(callid,
839 buf + pos % sbi->block_size, bytes);
840 free(buf);
841 goto out_success;
842 }
843
844 rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
845 if (rc != EOK)
846 goto out_error;
847
848 async_data_read_finalize(callid, b->data +
849 pos % sbi->block_size, bytes);
850
851 rc = block_put(b);
852 if (rc != EOK) {
853 mfs_node_put(fn);
854 return rc;
855 }
856 }
857out_success:
858 rc = mfs_node_put(fn);
859 *rbytes = bytes;
860 return rc;
861out_error:
862 ;
863 int tmp = mfs_node_put(fn);
864 async_answer_0(callid, tmp != EOK ? tmp : rc);
865 return tmp != EOK ? tmp : rc;
866}
867
868static int
869mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
870 size_t *wbytes, aoff64_t *nsize)
871{
872 fs_node_t *fn;
873 int r;
874 int flags = BLOCK_FLAGS_NONE;
875
876 r = mfs_node_get(&fn, service_id, index);
877 if (r != EOK)
878 return r;
879 if (!fn)
880 return ENOENT;
881
882 ipc_callid_t callid;
883 size_t len;
884
885 if (!async_data_write_receive(&callid, &len)) {
886 r = EINVAL;
887 goto out_err;
888 }
889
890 struct mfs_node *mnode = fn->data;
891 struct mfs_sb_info *sbi = mnode->instance->sbi;
892 struct mfs_ino_info *ino_i = mnode->ino_i;
893 const size_t bs = sbi->block_size;
894 size_t bytes = min(len, bs - pos % bs);
895 size_t boundary = ROUND_UP(ino_i->i_size, bs);
896 uint32_t block;
897
898 if (bytes == bs)
899 flags = BLOCK_FLAGS_NOREAD;
900
901 if (pos < boundary) {
902 r = mfs_read_map(&block, mnode, pos);
903 if (r != EOK)
904 goto out_err;
905
906 if (block == 0) {
907 /*Writing in a sparse block*/
908 r = mfs_alloc_zone(mnode->instance, &block);
909 if (r != EOK)
910 goto out_err;
911 flags = BLOCK_FLAGS_NOREAD;
912 }
913 } else {
914 uint32_t dummy;
915
916 r = mfs_alloc_zone(mnode->instance, &block);
917 if (r != EOK)
918 goto out_err;
919
920 r = mfs_write_map(mnode, pos, block, &dummy);
921 if (r != EOK)
922 goto out_err;
923 }
924
925 block_t *b;
926 r = block_get(&b, service_id, block, flags);
927 if (r != EOK)
928 goto out_err;
929
930 async_data_write_finalize(callid, b->data + pos % bs, bytes);
931 b->dirty = true;
932
933 r = block_put(b);
934 if (r != EOK) {
935 mfs_node_put(fn);
936 return r;
937 }
938
939 ino_i->i_size = pos + bytes;
940 ino_i->dirty = true;
941 r = mfs_node_put(fn);
942 *nsize = pos + bytes;
943 *wbytes = bytes;
944 return r;
945
946out_err:
947 mfs_node_put(fn);
948 async_answer_0(callid, r);
949 return r;
950}
951
952static int
953mfs_destroy(service_id_t service_id, fs_index_t index)
954{
955 fs_node_t *fn;
956 int r;
957
958 r = mfs_node_get(&fn, service_id, index);
959 if (r != EOK)
960 return r;
961 if (!fn)
962 return ENOENT;
963
964 /*Destroy the inode*/
965 return mfs_destroy_node(fn);
966}
967
968static int
969mfs_destroy_node(fs_node_t *fn)
970{
971 struct mfs_node *mnode = fn->data;
972 bool has_children;
973 int r;
974
975 mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
976
977 r = mfs_has_children(&has_children, fn);
978 if (r != EOK)
979 goto out;
980
981 assert(!has_children);
982
983 /*Free the entire inode content*/
984 r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
985 if (r != EOK)
986 goto out;
987
988 /*Mark the inode as free in the bitmap*/
989 r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
990
991out:
992 mfs_node_put(fn);
993 return r;
994}
995
996static int
997mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
998{
999 fs_node_t *fn;
1000 int r;
1001
1002 r = mfs_node_get(&fn, service_id, index);
1003 if (r != EOK)
1004 return r;
1005 if (!fn)
1006 return r;
1007
1008 struct mfs_node *mnode = fn->data;
1009 struct mfs_ino_info *ino_i = mnode->ino_i;
1010
1011 if (ino_i->i_size == size)
1012 r = EOK;
1013 else
1014 r = mfs_inode_shrink(mnode, ino_i->i_size - size);
1015
1016 mfs_node_put(fn);
1017 return r;
1018}
1019
1020static int
1021mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
1022{
1023 struct mfs_instance *instance_ptr;
1024
1025 fibril_mutex_lock(&inst_list_mutex);
1026
1027 if (list_empty(&inst_list)) {
1028 fibril_mutex_unlock(&inst_list_mutex);
1029 return EINVAL;
1030 }
1031
1032 list_foreach(inst_list, link) {
1033 instance_ptr = list_get_instance(link, struct mfs_instance,
1034 link);
1035
1036 if (instance_ptr->service_id == service_id) {
1037 *instance = instance_ptr;
1038 fibril_mutex_unlock(&inst_list_mutex);
1039 return EOK;
1040 }
1041 }
1042
1043 mfsdebug("Instance not found\n");
1044
1045 fibril_mutex_unlock(&inst_list_mutex);
1046 return EINVAL;
1047}
1048
1049static bool check_magic_number(uint16_t magic, bool *native,
1050 mfs_version_t *version, bool *longfilenames)
1051{
1052 bool rc = true;
1053 *longfilenames = false;
1054
1055 if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
1056 *native = magic == MFS_MAGIC_V1;
1057 *version = MFS_VERSION_V1;
1058 } else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
1059 *native = magic == MFS_MAGIC_V1L;
1060 *version = MFS_VERSION_V1;
1061 *longfilenames = true;
1062 } else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
1063 *native = magic == MFS_MAGIC_V2;
1064 *version = MFS_VERSION_V2;
1065 } else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
1066 *native = magic == MFS_MAGIC_V2L;
1067 *version = MFS_VERSION_V2;
1068 *longfilenames = true;
1069 } else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
1070 *native = magic == MFS_MAGIC_V3;
1071 *version = MFS_VERSION_V3;
1072 } else
1073 rc = false;
1074
1075 return rc;
1076}
1077
1078static int
1079mfs_close(service_id_t service_id, fs_index_t index)
1080{
1081 return 0;
1082}
1083
1084static int
1085mfs_sync(service_id_t service_id, fs_index_t index)
1086{
1087 fs_node_t *fn;
1088 int rc = mfs_node_get(&fn, service_id, index);
1089 if (rc != EOK)
1090 return rc;
1091 if (!fn)
1092 return ENOENT;
1093
1094 struct mfs_node *mnode = fn->data;
1095 mnode->ino_i->dirty = true;
1096
1097 return mfs_node_put(fn);
1098}
1099
1100vfs_out_ops_t mfs_ops = {
1101 .mounted = mfs_mounted,
1102 .unmounted = mfs_unmounted,
1103 .read = mfs_read,
1104 .write = mfs_write,
1105 .truncate = mfs_truncate,
1106 .close = mfs_close,
1107 .destroy = mfs_destroy,
1108 .sync = mfs_sync,
1109};
1110
1111/**
1112 * @}
1113 */
1114
Note: See TracBrowser for help on using the repository browser.