HelenOS sources
This source file includes following definitions.
- open_nodes_key_hash
- open_nodes_hash
- open_nodes_key_equal
- mfs_global_init
- mfs_read_sb
- mfs_fsprobe
- mfs_mounted
- mfs_unmounted
- mfs_service_get
- mfs_create_node
- mfs_match
- mfs_size_get
- mfs_node_get
- mfs_node_put
- mfs_node_open
- mfs_index_get
- mfs_lnkcnt_get
- mfs_node_core_get
- mfs_is_directory
- mfs_is_file
- mfs_root_get
- mfs_link
- mfs_unlink
- mfs_has_children
- mfs_read
- mfs_write
- mfs_destroy
- mfs_destroy_node
- mfs_truncate
- mfs_instance_get
- check_magic_number
- mfs_check_sanity
- mfs_close
- mfs_sync
- is_power_of_two
- mfs_size_block
- mfs_total_block_count
- mfs_free_block_count
#include <stdlib.h>
#include <fibril_synch.h>
#include <align.h>
#include <adt/hash_table.h>
#include <adt/hash.h>
#include <str.h>
#include "mfs.h"
static bool check_magic_number(uint16_t magic, bool *native,
mfs_version_t *version, bool *longfilenames);
static errno_t mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
fs_index_t index);
static errno_t mfs_node_put(fs_node_t *fsnode);
static errno_t mfs_node_open(fs_node_t *fsnode);
static fs_index_t mfs_index_get(fs_node_t *fsnode);
static unsigned mfs_lnkcnt_get(fs_node_t *fsnode);
static bool mfs_is_directory(fs_node_t *fsnode);
static bool mfs_is_file(fs_node_t *fsnode);
static errno_t mfs_has_children(bool *has_children, fs_node_t *fsnode);
static errno_t mfs_root_get(fs_node_t **rfn, service_id_t service_id);
static service_id_t mfs_service_get(fs_node_t *fsnode);
static aoff64_t mfs_size_get(fs_node_t *node);
static errno_t mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
static errno_t mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags);
static errno_t mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
static errno_t mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
static errno_t mfs_destroy_node(fs_node_t *fn);
static errno_t mfs_node_get(fs_node_t **rfn, service_id_t service_id,
fs_index_t index);
static errno_t mfs_instance_get(service_id_t service_id,
struct mfs_instance **instance);
static errno_t mfs_check_sanity(struct mfs_sb_info *sbi);
static bool is_power_of_two(uint32_t n);
static errno_t mfs_size_block(service_id_t service_id, uint32_t *size);
static errno_t mfs_total_block_count(service_id_t service_id, uint64_t *count);
static errno_t mfs_free_block_count(service_id_t service_id, uint64_t *count);
static hash_table_t open_nodes;
static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
libfs_ops_t mfs_libfs_ops = {
.size_get = mfs_size_get,
.root_get = mfs_root_get,
.service_get = mfs_service_get,
.is_directory = mfs_is_directory,
.is_file = mfs_is_file,
.node_get = mfs_node_get,
.node_put = mfs_node_put,
.node_open = mfs_node_open,
.index_get = mfs_index_get,
.match = mfs_match,
.create = mfs_create_node,
.link = mfs_link,
.unlink = mfs_unlink,
.destroy = mfs_destroy_node,
.has_children = mfs_has_children,
.lnkcnt_get = mfs_lnkcnt_get,
.size_block = mfs_size_block,
.total_block_count = mfs_total_block_count,
.free_block_count = mfs_free_block_count
};
typedef struct {
service_id_t service_id;
fs_index_t index;
} node_key_t;
static size_t
open_nodes_key_hash(const void *key)
{
const node_key_t *node_key = key;
return hash_combine(node_key->service_id, node_key->index);
}
static size_t
open_nodes_hash(const ht_link_t *item)
{
struct mfs_node *m = hash_table_get_inst(item, struct mfs_node, link);
return hash_combine(m->instance->service_id, m->ino_i->index);
}
static bool
open_nodes_key_equal(const void *key, const ht_link_t *item)
{
const node_key_t *node_key = key;
struct mfs_node *mnode = hash_table_get_inst(item, struct mfs_node, link);
return node_key->service_id == mnode->instance->service_id &&
node_key->index == mnode->ino_i->index;
}
static const hash_table_ops_t open_nodes_ops = {
.hash = open_nodes_hash,
.key_hash = open_nodes_key_hash,
.key_equal = open_nodes_key_equal,
.equal = NULL,
.remove_callback = NULL,
};
errno_t
mfs_global_init(void)
{
if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) {
return ENOMEM;
}
return EOK;
}
static errno_t mfs_read_sb(service_id_t service_id, struct mfs_sb_info **rsbi)
{
struct mfs_superblock *sb = NULL;
struct mfs3_superblock *sb3 = NULL;
struct mfs_sb_info *sbi;
size_t bsize;
bool native, longnames;
mfs_version_t version;
uint16_t magic;
errno_t rc;
sbi = malloc(sizeof(*sbi));
if (!sbi) {
rc = ENOMEM;
goto out_error;
}
sb = malloc(MFS_SUPERBLOCK_SIZE);
if (!sb) {
rc = ENOMEM;
goto out_error;
}
rc = block_get_bsize(service_id, &bsize);
if (rc != EOK) {
rc = EIO;
goto out_error;
}
if (bsize != 512) {
rc = ENOTSUP;
goto out_error;
}
rc = block_read_direct(service_id, MFS_SUPERBLOCK << 1, 2, sb);
if (rc != EOK)
goto out_error;
sb3 = (struct mfs3_superblock *) sb;
if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
magic = sb->s_magic;
} else if (check_magic_number(sb3->s_magic, &native,
&version, &longnames)) {
magic = sb3->s_magic;
} else {
mfsdebug("magic number not recognized\n");
rc = ENOTSUP;
goto out_error;
}
mfsdebug("magic number recognized = %04x\n", magic);
sbi->fs_version = version;
sbi->long_names = longnames;
sbi->native = native;
sbi->magic = magic;
sbi->isearch = 0;
sbi->zsearch = 0;
sbi->nfree_zones_valid = false;
sbi->nfree_zones = 0;
if (version == MFS_VERSION_V3) {
sbi->ninodes = conv32(native, sb3->s_ninodes);
sbi->ibmap_blocks = conv16(native, sb3->s_ibmap_blocks);
sbi->zbmap_blocks = conv16(native, sb3->s_zbmap_blocks);
sbi->firstdatazone = conv16(native, sb3->s_first_data_zone);
sbi->log2_zone_size = conv16(native, sb3->s_log2_zone_size);
sbi->max_file_size = conv32(native, sb3->s_max_file_size);
sbi->nzones = conv32(native, sb3->s_nzones);
sbi->block_size = conv16(native, sb3->s_block_size);
sbi->ino_per_block = V3_INODES_PER_BLOCK(sbi->block_size);
sbi->dirsize = MFS3_DIRSIZE;
sbi->max_name_len = MFS3_MAX_NAME_LEN;
} else {
sbi->ninodes = conv16(native, sb->s_ninodes);
sbi->ibmap_blocks = conv16(native, sb->s_ibmap_blocks);
sbi->zbmap_blocks = conv16(native, sb->s_zbmap_blocks);
sbi->firstdatazone = conv16(native, sb->s_first_data_zone);
sbi->log2_zone_size = conv16(native, sb->s_log2_zone_size);
sbi->max_file_size = conv32(native, sb->s_max_file_size);
sbi->block_size = MFS_BLOCKSIZE;
if (version == MFS_VERSION_V2) {
sbi->nzones = conv32(native, sb->s_nzones2);
sbi->ino_per_block = V2_INODES_PER_BLOCK;
} else {
sbi->nzones = conv16(native, sb->s_nzones);
sbi->ino_per_block = V1_INODES_PER_BLOCK;
}
sbi->dirsize = longnames ? MFSL_DIRSIZE : MFS_DIRSIZE;
sbi->max_name_len = longnames ? MFS_L_MAX_NAME_LEN :
MFS_MAX_NAME_LEN;
}
if (sbi->log2_zone_size != 0) {
rc = ENOTSUP;
goto out_error;
}
sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
if ((rc = mfs_check_sanity(sbi)) != EOK) {
fprintf(stderr, "Filesystem corrupted, invalid superblock");
goto out_error;
}
mfsdebug("read superblock successful\n");
free(sb);
*rsbi = sbi;
return EOK;
out_error:
if (sb)
free(sb);
if (sbi)
free(sbi);
return rc;
}
static errno_t mfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
{
struct mfs_sb_info *sbi = NULL;
errno_t rc;
rc = block_init(service_id);
if (rc != EOK)
return rc;
rc = mfs_read_sb(service_id, &sbi);
block_fini(service_id);
return rc;
}
static errno_t
mfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
aoff64_t *size)
{
enum cache_mode cmode;
struct mfs_sb_info *sbi = NULL;
struct mfs_instance *instance = NULL;
errno_t rc;
if (str_cmp(opts, "wtcache") == 0)
cmode = CACHE_MODE_WT;
else
cmode = CACHE_MODE_WB;
rc = block_init(service_id);
if (rc != EOK)
return rc;
instance = malloc(sizeof(*instance));
if (!instance) {
rc = ENOMEM;
goto out_error;
}
rc = mfs_read_sb(service_id, &sbi);
if (rc != EOK)
goto out_error;
rc = block_cache_init(service_id, sbi->block_size, 0, cmode);
if (rc != EOK) {
mfsdebug("block cache initialization failed\n");
rc = EINVAL;
goto out_error;
}
instance->service_id = service_id;
instance->sbi = sbi;
instance->open_nodes_cnt = 0;
rc = fs_instance_create(service_id, instance);
if (rc != EOK) {
block_cache_fini(service_id);
mfsdebug("fs instance creation failed\n");
goto out_error;
}
mfsdebug("mount successful\n");
fs_node_t *fn;
mfs_node_get(&fn, service_id, MFS_ROOT_INO);
struct mfs_node *mroot = fn->data;
*index = mroot->ino_i->index;
*size = mroot->ino_i->i_size;
return mfs_node_put(fn);
out_error:
block_fini(service_id);
if (sbi)
free(sbi);
if (instance)
free(instance);
return rc;
}
static errno_t
mfs_unmounted(service_id_t service_id)
{
struct mfs_instance *inst;
mfsdebug("%s()\n", __FUNCTION__);
errno_t r = mfs_instance_get(service_id, &inst);
if (r != EOK)
return r;
if (inst->open_nodes_cnt != 0)
return EBUSY;
(void) block_cache_fini(service_id);
block_fini(service_id);
(void) fs_instance_destroy(service_id);
free(inst->sbi);
free(inst);
return EOK;
}
service_id_t
mfs_service_get(fs_node_t *fsnode)
{
return 0;
}
static errno_t
mfs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
{
errno_t r;
struct mfs_instance *inst;
struct mfs_node *mnode;
fs_node_t *fsnode;
uint32_t inum;
r = mfs_instance_get(service_id, &inst);
if (r != EOK)
return r;
r = mfs_alloc_inode(inst, &inum);
if (r != EOK)
return r;
struct mfs_ino_info *ino_i;
ino_i = malloc(sizeof(*ino_i));
if (!ino_i) {
r = ENOMEM;
goto out_err;
}
mnode = malloc(sizeof(*mnode));
if (!mnode) {
r = ENOMEM;
goto out_err_1;
}
fsnode = malloc(sizeof(fs_node_t));
if (!fsnode) {
r = ENOMEM;
goto out_err_2;
}
if (flags & L_DIRECTORY) {
ino_i->i_mode = S_IFDIR;
ino_i->i_nlinks = 1;
} else {
ino_i->i_mode = S_IFREG;
ino_i->i_nlinks = 0;
}
ino_i->i_uid = 0;
ino_i->i_gid = 0;
ino_i->i_size = 0;
ino_i->i_atime = 0;
ino_i->i_mtime = 0;
ino_i->i_ctime = 0;
memset(ino_i->i_dzone, 0, sizeof(uint32_t) * V2_NR_DIRECT_ZONES);
memset(ino_i->i_izone, 0, sizeof(uint32_t) * V2_NR_INDIRECT_ZONES);
mfsdebug("new node idx = %d\n", (int) inum);
ino_i->index = inum;
ino_i->dirty = true;
mnode->ino_i = ino_i;
mnode->instance = inst;
mnode->refcnt = 1;
fibril_mutex_lock(&open_nodes_lock);
hash_table_insert(&open_nodes, &mnode->link);
fibril_mutex_unlock(&open_nodes_lock);
inst->open_nodes_cnt++;
mnode->ino_i->dirty = true;
fs_node_initialize(fsnode);
fsnode->data = mnode;
mnode->fsnode = fsnode;
*rfn = fsnode;
return EOK;
out_err_2:
free(mnode);
out_err_1:
free(ino_i);
out_err:
mfs_free_inode(inst, inum);
return r;
}
static errno_t
mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
{
struct mfs_node *mnode = pfn->data;
struct mfs_ino_info *ino_i = mnode->ino_i;
struct mfs_dentry_info d_info;
errno_t r;
if (!S_ISDIR(ino_i->i_mode))
return ENOTDIR;
struct mfs_sb_info *sbi = mnode->instance->sbi;
const size_t comp_size = str_size(component);
unsigned i;
for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
r = mfs_read_dentry(mnode, &d_info, i);
if (r != EOK)
return r;
if (!d_info.d_inum) {
continue;
}
const size_t dentry_name_size = str_size(d_info.d_name);
if (comp_size == dentry_name_size &&
memcmp(component, d_info.d_name, dentry_name_size) == 0) {
mfs_node_core_get(rfn, mnode->instance,
d_info.d_inum);
goto found;
}
}
*rfn = NULL;
found:
return EOK;
}
static aoff64_t
mfs_size_get(fs_node_t *node)
{
const struct mfs_node *mnode = node->data;
return mnode->ino_i->i_size;
}
static errno_t
mfs_node_get(fs_node_t **rfn, service_id_t service_id,
fs_index_t index)
{
errno_t rc;
struct mfs_instance *instance;
rc = mfs_instance_get(service_id, &instance);
if (rc != EOK)
return rc;
return mfs_node_core_get(rfn, instance, index);
}
static errno_t
mfs_node_put(fs_node_t *fsnode)
{
errno_t rc = EOK;
struct mfs_node *mnode = fsnode->data;
fibril_mutex_lock(&open_nodes_lock);
assert(mnode->refcnt > 0);
mnode->refcnt--;
if (mnode->refcnt == 0) {
hash_table_remove_item(&open_nodes, &mnode->link);
assert(mnode->instance->open_nodes_cnt > 0);
mnode->instance->open_nodes_cnt--;
rc = mfs_put_inode(mnode);
free(mnode->ino_i);
free(mnode);
free(fsnode);
}
fibril_mutex_unlock(&open_nodes_lock);
return rc;
}
static errno_t
mfs_node_open(fs_node_t *fsnode)
{
return EOK;
}
static fs_index_t
mfs_index_get(fs_node_t *fsnode)
{
struct mfs_node *mnode = fsnode->data;
return mnode->ino_i->index;
}
static unsigned
mfs_lnkcnt_get(fs_node_t *fsnode)
{
struct mfs_node *mnode = fsnode->data;
mfsdebug("%s() %d\n", __FUNCTION__, mnode->ino_i->i_nlinks);
if (S_ISDIR(mnode->ino_i->i_mode)) {
if (mnode->ino_i->i_nlinks > 1)
return 1;
else
return 0;
} else
return mnode->ino_i->i_nlinks;
}
static errno_t
mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
fs_index_t index)
{
fs_node_t *node = NULL;
struct mfs_node *mnode = NULL;
errno_t rc;
fibril_mutex_lock(&open_nodes_lock);
node_key_t key = {
.service_id = inst->service_id,
.index = index
};
ht_link_t *already_open = hash_table_find(&open_nodes, &key);
if (already_open) {
mnode = hash_table_get_inst(already_open, struct mfs_node, link);
*rfn = mnode->fsnode;
mnode->refcnt++;
fibril_mutex_unlock(&open_nodes_lock);
return EOK;
}
node = malloc(sizeof(fs_node_t));
if (!node) {
rc = ENOMEM;
goto out_err;
}
fs_node_initialize(node);
mnode = malloc(sizeof(*mnode));
if (!mnode) {
rc = ENOMEM;
goto out_err;
}
struct mfs_ino_info *ino_i;
rc = mfs_get_inode(inst, &ino_i, index);
if (rc != EOK)
goto out_err;
ino_i->index = index;
mnode->ino_i = ino_i;
mnode->refcnt = 1;
mnode->instance = inst;
node->data = mnode;
mnode->fsnode = node;
*rfn = node;
hash_table_insert(&open_nodes, &mnode->link);
inst->open_nodes_cnt++;
fibril_mutex_unlock(&open_nodes_lock);
return EOK;
out_err:
if (node)
free(node);
if (mnode)
free(mnode);
fibril_mutex_unlock(&open_nodes_lock);
return rc;
}
static bool
mfs_is_directory(fs_node_t *fsnode)
{
const struct mfs_node *node = fsnode->data;
return S_ISDIR(node->ino_i->i_mode);
}
static bool
mfs_is_file(fs_node_t *fsnode)
{
struct mfs_node *node = fsnode->data;
return S_ISREG(node->ino_i->i_mode);
}
static errno_t
mfs_root_get(fs_node_t **rfn, service_id_t service_id)
{
errno_t rc = mfs_node_get(rfn, service_id, MFS_ROOT_INO);
return rc;
}
static errno_t
mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
{
struct mfs_node *parent = pfn->data;
struct mfs_node *child = cfn->data;
struct mfs_sb_info *sbi = parent->instance->sbi;
bool destroy_dentry = false;
if (str_size(name) > sbi->max_name_len)
return ENAMETOOLONG;
errno_t r = mfs_insert_dentry(parent, name, child->ino_i->index);
if (r != EOK)
return r;
if (S_ISDIR(child->ino_i->i_mode)) {
if (child->ino_i->i_nlinks != 1) {
destroy_dentry = true;
r = EMLINK;
goto exit;
}
r = mfs_insert_dentry(child, ".", child->ino_i->index);
if (r != EOK) {
destroy_dentry = true;
goto exit;
}
r = mfs_insert_dentry(child, "..", parent->ino_i->index);
if (r != EOK) {
mfs_remove_dentry(child, ".");
destroy_dentry = true;
goto exit;
}
parent->ino_i->i_nlinks++;
parent->ino_i->dirty = true;
}
exit:
if (destroy_dentry) {
errno_t r2 = mfs_remove_dentry(parent, name);
if (r2 != EOK)
r = r2;
} else {
child->ino_i->i_nlinks++;
child->ino_i->dirty = true;
}
return r;
}
static errno_t
mfs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
{
struct mfs_node *parent = pfn->data;
struct mfs_node *child = cfn->data;
bool has_children;
errno_t r;
if (!parent)
return EBUSY;
r = mfs_has_children(&has_children, cfn);
if (r != EOK)
return r;
if (has_children)
return ENOTEMPTY;
r = mfs_remove_dentry(parent, name);
if (r != EOK)
return r;
struct mfs_ino_info *chino = child->ino_i;
assert(chino->i_nlinks >= 1);
chino->i_nlinks--;
mfsdebug("Links: %d\n", chino->i_nlinks);
if (chino->i_nlinks <= 1 && S_ISDIR(chino->i_mode)) {
parent->ino_i->i_nlinks--;
parent->ino_i->dirty = true;
}
chino->dirty = true;
return r;
}
static errno_t
mfs_has_children(bool *has_children, fs_node_t *fsnode)
{
struct mfs_node *mnode = fsnode->data;
struct mfs_sb_info *sbi = mnode->instance->sbi;
errno_t r;
*has_children = false;
if (!S_ISDIR(mnode->ino_i->i_mode))
goto out;
struct mfs_dentry_info d_info;
unsigned i;
for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
r = mfs_read_dentry(mnode, &d_info, i);
if (r != EOK)
return r;
if (d_info.d_inum) {
*has_children = true;
break;
}
}
out:
return EOK;
}
static errno_t
mfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
size_t *rbytes)
{
errno_t rc;
errno_t tmp;
fs_node_t *fn = NULL;
rc = mfs_node_get(&fn, service_id, index);
if (rc != EOK)
return rc;
if (!fn)
return ENOENT;
struct mfs_node *mnode;
struct mfs_ino_info *ino_i;
size_t len, bytes = 0;
ipc_call_t call;
mnode = fn->data;
ino_i = mnode->ino_i;
if (!async_data_read_receive(&call, &len)) {
rc = EINVAL;
goto out_error;
}
if (S_ISDIR(ino_i->i_mode)) {
aoff64_t spos = pos;
struct mfs_dentry_info d_info;
struct mfs_sb_info *sbi = mnode->instance->sbi;
if (pos < 2) {
pos = 2;
}
for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
rc = mfs_read_dentry(mnode, &d_info, pos);
if (rc != EOK)
goto out_error;
if (d_info.d_inum) {
goto found;
}
}
rc = mfs_node_put(fn);
async_answer_0(&call, rc != EOK ? rc : ENOENT);
return rc;
found:
async_data_read_finalize(&call, d_info.d_name,
str_size(d_info.d_name) + 1);
bytes = ((pos - spos) + 1);
} else {
struct mfs_sb_info *sbi = mnode->instance->sbi;
if (pos >= (size_t) ino_i->i_size) {
bytes = 0;
(void) async_data_read_finalize(&call, NULL, 0);
goto out_success;
}
bytes = min(len, sbi->block_size - pos % sbi->block_size);
bytes = min(bytes, ino_i->i_size - pos);
uint32_t zone;
block_t *b;
rc = mfs_read_map(&zone, mnode, pos);
if (rc != EOK)
goto out_error;
if (zone == 0) {
uint8_t *buf = malloc(sbi->block_size);
if (!buf) {
rc = ENOMEM;
goto out_error;
}
memset(buf, 0, sizeof(sbi->block_size));
async_data_read_finalize(&call,
buf + pos % sbi->block_size, bytes);
free(buf);
goto out_success;
}
rc = block_get(&b, service_id, zone, BLOCK_FLAGS_NONE);
if (rc != EOK)
goto out_error;
async_data_read_finalize(&call, b->data +
pos % sbi->block_size, bytes);
rc = block_put(b);
if (rc != EOK) {
mfs_node_put(fn);
return rc;
}
}
out_success:
rc = mfs_node_put(fn);
*rbytes = bytes;
return rc;
out_error:
tmp = mfs_node_put(fn);
async_answer_0(&call, tmp != EOK ? tmp : rc);
return tmp != EOK ? tmp : rc;
}
static errno_t
mfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
size_t *wbytes, aoff64_t *nsize)
{
fs_node_t *fn;
errno_t r;
int flags = BLOCK_FLAGS_NONE;
r = mfs_node_get(&fn, service_id, index);
if (r != EOK)
return r;
if (!fn)
return ENOENT;
ipc_call_t call;
size_t len;
if (!async_data_write_receive(&call, &len)) {
r = EINVAL;
goto out_err;
}
struct mfs_node *mnode = fn->data;
struct mfs_sb_info *sbi = mnode->instance->sbi;
struct mfs_ino_info *ino_i = mnode->ino_i;
const size_t bs = sbi->block_size;
size_t bytes = min(len, bs - (pos % bs));
uint32_t block;
if (bytes == bs)
flags = BLOCK_FLAGS_NOREAD;
r = mfs_read_map(&block, mnode, pos);
if (r != EOK)
goto out_err;
if (block == 0) {
uint32_t dummy;
r = mfs_alloc_zone(mnode->instance, &block);
if (r != EOK)
goto out_err;
r = mfs_write_map(mnode, pos, block, &dummy);
if (r != EOK) {
mfs_free_zone(mnode->instance, block);
goto out_err;
}
flags = BLOCK_FLAGS_NOREAD;
}
block_t *b;
r = block_get(&b, service_id, block, flags);
if (r != EOK)
goto out_err;
if (flags == BLOCK_FLAGS_NOREAD)
memset(b->data, 0, sbi->block_size);
async_data_write_finalize(&call, b->data + (pos % bs), bytes);
b->dirty = true;
r = block_put(b);
if (r != EOK) {
mfs_node_put(fn);
return r;
}
if (pos + bytes > ino_i->i_size) {
ino_i->i_size = pos + bytes;
ino_i->dirty = true;
}
r = mfs_node_put(fn);
*nsize = ino_i->i_size;
*wbytes = bytes;
return r;
out_err:
mfs_node_put(fn);
async_answer_0(&call, r);
return r;
}
static errno_t
mfs_destroy(service_id_t service_id, fs_index_t index)
{
fs_node_t *fn = NULL;
errno_t r;
r = mfs_node_get(&fn, service_id, index);
if (r != EOK)
return r;
if (!fn)
return ENOENT;
return mfs_destroy_node(fn);
}
static errno_t
mfs_destroy_node(fs_node_t *fn)
{
struct mfs_node *mnode = fn->data;
bool has_children;
errno_t r;
mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
r = mfs_has_children(&has_children, fn);
if (r != EOK)
goto out;
assert(!has_children);
r = mfs_inode_shrink(mnode, mnode->ino_i->i_size);
if (r != EOK)
goto out;
r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
out:
mfs_node_put(fn);
return r;
}
static errno_t
mfs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
{
fs_node_t *fn;
errno_t r;
r = mfs_node_get(&fn, service_id, index);
if (r != EOK)
return r;
if (!fn)
return r;
struct mfs_node *mnode = fn->data;
struct mfs_ino_info *ino_i = mnode->ino_i;
if (ino_i->i_size == size)
r = EOK;
else
r = mfs_inode_shrink(mnode, ino_i->i_size - size);
mfs_node_put(fn);
return r;
}
static errno_t
mfs_instance_get(service_id_t service_id, struct mfs_instance **instance)
{
void *data;
errno_t rc;
rc = fs_instance_get(service_id, &data);
if (rc == EOK)
*instance = (struct mfs_instance *) data;
else {
mfsdebug("instance not found\n");
}
return rc;
}
static bool
check_magic_number(uint16_t magic, bool *native,
mfs_version_t *version, bool *longfilenames)
{
bool rc = true;
*longfilenames = false;
if (magic == MFS_MAGIC_V1 || magic == MFS_MAGIC_V1R) {
*native = magic == MFS_MAGIC_V1;
*version = MFS_VERSION_V1;
} else if (magic == MFS_MAGIC_V1L || magic == MFS_MAGIC_V1LR) {
*native = magic == MFS_MAGIC_V1L;
*version = MFS_VERSION_V1;
*longfilenames = true;
} else if (magic == MFS_MAGIC_V2 || magic == MFS_MAGIC_V2R) {
*native = magic == MFS_MAGIC_V2;
*version = MFS_VERSION_V2;
} else if (magic == MFS_MAGIC_V2L || magic == MFS_MAGIC_V2LR) {
*native = magic == MFS_MAGIC_V2L;
*version = MFS_VERSION_V2;
*longfilenames = true;
} else if (magic == MFS_MAGIC_V3 || magic == MFS_MAGIC_V3R) {
*native = magic == MFS_MAGIC_V3;
*version = MFS_VERSION_V3;
} else
rc = false;
return rc;
}
static errno_t
mfs_check_sanity(struct mfs_sb_info *sbi)
{
if (!is_power_of_two(sbi->block_size) ||
sbi->block_size < MFS_MIN_BLOCKSIZE ||
sbi->block_size > MFS_MAX_BLOCKSIZE)
return ENOTSUP;
else if (sbi->ibmap_blocks == 0 || sbi->zbmap_blocks == 0)
return ENOTSUP;
else if (sbi->ninodes == 0 || sbi->nzones == 0)
return ENOTSUP;
else if (sbi->firstdatazone == 0)
return ENOTSUP;
return EOK;
}
static errno_t
mfs_close(service_id_t service_id, fs_index_t index)
{
return 0;
}
static errno_t
mfs_sync(service_id_t service_id, fs_index_t index)
{
fs_node_t *fn = NULL;
errno_t rc = mfs_node_get(&fn, service_id, index);
if (rc != EOK)
return rc;
if (!fn)
return ENOENT;
struct mfs_node *mnode = fn->data;
mnode->ino_i->dirty = true;
return mfs_node_put(fn);
}
static bool
is_power_of_two(uint32_t n)
{
if (n == 0)
return false;
return (n & (n - 1)) == 0;
}
static errno_t
mfs_size_block(service_id_t service_id, uint32_t *size)
{
struct mfs_instance *inst;
errno_t rc;
rc = mfs_instance_get(service_id, &inst);
if (rc != EOK)
return rc;
if (NULL == inst)
return ENOENT;
*size = inst->sbi->block_size;
return EOK;
}
static errno_t
mfs_total_block_count(service_id_t service_id, uint64_t *count)
{
struct mfs_instance *inst;
errno_t rc;
rc = mfs_instance_get(service_id, &inst);
if (rc != EOK)
return rc;
if (NULL == inst)
return ENOENT;
*count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
return EOK;
}
static errno_t
mfs_free_block_count(service_id_t service_id, uint64_t *count)
{
uint32_t block_free;
struct mfs_instance *inst;
errno_t rc = mfs_instance_get(service_id, &inst);
if (rc != EOK)
return rc;
struct mfs_sb_info *sbi = inst->sbi;
if (!sbi->nfree_zones_valid) {
rc = mfs_count_free_zones(inst, &block_free);
if (rc != EOK)
return rc;
sbi->nfree_zones = block_free;
sbi->nfree_zones_valid = true;
}
*count = sbi->nfree_zones;
return rc;
}
vfs_out_ops_t mfs_ops = {
.fsprobe = mfs_fsprobe,
.mounted = mfs_mounted,
.unmounted = mfs_unmounted,
.read = mfs_read,
.write = mfs_write,
.truncate = mfs_truncate,
.close = mfs_close,
.destroy = mfs_destroy,
.sync = mfs_sync,
};
HelenOS homepage, sources at GitHub