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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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