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

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

fix hard links creation (they are needed to support the rename() function, used by the mv command).

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