Index: uspace/srv/fs/minixfs/Makefile
===================================================================
--- uspace/srv/fs/minixfs/Makefile	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/Makefile	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 Jakub Jermar
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBBLOCK_PREFIX)/libblock.a $(LIBFS_PREFIX)/libfs.a
+EXTRA_CFLAGS += -I$(LIBBLOCK_PREFIX) -I$(LIBFS_PREFIX) -I$(LIBMINIX_PREFIX)
+BINARY = mfs
+
+SOURCES = \
+	mfs.c \
+	mfs_ops.c \
+	mfs_inode.c \
+	mfs_rw.c \
+	mfs_dentry.c \
+	mfs_balloc.c \
+	mfs_utils.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/minixfs/mfs.c
===================================================================
--- uspace/srv/fs/minixfs/mfs.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2006 Martin Decky
+ * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+/**
+ * @file	mfs.c
+ * @brief	Minix file system driver for HelenOS.
+ */
+
+#define _MAIN
+
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <errno.h>
+#include <unistd.h>
+#include <task.h>
+#include <stdio.h>
+#include <libfs.h>
+#include "mfs.h"
+
+vfs_info_t mfs_vfs_info = {
+	.name = NAME,
+	.concurrent_read_write = false,
+	.write_retains_size = false,
+};
+
+
+/**
+ * This connection fibril processes VFS requests from VFS.
+ *
+ * In order to support simultaneous VFS requests, our design is as follows.
+ * The connection fibril accepts VFS requests from VFS. If there is only one
+ * instance of the fibril, VFS will need to serialize all VFS requests it sends
+ * to MinixFS. To overcome this bottleneck, VFS can send MinixFS the IPC_M_CONNECT_ME_TO
+ * call. In that case, a new connection fibril will be created, which in turn
+ * will accept the call. Thus, a new phone will be opened for VFS.
+ *
+ * There are few issues with this arrangement. First, VFS can run out of
+ * available phones. In that case, VFS can close some other phones or use one
+ * phone for more serialized requests. Similarily, MinixFS can refuse to duplicate
+ * the connection. VFS should then just make use of already existing phones and
+ * route its requests through them. To avoid paying the fibril creation price
+ * upon each request, MinixFS might want to keep the connections open after the
+ * request has been completed.
+ */
+
+static void mfs_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	if (iid) {
+		/*
+		 * This only happens for connections opened by
+		 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
+		 * created by IPC_M_CONNECT_TO_ME.
+		 */
+		async_answer_0(iid, EOK);
+	}
+
+	while (1) {
+		ipc_callid_t callid;
+		ipc_call_t call;
+
+		callid = async_get_call(&call);
+		int method = IPC_GET_IMETHOD(call);
+
+		/*mfsdebug(NAME "method = %d\n", method);*/
+		switch  (method) {
+		case VFS_OUT_MOUNTED:
+			mfs_mounted(callid, &call);
+			break;
+		case VFS_OUT_MOUNT:
+			mfs_mount(callid, &call);
+			break;
+		case VFS_OUT_STAT:
+			mfs_stat(callid, &call);
+			break;
+		case VFS_OUT_LOOKUP:
+			mfs_lookup(callid, &call);
+			break;
+		case VFS_OUT_READ:
+			mfs_read(callid, &call);
+			break;
+		case VFS_OUT_OPEN_NODE:
+			mfs_open_node(callid, &call);
+			break;
+		case VFS_OUT_CLOSE:
+			mfs_close(callid, &call);
+			break;
+		case VFS_OUT_WRITE:
+			mfs_write(callid, &call);
+			break;
+		case VFS_OUT_TRUNCATE:
+			mfs_truncate(callid, &call);
+			break;
+		case VFS_OUT_DESTROY:
+			mfs_destroy(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNTED:
+			mfs_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			mfs_unmount(callid, &call);
+			break;
+		case VFS_OUT_SYNC:
+			mfs_sync(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+}
+
+
+int main(int argc, char **argv)
+{
+	int rc;
+
+	printf(NAME ": HelenOS Minix file system server\n");
+
+	async_sess_t *vfs_sess = service_connect_blocking(EXCHANGE_SERIALIZE,
+				SERVICE_VFS, 0, 0);
+
+	if (!vfs_sess) {
+		printf(NAME ": failed to connect to VFS\n");
+		return -1;
+	}
+
+	rc = mfs_global_init();
+	if (rc != EOK) {
+		printf(NAME ": Failed global initialization\n");
+		goto err;
+	}
+
+	rc = fs_register(vfs_sess, &mfs_reg, &mfs_vfs_info, mfs_connection);
+	if (rc != EOK)
+		goto err;
+
+	printf(NAME ": Accepting connections\n");
+	task_retval(0);
+	async_manager();
+	/* not reached */
+	return 0;
+
+err:
+	printf(NAME ": Failed to register file system (%d)\n", rc);
+	return rc;
+}
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs.h
===================================================================
--- uspace/srv/fs/minixfs/mfs.h	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs.h	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#ifndef _MFS_H_
+#define _MFS_H_
+
+#include <minix.h>
+#include <macros.h>
+#include <libblock.h>
+#include <libfs.h>
+#include <adt/list.h>
+#include <malloc.h>
+#include <mem.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include "../../vfs/vfs.h"
+
+#define NAME		"mfs"
+
+//#define DEBUG_MODE
+
+#define min(a, b)	((a) < (b) ? (a) : (b))
+
+#ifdef DEBUG_MODE
+#define mfsdebug(...)	printf(__VA_ARGS__)
+#else
+#define mfsdebug(...)
+#endif
+
+#ifdef _MAIN
+#define GLOBAL
+#else
+#define GLOBAL extern
+#endif
+
+#define on_error(r, inst) do {	\
+				if (r != EOK) inst;	\
+			}while(0)
+
+GLOBAL fs_reg_t mfs_reg;
+
+typedef uint32_t bitchunk_t;
+
+typedef enum {
+	BMAP_ZONE,
+	BMAP_INODE
+} bmap_id_t;
+
+typedef enum {
+	MFS_VERSION_V1 = 1,
+	MFS_VERSION_V2,
+	MFS_VERSION_V3
+} mfs_version_t;
+
+/*Generic MinixFS superblock*/
+struct mfs_sb_info {
+	uint32_t ninodes;
+	uint32_t nzones;
+	unsigned long ibmap_blocks;
+	unsigned long zbmap_blocks;
+	unsigned long firstdatazone;
+	int log2_zone_size;
+	int block_size;
+	uint32_t max_file_size;
+	uint16_t magic;
+	uint16_t state;
+
+	/*The following fields do not exist on disk but only in memory*/
+	unsigned long itable_size;
+	mfs_version_t fs_version;
+	int ino_per_block;
+	size_t dirsize;
+	int itable_off;
+	unsigned max_name_len;
+	bool long_names;
+	bool native;
+	unsigned isearch;
+	unsigned zsearch;
+};
+
+/*Generic MinixFS inode*/
+struct mfs_ino_info {
+	uint16_t	i_mode;
+	uint16_t	i_nlinks;
+	int16_t		i_uid;
+	uint16_t	i_gid;
+	size_t		i_size;
+	int32_t		i_atime;
+	int32_t		i_mtime;
+	int32_t		i_ctime;
+	/*Block numbers for direct zones*/
+	uint32_t	i_dzone[V2_NR_DIRECT_ZONES];
+	/*Block numbers for indirect zones*/
+	uint32_t	i_izone[V2_NR_INDIRECT_ZONES];
+
+	/*The following fields do not exist on disk but only in memory*/
+	bool dirty;
+	fs_index_t index;
+};
+
+/*Generic MFS directory entry*/
+struct mfs_dentry_info {
+	uint32_t d_inum;
+	char d_name[MFS3_MAX_NAME_LEN + 1];
+
+	/*The following fields do not exist on disk but only in memory*/
+
+	/*Index of the dentry in the list*/
+	unsigned index;
+	/*Pointer to the node at witch the dentry belongs*/
+	struct mfs_node *node;
+};
+
+struct mfs_instance {
+	link_t link;
+	devmap_handle_t handle;
+	struct mfs_sb_info *sbi;
+	unsigned open_nodes_cnt;
+};
+
+/*MinixFS node in core*/
+struct mfs_node {
+	struct mfs_ino_info *ino_i;
+	struct mfs_instance *instance;
+	unsigned refcnt;
+	fs_node_t *fsnode;
+	link_t link;
+};
+
+/*mfs_ops.c*/
+extern void mfs_mounted(ipc_callid_t rid, ipc_call_t *request);
+extern void mfs_mount(ipc_callid_t rid, ipc_call_t *request);
+extern void mfs_lookup(ipc_callid_t rid, ipc_call_t *request);
+extern int mfs_instance_get(devmap_handle_t handle,
+			    struct mfs_instance **instance);
+
+extern void mfs_stat(ipc_callid_t rid, ipc_call_t *request);
+extern void mfs_close(ipc_callid_t rid, ipc_call_t *request);
+extern void mfs_open_node(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_read(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_write(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_truncate(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_destroy(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_unmounted(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_unmount(ipc_callid_t rid, ipc_call_t *request);
+
+extern void
+mfs_sync(ipc_callid_t rid, ipc_call_t *request);
+
+extern int
+mfs_global_init(void);
+
+/*mfs_inode.c*/
+extern int
+get_inode(struct mfs_instance *inst, struct mfs_ino_info **ino_i,
+	  fs_index_t index);
+
+extern int
+put_inode(struct mfs_node *mnode);
+
+extern int
+inode_shrink(struct mfs_node *mnode, size_t size_shrink);
+
+/*mfs_rw.c*/
+extern int
+read_map(uint32_t *b, const struct mfs_node *mnode, const uint32_t pos);
+
+extern int
+write_map(struct mfs_node *mnode, uint32_t pos, uint32_t new_zone,
+	  uint32_t *old_zone);
+
+extern int
+prune_ind_zones(struct mfs_node *mnode, size_t new_size);
+
+/*mfs_dentry.c*/
+extern int
+read_dentry(struct mfs_node *mnode,
+		     struct mfs_dentry_info *d_info, unsigned index);
+
+extern int
+write_dentry(struct mfs_dentry_info *d_info);
+
+extern int
+remove_dentry(struct mfs_node *mnode, const char *d_name);
+
+extern int
+insert_dentry(struct mfs_node *mnode, const char *d_name, fs_index_t d_inum);
+
+/*mfs_balloc.c*/
+extern int
+mfs_alloc_inode(struct mfs_instance *inst, uint32_t *inum);
+
+extern int
+mfs_free_inode(struct mfs_instance *inst, uint32_t inum);
+
+extern int
+mfs_alloc_zone(struct mfs_instance *inst, uint32_t *zone);
+
+extern int
+mfs_free_zone(struct mfs_instance *inst, uint32_t zone);
+
+/*mfs_utils.c*/
+extern uint16_t
+conv16(bool native, uint16_t n);
+
+extern uint32_t
+conv32(bool native, uint32_t n);
+
+extern uint64_t
+conv64(bool native, uint64_t n);
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_balloc.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_balloc.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_balloc.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include <stdlib.h>
+#include "mfs.h"
+
+static int
+find_free_bit_and_set(bitchunk_t *b, const int bsize,
+		      const bool native, unsigned start_bit);
+
+static int
+mfs_free_bit(struct mfs_instance *inst, uint32_t idx, bmap_id_t bid);
+
+static int
+mfs_alloc_bit(struct mfs_instance *inst, uint32_t *idx, bmap_id_t bid);
+
+/**Allocate a new inode.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param inum		Pointer to a 32 bit number where the index of
+ * 			the new inode will be saved.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+mfs_alloc_inode(struct mfs_instance *inst, uint32_t *inum)
+{
+	int r = mfs_alloc_bit(inst, inum, BMAP_INODE);
+
+	*inum += 1;
+	return r;
+}
+
+/**Free an inode.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param inum		Number of the inode to free.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+mfs_free_inode(struct mfs_instance *inst, uint32_t inum)
+{
+	return mfs_free_bit(inst, inum - 1, BMAP_INODE);
+}
+
+/**Allocate a new zone.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param zone		Pointer to a 32 bit number where the index
+ * 			of the zone will be saved.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+mfs_alloc_zone(struct mfs_instance *inst, uint32_t *zone)
+{
+	int r = mfs_alloc_bit(inst, zone, BMAP_ZONE);
+
+	*zone += inst->sbi->firstdatazone - 1;
+	return r;
+}
+
+/**Free a zone.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param zone		Index of the zone to free.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+mfs_free_zone(struct mfs_instance *inst, uint32_t zone)
+{
+	zone -= inst->sbi->firstdatazone - 1;
+
+	return mfs_free_bit(inst, zone, BMAP_ZONE);
+}
+
+/**Clear a bit in a bitmap.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param idx		Index of the bit to clear.
+ * @param bid		BMAP_ZONE if operating on the zone's bitmap,
+ * 			BMAP_INODE if operating on the inode's bitmap.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+static int
+mfs_free_bit(struct mfs_instance *inst, uint32_t idx, bmap_id_t bid)
+{
+	struct mfs_sb_info *sbi;
+	int r;
+	unsigned start_block;
+	unsigned *search;
+	block_t *b;
+
+	assert(inst != NULL);
+	sbi = inst->sbi;
+	assert(sbi != NULL);
+
+	if (bid == BMAP_ZONE) {
+		search = &sbi->zsearch;
+		start_block = 2 + sbi->ibmap_blocks;
+		if (idx > sbi->nzones) {
+			printf(NAME ": Error! Trying to free beyond the" \
+			       "bitmap max size\n");
+			return -1;
+		}
+	} else {
+		/*bid == BMAP_INODE*/
+		search = &sbi->isearch;
+		start_block = 2;
+		if (idx > sbi->ninodes) {
+			printf(NAME ": Error! Trying to free beyond the" \
+			       "bitmap max size\n");
+			return -1;
+		}
+	}
+
+	/*Compute the bitmap block*/
+	uint32_t block = idx / (sbi->block_size * 8) + start_block;
+
+	r = block_get(&b, inst->handle, block, BLOCK_FLAGS_NONE);
+	on_error(r, goto out_err);
+
+	/*Compute the bit index in the block*/
+	idx %= (sbi->block_size * 8);
+	bitchunk_t *ptr = b->data;
+	bitchunk_t chunk;
+	const size_t chunk_bits = sizeof(bitchunk_t) * 8;
+
+	chunk = conv32(sbi->native, ptr[idx / chunk_bits]);
+	chunk &= ~(1 << (idx % chunk_bits));
+	ptr[idx / chunk_bits] = conv32(sbi->native, chunk);
+
+	b->dirty = true;
+	r = block_put(b);
+
+	if (*search > idx)
+		*search = idx;
+
+out_err:
+	return r;
+}
+
+/**Search a free bit in a bitmap and mark it as used.
+ *
+ * @param inst		Pointer to the filesystem instance.
+ * @param idx		Pointer of a 32 bit number where the index
+ * 			of the found bit will be stored.
+ * @param bid		BMAP_ZONE if operating on the zone's bitmap,
+ * 			BMAP_INODE if operating on the inode's bitmap.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+static int
+mfs_alloc_bit(struct mfs_instance *inst, uint32_t *idx, bmap_id_t bid)
+{
+	struct mfs_sb_info *sbi;
+	uint32_t limit;
+	unsigned long nblocks;
+	unsigned *search, i, start_block;
+	unsigned bits_per_block;
+	int r, freebit;
+
+	assert(inst != NULL);
+	sbi = inst->sbi;
+	assert(sbi != NULL);
+
+	if (bid == BMAP_ZONE) {
+		search = &sbi->zsearch;
+		start_block = 2 + sbi->ibmap_blocks;
+		nblocks = sbi->zbmap_blocks;
+		limit = sbi->nzones;
+	} else {
+		/*bid == BMAP_INODE*/
+		search = &sbi->isearch;
+		start_block = 2;
+		nblocks = sbi->ibmap_blocks;
+		limit = sbi->ninodes;
+	}
+	bits_per_block = sbi->block_size * 8;
+
+	block_t *b;
+
+retry:
+
+	for (i = *search / bits_per_block; i < nblocks; ++i) {
+		r = block_get(&b, inst->handle, i + start_block,
+			      BLOCK_FLAGS_NONE);
+
+		on_error(r, goto out);
+
+		unsigned tmp = *search % bits_per_block;
+
+		freebit = find_free_bit_and_set(b->data, sbi->block_size,
+						sbi->native, tmp);
+		if (freebit == -1) {
+			/*No free bit in this block*/
+			r = block_put(b);
+			on_error(r, goto out);
+			continue;
+		}
+
+		/*Free bit found in this block, compute the real index*/
+		*idx = freebit + bits_per_block * i;
+		if (*idx > limit) {
+			/*Index is beyond the limit, it is invalid*/
+			r = block_put(b);
+			on_error(r, goto out);
+			break;
+		}
+
+		*search = *idx;
+		b->dirty = true;
+		r = block_put(b);
+		goto out;
+	}
+
+	if (*search > 0) {
+		/*Repeat the search from the first bitmap block*/
+		*search = 0;
+		goto retry;
+	}
+
+	/*Free bit not found, return error*/
+	return ENOSPC;
+
+out:
+	return r;
+}
+
+static int
+find_free_bit_and_set(bitchunk_t *b, const int bsize,
+		      const bool native, unsigned start_bit)
+{
+	int r = -1;
+	unsigned i, j;
+	bitchunk_t chunk;
+	const size_t chunk_bits = sizeof(bitchunk_t) * 8;
+
+	for (i = start_bit / chunk_bits;
+	     i < bsize / sizeof(bitchunk_t); ++i) {
+		if (!(~b[i])) {
+			/*No free bit in this chunk*/
+			continue;
+		}
+
+		chunk = conv32(native, b[i]);
+
+		for (j = 0; j < chunk_bits; ++j) {
+			if (!(chunk & (1 << j))) {
+				r = i * chunk_bits + j;
+				chunk |= 1 << j;
+				b[i] = conv32(native, chunk);
+				goto found;
+			}
+		}
+	}
+found:
+	return r;
+}
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_dentry.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_dentry.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_dentry.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include "mfs.h"
+
+/**Read a directory entry from disk.
+ *
+ * @param mnode		Pointer to the directory node.
+ * @param d_info	Pointer to a directory entry structure where the dentry info
+ *			will be stored.
+ * @param index		index of the dentry in the list.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+read_dentry(struct mfs_node *mnode,
+		     struct mfs_dentry_info *d_info, unsigned index)
+{
+	const struct mfs_instance *inst = mnode->instance;
+	const struct mfs_sb_info *sbi = inst->sbi;
+	const bool longnames = sbi->long_names;
+	uint32_t block;
+	block_t *b;
+
+	int r = read_map(&block, mnode, index * sbi->dirsize);
+	on_error(r, goto out_err);
+
+	if (block == 0) {
+		/*End of the dentries list*/
+		r = EOK;
+		goto out_err;
+	}
+
+	r = block_get(&b, inst->handle, block, BLOCK_FLAGS_NONE);
+	on_error(r, goto out_err);
+
+	unsigned dentries_per_zone = sbi->block_size / sbi->dirsize;
+	unsigned dentry_off = index % dentries_per_zone;
+
+	if (sbi->fs_version == MFS_VERSION_V3) {
+		struct mfs3_dentry *d3;
+
+		d3 = b->data + (dentry_off * MFS3_DIRSIZE);
+
+		d_info->d_inum = conv32(sbi->native, d3->d_inum);
+		memcpy(d_info->d_name, d3->d_name, MFS3_MAX_NAME_LEN);
+	} else {
+		const int namelen = longnames ? MFS_L_MAX_NAME_LEN :
+				    MFS_MAX_NAME_LEN;
+
+		struct mfs_dentry *d;
+
+		d = b->data + dentry_off * (longnames ? MFSL_DIRSIZE :
+					    MFS_DIRSIZE);
+		d_info->d_inum = conv16(sbi->native, d->d_inum);
+		memcpy(d_info->d_name, d->d_name, namelen);
+	}
+
+	r = block_put(b);
+
+	d_info->index = index;
+	d_info->node = mnode;
+
+out_err:
+	return r;
+}
+
+/**Write a directory entry on disk.
+ *
+ * @param d_info	Pointer to the directory entry structure to write on disk.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+write_dentry(struct mfs_dentry_info *d_info)
+{
+	struct mfs_node *mnode = d_info->node;
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	const unsigned d_off_bytes = d_info->index * sbi->dirsize;
+	const unsigned dirs_per_block = sbi->block_size / sbi->dirsize;
+	block_t *b;
+	uint32_t block;
+	int r;
+
+	r = read_map(&block, mnode, d_off_bytes);
+	on_error(r, goto out);
+
+	r = block_get(&b, mnode->instance->handle, block, BLOCK_FLAGS_NONE);
+	on_error(r, goto out);
+
+	const size_t name_len = sbi->max_name_len;
+	uint8_t *ptr = b->data;
+	ptr += (d_info->index % dirs_per_block) * sbi->dirsize;
+
+	if (sbi->fs_version == MFS_VERSION_V3) {
+		struct mfs3_dentry *dentry;
+		dentry = (struct mfs3_dentry *) ptr;
+
+		dentry->d_inum = conv32(sbi->native, d_info->d_inum);
+		memcpy(dentry->d_name, d_info->d_name, name_len);
+	} else {
+		struct mfs_dentry *dentry;
+		dentry = (struct mfs_dentry *) ptr;
+
+		dentry->d_inum = conv16(sbi->native, d_info->d_inum);
+		memcpy(dentry->d_name, d_info->d_name, name_len);
+	}
+
+	b->dirty = true;
+	r = block_put(b);
+
+out:
+	return r;
+}
+
+/**Remove a directory entry from a directory.
+ *
+ * @param mnode		Pointer to the directory node.
+ * @param d_name	Name of the directory entry to delete.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+remove_dentry(struct mfs_node *mnode, const char *d_name)
+{
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	struct mfs_dentry_info d_info;
+	int r;
+
+	const size_t name_len = str_size(d_name);
+
+	if (name_len > sbi->max_name_len)
+		return ENAMETOOLONG;
+
+	/*Search the directory entry to be removed*/
+	unsigned i;
+	for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize ; ++i) {
+		r = read_dentry(mnode, &d_info, i);
+		on_error(r, return r);
+
+		const size_t d_name_len = str_size(d_info.d_name);
+
+		if (name_len == d_name_len &&
+				!bcmp(d_info.d_name, d_name, name_len)) {
+			d_info.d_inum = 0;
+			r = write_dentry(&d_info);
+			return r;
+		}
+	}
+
+	return ENOENT;
+}
+
+/**Insert a new directory entry in a existing directory.
+ *
+ * @param mnode		Pointer to the directory node.
+ * @param d_name	Name of the new directory entry.
+ * @param d_inum	index of the inode that will be pointed by the new dentry.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+insert_dentry(struct mfs_node *mnode, const char *d_name, fs_index_t d_inum)
+{
+	int r;
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	struct mfs_dentry_info d_info;
+	bool empty_dentry_found = false;
+
+	const size_t name_len = str_size(d_name);
+
+	if (name_len > sbi->max_name_len)
+		return ENAMETOOLONG;
+
+	/*Search for an empty dentry*/
+	unsigned i;
+	for (i = 0; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
+		r = read_dentry(mnode, &d_info, i);
+		on_error(r, return r);
+
+		if (d_info.d_inum == 0) {
+			/*This entry is not used*/
+			empty_dentry_found = true;
+			break;
+		}
+	}
+
+	if (!empty_dentry_found) {
+		uint32_t b, pos;
+		pos = mnode->ino_i->i_size;
+		r = read_map(&b, mnode, pos);
+		on_error(r, goto out);
+
+		if (b == 0) {
+			/*Increase the inode size*/
+
+			uint32_t dummy;
+			r = mfs_alloc_zone(mnode->instance, &b);
+			on_error(r, goto out);
+			r = write_map(mnode, pos, b, &dummy);
+			on_error(r, goto out);
+		}
+
+		mnode->ino_i->i_size += sbi->dirsize;
+		mnode->ino_i->dirty = true;
+
+		d_info.index = i;
+		d_info.node = mnode;
+	}
+
+	d_info.d_inum = d_inum;
+	memcpy(d_info.d_name, d_name, name_len);
+	d_info.d_name[name_len] = 0;
+
+	r = write_dentry(&d_info);
+out:
+	return r;
+}
+
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_inode.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_inode.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_inode.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include <stdlib.h>
+#include "mfs.h"
+
+static int
+mfs_write_inode_raw(struct mfs_node *mnode);
+
+static int
+mfs2_write_inode_raw(struct mfs_node *mnode);
+
+static int
+mfs_read_inode_raw(const struct mfs_instance *instance,
+		struct mfs_ino_info **ino_ptr, uint16_t inum);
+
+static int
+mfs2_read_inode_raw(const struct mfs_instance *instance,
+		struct mfs_ino_info **ino_ptr, uint32_t inum);
+
+
+int
+get_inode(struct mfs_instance *inst, struct mfs_ino_info **ino_i,
+	  fs_index_t index)
+{
+	struct mfs_sb_info *sbi = inst->sbi;
+	int r;
+
+	if (sbi->fs_version == MFS_VERSION_V1) {
+		/*Read a MFS V1 inode*/
+		r = mfs_read_inode_raw(inst, ino_i, index);
+	} else {
+		/*Read a MFS V2/V3 inode*/
+		r = mfs2_read_inode_raw(inst, ino_i, index);
+	}
+
+	return r;
+}
+
+static int
+mfs_read_inode_raw(const struct mfs_instance *instance,
+		struct mfs_ino_info **ino_ptr, uint16_t inum) {
+	struct mfs_inode *ino;
+	struct mfs_ino_info *ino_i = NULL;
+	struct mfs_sb_info *sbi;
+	block_t *b;
+	int i, r;
+
+	sbi = instance->sbi;
+	assert(sbi);
+
+	/*inode 0 does not exist*/
+	inum -= 1;
+
+	const int ino_off = inum % sbi->ino_per_block;
+
+	ino_i = malloc(sizeof(*ino_i));
+
+	if (!ino_i) {
+		r = ENOMEM;
+		goto out_err;
+	}
+
+	const int itable_off = sbi->itable_off;
+
+	r = block_get(&b, instance->handle,
+		      itable_off + inum / sbi->ino_per_block,
+		      BLOCK_FLAGS_NONE);
+	on_error(r, goto out_err);
+
+	ino = b->data + ino_off * sizeof(struct mfs_inode);
+
+	ino_i->i_mode = conv16(sbi->native, ino->i_mode);
+	ino_i->i_uid = conv16(sbi->native, ino->i_uid);
+	ino_i->i_size = conv32(sbi->native, ino->i_size);
+	ino_i->i_mtime = conv32(sbi->native, ino->i_mtime);
+	ino_i->i_nlinks = ino->i_nlinks;
+
+	for (i = 0; i < V1_NR_DIRECT_ZONES; ++i)
+		ino_i->i_dzone[i] = conv16(sbi->native, ino->i_dzone[i]);
+
+	for (i = 0; i < V1_NR_INDIRECT_ZONES; ++i)
+		ino_i->i_izone[i] = conv16(sbi->native, ino->i_izone[i]);
+
+	r = block_put(b);
+	ino_i->dirty = false;
+	*ino_ptr = ino_i;
+
+	return r;
+
+out_err:
+	if (ino_i)
+		free(ino_i);
+	return EOK;
+}
+
+static int
+mfs2_read_inode_raw(const struct mfs_instance *instance,
+		struct mfs_ino_info **ino_ptr, uint32_t inum) {
+	struct mfs2_inode *ino;
+	struct mfs_ino_info *ino_i = NULL;
+	struct mfs_sb_info *sbi;
+	block_t *b;
+	int i, r;
+
+	ino_i = malloc(sizeof(*ino_i));
+
+	if (!ino_i) {
+		r = ENOMEM;
+		goto out_err;
+	}
+
+	sbi = instance->sbi;
+	assert(sbi);
+
+	/*inode 0 does not exist*/
+	inum -= 1;
+
+	const int itable_off = sbi->itable_off;
+	const int ino_off = inum % sbi->ino_per_block;
+
+	r = block_get(&b, instance->handle,
+		      itable_off + inum / sbi->ino_per_block,
+		      BLOCK_FLAGS_NONE);
+	on_error(r, goto out_err);
+
+	ino = b->data + ino_off * sizeof(struct mfs2_inode);
+
+	ino_i->i_mode = conv16(sbi->native, ino->i_mode);
+	ino_i->i_nlinks = conv16(sbi->native, ino->i_nlinks);
+	ino_i->i_uid = conv16(sbi->native, ino->i_uid);
+	ino_i->i_gid = conv16(sbi->native, ino->i_gid);
+	ino_i->i_size = conv32(sbi->native, ino->i_size);
+	ino_i->i_atime = conv32(sbi->native, ino->i_atime);
+	ino_i->i_mtime = conv32(sbi->native, ino->i_mtime);
+	ino_i->i_ctime = conv32(sbi->native, ino->i_ctime);
+
+	for (i = 0; i < V2_NR_DIRECT_ZONES; ++i)
+		ino_i->i_dzone[i] = conv32(sbi->native, ino->i_dzone[i]);
+
+	for (i = 0; i < V2_NR_INDIRECT_ZONES; ++i)
+		ino_i->i_izone[i] = conv32(sbi->native, ino->i_izone[i]);
+
+	r = block_put(b);
+	ino_i->dirty = false;
+	*ino_ptr = ino_i;
+
+	return r;
+
+out_err:
+	if (ino_i)
+		free(ino_i);
+	return EOK;
+}
+
+int
+put_inode(struct mfs_node *mnode)
+{
+	int rc = EOK;
+
+	assert(mnode);
+	assert(mnode->ino_i);
+
+	if (!mnode->ino_i->dirty)
+		goto out;
+
+	struct mfs_instance *inst = mnode->instance;
+	assert(inst);
+	struct mfs_sb_info *sbi = inst->sbi;
+	assert(sbi);
+
+	if (sbi->fs_version == MFS_VERSION_V1)
+		rc = mfs_write_inode_raw(mnode);
+	else
+		rc = mfs2_write_inode_raw(mnode);
+
+out:
+	return rc;
+}
+
+static int
+mfs_write_inode_raw(struct mfs_node *mnode)
+{
+	int i, r;
+	block_t *b;
+	struct mfs_ino_info *ino_i = mnode->ino_i;
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+
+	const uint32_t inum = ino_i->index - 1;
+	const int itable_off = sbi->itable_off;
+	const int ino_off = inum % sbi->ino_per_block;
+	const bool native = sbi->native;
+
+	r = block_get(&b, mnode->instance->handle,
+		      itable_off + inum / sbi->ino_per_block,
+		      BLOCK_FLAGS_NONE);
+
+	on_error(r, goto out);
+
+	struct mfs_inode *ino = b->data;
+	ino += ino_off;
+
+	ino->i_mode = conv16(native, ino_i->i_mode);
+	ino->i_uid = conv16(native, ino_i->i_uid);
+	ino->i_gid = ino_i->i_gid;
+	ino->i_nlinks = ino_i->i_nlinks;
+	ino->i_size = conv32(native, ino_i->i_size);
+	ino->i_mtime = conv32(native, ino_i->i_mtime);
+
+	for (i = 0; i < V1_NR_DIRECT_ZONES; ++i)
+		ino->i_dzone[i] = conv16(native, ino_i->i_dzone[i]);
+	for (i = 0; i < V1_NR_INDIRECT_ZONES; ++i)
+		ino->i_izone[i] = conv16(native, ino_i->i_izone[i]);
+
+	b->dirty = true;
+	r = block_put(b);
+
+	ino_i->dirty = false;
+out:
+	return r;
+}
+
+static int
+mfs2_write_inode_raw(struct mfs_node *mnode)
+{
+	struct mfs_ino_info *ino_i = mnode->ino_i;
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	block_t *b;
+	int i, r;
+
+	const uint32_t inum = ino_i->index - 1;
+	const int itable_off = sbi->itable_off;
+	const int ino_off = inum % sbi->ino_per_block;
+	const bool native = sbi->native;
+
+	r = block_get(&b, mnode->instance->handle,
+		      itable_off + inum / sbi->ino_per_block,
+		      BLOCK_FLAGS_NONE);
+
+	on_error(r, goto out);
+
+	struct mfs2_inode *ino2 = b->data;
+	ino2 += ino_off;
+
+	ino2->i_mode = conv16(native, ino_i->i_mode);
+	ino2->i_nlinks = conv16(native, ino_i->i_nlinks);
+	ino2->i_uid = conv16(native, ino_i->i_uid);
+	ino2->i_gid = conv16(native, ino_i->i_gid);
+	ino2->i_size = conv32(native, ino_i->i_size);
+	ino2->i_atime = conv32(native, ino_i->i_atime);
+	ino2->i_mtime = conv32(native, ino_i->i_mtime);
+	ino2->i_ctime = conv32(native, ino_i->i_ctime);
+
+	for (i = 0; i < V2_NR_DIRECT_ZONES; ++i)
+		ino2->i_dzone[i] = conv32(native, ino_i->i_dzone[i]);
+
+	for (i = 0; i < V2_NR_INDIRECT_ZONES; ++i)
+		ino2->i_izone[i] = conv32(native, ino_i->i_izone[i]);
+
+	b->dirty = true;
+	r = block_put(b);
+	ino_i->dirty = false;
+
+out:
+	return r;
+}
+
+int
+inode_shrink(struct mfs_node *mnode, size_t size_shrink)
+{
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	struct mfs_ino_info *ino_i = mnode->ino_i;
+	const size_t bs = sbi->block_size;
+	int r;
+
+	if (size_shrink == 0) {
+		/*File is empty*/
+		return EOK;
+	}
+
+	const size_t old_size = ino_i->i_size;
+	const size_t new_size = ino_i->i_size - size_shrink;
+
+	assert(size_shrink <= old_size);
+
+	ino_i->dirty = true;
+
+	/*Compute the number of zones to free*/
+	unsigned zones_to_free;
+
+	size_t diff = old_size - new_size;
+	zones_to_free = diff / bs;
+
+	if (diff % bs != 0)
+		zones_to_free++;
+
+	uint32_t pos = old_size - 1;
+	unsigned i;
+	for (i = 0; i < zones_to_free; ++i, pos -= bs) {
+		uint32_t old_zone;
+
+		r = write_map(mnode, pos, 0, &old_zone);
+		on_error(r, goto exit_error);
+
+		ino_i->i_size -= bs;
+
+		if (old_zone == 0)
+			continue; /*Sparse block*/
+
+		r = mfs_free_zone(mnode->instance, old_zone);
+		on_error(r, goto exit_error);
+	}
+
+	ino_i->i_size = new_size;
+
+	return prune_ind_zones(mnode, new_size);
+
+exit_error:
+	return r;
+}
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_ops.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_ops.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_ops.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,1158 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include <stdlib.h>
+#include <fibril_synch.h>
+#include <align.h>
+#include <adt/hash_table.h>
+#include "mfs.h"
+
+#define OPEN_NODES_KEYS 2
+#define OPEN_NODES_DEV_HANDLE_KEY 0
+#define OPEN_NODES_INODE_KEY 1
+#define OPEN_NODES_BUCKETS 256
+
+static bool check_magic_number(uint16_t magic, bool *native,
+			       mfs_version_t *version, bool *longfilenames);
+static int mfs_node_core_get(fs_node_t **rfn, struct mfs_instance *inst,
+			     fs_index_t index);
+
+static int mfs_node_put(fs_node_t *fsnode);
+static int 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 char mfs_plb_get_char(unsigned pos);
+static bool mfs_is_directory(fs_node_t *fsnode);
+static bool mfs_is_file(fs_node_t *fsnode);
+static int mfs_has_children(bool *has_children, fs_node_t *fsnode);
+static int mfs_root_get(fs_node_t **rfn, devmap_handle_t handle);
+static devmap_handle_t mfs_device_get(fs_node_t *fsnode);
+static aoff64_t mfs_size_get(fs_node_t *node);
+static int mfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component);
+static int mfs_create_node(fs_node_t **rfn, devmap_handle_t handle, int flags);
+static int mfs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name);
+static int mfs_unlink(fs_node_t *, fs_node_t *, const char *name);
+static int mfs_destroy_node(fs_node_t *fn);
+static hash_index_t open_nodes_hash(unsigned long key[]);
+static int open_nodes_compare(unsigned long key[], hash_count_t keys,
+		link_t *item);
+static void open_nodes_remove_cb(link_t *link);
+
+static int mfs_node_get(fs_node_t **rfn, devmap_handle_t devmap_handle,
+			fs_index_t index);
+
+
+static LIST_INITIALIZE(inst_list);
+static FIBRIL_MUTEX_INITIALIZE(inst_list_mutex);
+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,
+	.device_get = mfs_device_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,
+	.plb_get_char = mfs_plb_get_char,
+	.has_children = mfs_has_children,
+	.lnkcnt_get = mfs_lnkcnt_get
+};
+
+/* Hash table interface for open nodes hash table */
+static hash_index_t open_nodes_hash(unsigned long key[])
+{
+	/* TODO: This is very simple and probably can be improved */
+	return key[OPEN_NODES_INODE_KEY] % OPEN_NODES_BUCKETS;
+}
+
+static int open_nodes_compare(unsigned long key[], hash_count_t keys,
+		link_t *item)
+{
+	struct mfs_node *mnode = hash_table_get_instance(item, struct mfs_node, link);
+	assert(keys > 0);
+	if (mnode->instance->handle !=
+	    ((devmap_handle_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
+		return false;
+	}
+	if (keys == 1) {
+		return true;
+	}
+	assert(keys == 2);
+	return (mnode->ino_i->index == key[OPEN_NODES_INODE_KEY]);
+}
+
+static void open_nodes_remove_cb(link_t *link)
+{
+	/* We don't use remove callback for this hash table */
+}
+
+static hash_table_operations_t open_nodes_ops = {
+	.hash = open_nodes_hash,
+	.compare = open_nodes_compare,
+	.remove_callback = open_nodes_remove_cb,
+};
+
+int mfs_global_init(void)
+{
+	if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
+			OPEN_NODES_KEYS, &open_nodes_ops)) {
+		return ENOMEM;
+	}
+	return EOK;
+}
+
+void mfs_mounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	enum cache_mode cmode;
+	struct mfs_superblock *sb;
+	struct mfs3_superblock *sb3;
+	struct mfs_sb_info *sbi;
+	struct mfs_instance *instance;
+	bool native, longnames;
+	mfs_version_t version;
+	uint16_t magic;
+
+	/* Accept the mount options */
+	char *opts;
+	int rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
+
+	if (rc != EOK) {
+		mfsdebug("Can't accept async data write\n");
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	/* Check for option enabling write through. */
+	if (str_cmp(opts, "wtcache") == 0)
+		cmode = CACHE_MODE_WT;
+	else
+		cmode = CACHE_MODE_WB;
+
+	free(opts);
+
+	/* initialize libblock */
+	rc = block_init(EXCHANGE_SERIALIZE, devmap_handle, 1024);
+	if (rc != EOK) {
+		mfsdebug("libblock initialization failed\n");
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	/*Allocate space for generic MFS superblock*/
+	sbi = malloc(sizeof(*sbi));
+
+	if (!sbi) {
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	/*Allocate space for filesystem instance*/
+	instance = malloc(sizeof(*instance));
+
+	if (!instance) {
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	instance->open_nodes_cnt = 0;
+
+	sb = malloc(MFS_SUPERBLOCK_SIZE);
+
+	if (!sb) {
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	/* Read the superblock */
+	rc = block_read_direct(devmap_handle, MFS_SUPERBLOCK << 1, 1, sb);
+	if (rc != EOK) {
+		block_fini(devmap_handle);
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	sb3 = (struct mfs3_superblock *) sb;
+
+	if (check_magic_number(sb->s_magic, &native, &version, &longnames)) {
+		/*This is a V1 or V2 Minix filesystem*/
+		magic = sb->s_magic;
+		goto recognized;
+	}
+
+	if (!check_magic_number(sb3->s_magic, &native, &version, &longnames)) {
+		mfsdebug("magic number not recognized\n");
+		block_fini(devmap_handle);
+		async_answer_0(rid, ENOTSUP);
+		return;
+	}
+
+	/*This is a V3 Minix filesystem*/
+
+	magic = sb3->s_magic;
+
+recognized:
+
+	mfsdebug("magic number recognized = %04x\n", magic);
+
+	/*Fill superblock info structure*/
+
+	sbi->fs_version = version;
+	sbi->long_names = longnames;
+	sbi->native = native;
+	sbi->magic = magic;
+	sbi->isearch = 0;
+	sbi->zsearch = 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;
+	}
+	sbi->itable_off = 2 + sbi->ibmap_blocks + sbi->zbmap_blocks;
+
+	free(sb);
+
+	rc = block_cache_init(devmap_handle, sbi->block_size, 0, cmode);
+
+	if (rc != EOK) {
+		block_fini(devmap_handle);
+		async_answer_0(rid, EINVAL);
+		mfsdebug("block cache initialization failed\n");
+		return;
+	}
+
+	/*Initialize the instance structure and add it to the list*/
+	link_initialize(&instance->link);
+	instance->handle = devmap_handle;
+	instance->sbi = sbi;
+
+	fibril_mutex_lock(&inst_list_mutex);
+	list_append(&instance->link, &inst_list);
+	fibril_mutex_unlock(&inst_list_mutex);
+
+	mfsdebug("mount successful\n");
+
+	fs_node_t *fn;
+	mfs_node_get(&fn, devmap_handle, MFS_ROOT_INO);
+
+	struct mfs_node *nroot = fn->data;
+
+	async_answer_3(rid, EOK,
+			MFS_ROOT_INO,
+			0,
+			nroot->ino_i->i_nlinks);
+
+	mfs_node_put(fn);
+}
+
+void mfs_mount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_mount(&mfs_libfs_ops, mfs_reg.fs_handle, rid, request);
+}
+
+void mfs_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_unmount(&mfs_libfs_ops, rid, request);
+}
+
+void mfs_unmounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t devmap = (devmap_handle_t) IPC_GET_ARG1(*request);
+	struct mfs_instance *inst;
+
+	int r = mfs_instance_get(devmap, &inst);
+	if (r != EOK) {
+		async_answer_0(rid, r);
+		return;
+	}
+
+	if (inst->open_nodes_cnt != 0) {
+		async_answer_0(rid, EBUSY);
+		return;
+	}
+
+	(void) block_cache_fini(devmap);
+	block_fini(devmap);
+
+	/* Remove the instance from the list */
+	fibril_mutex_lock(&inst_list_mutex);
+	list_remove(&inst->link);
+	fibril_mutex_unlock(&inst_list_mutex);
+
+	free(inst->sbi);
+	free(inst);
+
+	async_answer_0(rid, EOK);
+}
+
+devmap_handle_t mfs_device_get(fs_node_t *fsnode)
+{
+	struct mfs_node *node = fsnode->data;
+	return node->instance->handle;
+}
+
+static int mfs_create_node(fs_node_t **rfn, devmap_handle_t handle, int flags)
+{
+	int r;
+	struct mfs_instance *inst;
+	struct mfs_node *mnode;
+	fs_node_t *fsnode;
+	uint32_t inum;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	r = mfs_instance_get(handle, &inst);
+	on_error(r, return r);
+
+	/*Alloc a new inode*/
+	r = mfs_alloc_inode(inst, &inum);
+	on_error(r, 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;
+	else
+		ino_i->i_mode = S_IFREG;
+
+	ino_i->i_nlinks = 1;
+	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;
+
+	link_initialize(&mnode->link);
+
+	unsigned long key[] = {
+		[OPEN_NODES_DEV_HANDLE_KEY] = inst->handle,
+		[OPEN_NODES_INODE_KEY] = inum,
+	};
+
+	fibril_mutex_lock(&open_nodes_lock);
+	hash_table_insert(&open_nodes, key, &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:
+	return r;
+}
+
+static int 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;
+	int r;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	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 = read_dentry(mnode, &d_info, i);
+		on_error(r, return r);
+
+		if (!d_info.d_inum) {
+			/*This entry is not used*/
+			continue;
+		}
+
+		const size_t dentry_name_size = str_size(d_info.d_name);
+
+		if (comp_size == dentry_name_size &&
+			!bcmp(component, d_info.d_name, dentry_name_size)) {
+			/*Hit!*/
+			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)
+{
+	assert(node);
+
+	const struct mfs_node *mnode = node->data;
+	assert(mnode);
+	assert(mnode->ino_i);
+
+	return mnode->ino_i->i_size;
+}
+
+void mfs_stat(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_stat(&mfs_libfs_ops, mfs_reg.fs_handle, rid, request);
+}
+
+static int mfs_node_get(fs_node_t **rfn, devmap_handle_t devmap_handle,
+			fs_index_t index)
+{
+	int rc;
+	struct mfs_instance *instance;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	rc = mfs_instance_get(devmap_handle, &instance);
+	on_error(rc, return rc);
+
+	return mfs_node_core_get(rfn, instance, index);
+}
+
+static int mfs_node_put(fs_node_t *fsnode)
+{
+	int rc = EOK;
+	struct mfs_node *mnode = fsnode->data;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	fibril_mutex_lock(&open_nodes_lock);
+
+	assert(mnode->refcnt > 0);
+	mnode->refcnt--;
+	if (mnode->refcnt == 0) {
+		unsigned long key[] = {
+			[OPEN_NODES_DEV_HANDLE_KEY] = mnode->instance->handle,
+			[OPEN_NODES_INODE_KEY] = mnode->ino_i->index
+		};
+		hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
+		assert(mnode->instance->open_nodes_cnt > 0);
+		mnode->instance->open_nodes_cnt--;
+		rc = put_inode(mnode);
+		free(mnode->ino_i);
+		free(mnode);
+		free(fsnode);
+	}
+
+	fibril_mutex_unlock(&open_nodes_lock);
+	return rc;
+}
+
+static int mfs_node_open(fs_node_t *fsnode)
+{
+	/*
+	 * Opening a file is stateless, nothing
+	 * to be done here.
+	 */
+	return EOK;
+}
+
+static fs_index_t mfs_index_get(fs_node_t *fsnode)
+{
+	struct mfs_node *mnode = fsnode->data;
+
+	assert(mnode->ino_i);
+	return mnode->ino_i->index;
+}
+
+static unsigned mfs_lnkcnt_get(fs_node_t *fsnode)
+{
+	struct mfs_node *mnode = fsnode->data;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	assert(mnode);
+	assert(mnode->ino_i);
+
+	return mnode->ino_i->i_nlinks;
+}
+
+static int 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;
+	int rc;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	fibril_mutex_lock(&open_nodes_lock);
+
+	/* Check if the node is not already open */
+	unsigned long key[] = {
+		[OPEN_NODES_DEV_HANDLE_KEY] = inst->handle,
+		[OPEN_NODES_INODE_KEY] = index,
+	};
+	link_t *already_open = hash_table_find(&open_nodes, key);
+
+	if (already_open) {
+		mnode = hash_table_get_instance(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 = get_inode(inst, &ino_i, index);
+	on_error(rc, goto out_err);
+
+	ino_i->index = index;
+	mnode->ino_i = ino_i;
+	mnode->refcnt = 1;
+	link_initialize(&mnode->link);
+
+	mnode->instance = inst;
+	node->data = mnode;
+	mnode->fsnode = node;
+	*rfn = node;
+
+	hash_table_insert(&open_nodes, key, &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 int mfs_root_get(fs_node_t **rfn, devmap_handle_t handle)
+{
+	int rc = mfs_node_get(rfn, handle, MFS_ROOT_INO);
+	return rc;
+}
+
+void mfs_lookup(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_lookup(&mfs_libfs_ops, mfs_reg.fs_handle, rid, request);
+}
+
+static char mfs_plb_get_char(unsigned pos)
+{
+	return mfs_reg.plb_ro[pos % PLB_SIZE];
+}
+
+static int 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;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	if (str_size(name) > sbi->max_name_len)
+		return ENAMETOOLONG;
+
+	int r = insert_dentry(parent, name, child->ino_i->index);
+	on_error(r, goto exit_error);
+
+	if (S_ISDIR(child->ino_i->i_mode)) {
+		r = insert_dentry(child, ".", child->ino_i->index);
+		on_error(r, goto exit_error);
+		//child->ino_i->i_nlinks++;
+		//child->ino_i->dirty = true;
+		r = insert_dentry(child, "..", parent->ino_i->index);
+		on_error(r, goto exit_error);
+		//parent->ino_i->i_nlinks++;
+		//parent->ino_i->dirty = true;
+	}
+
+exit_error:
+	return r;
+}
+
+static int
+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;
+	int r;
+
+	mfsdebug("%s()\n", __FUNCTION__);
+
+	if (!parent)
+		return EBUSY;
+
+	r = mfs_has_children(&has_children, cfn);
+	on_error(r, return r);
+
+	if (has_children)
+		return ENOTEMPTY;
+
+	r = remove_dentry(parent, name);
+	on_error(r, return r);
+
+	struct mfs_ino_info *chino = child->ino_i;
+
+	assert(chino->i_nlinks >= 1);
+	--chino->i_nlinks;
+/*
+	if (chino->i_nlinks == 0 && S_ISDIR(chino->i_mode)) {
+		parent->ino_i->i_nlinks--;
+		parent->ino_i->dirty = true;
+	}
+*/
+
+	chino->dirty = true;
+
+	return r;
+}
+
+static int mfs_has_children(bool *has_children, fs_node_t *fsnode)
+{
+	struct mfs_node *mnode = fsnode->data;
+	struct mfs_sb_info *sbi = mnode->instance->sbi;
+	int r;
+
+	*has_children = false;
+
+	if (!S_ISDIR(mnode->ino_i->i_mode))
+		goto out;
+
+	struct mfs_dentry_info d_info;
+
+	/* The first two dentries are always . and .. */
+	unsigned i;
+	for (i = 2; i < mnode->ino_i->i_size / sbi->dirsize; ++i) {
+		r = read_dentry(mnode, &d_info, i);
+		on_error(r, return r);
+
+		if (d_info.d_inum) {
+			/*A valid entry has been found*/
+			*has_children = true;
+			break;
+		}
+	}
+out:
+
+	return EOK;
+}
+
+void
+mfs_read(ipc_callid_t rid, ipc_call_t *request)
+{
+	int rc;
+	devmap_handle_t handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request),
+					       IPC_GET_ARG4(*request));
+	fs_node_t *fn;
+
+	rc = mfs_node_get(&fn, handle, index);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	if (!fn) {
+		async_answer_0(rid, ENOENT);
+		return;
+	}
+
+	struct mfs_node *mnode;
+	struct mfs_ino_info *ino_i;
+	size_t len, bytes = 0;
+	ipc_callid_t callid;
+
+	mnode = fn->data;
+	ino_i = mnode->ino_i;
+
+	if (!async_data_read_receive(&callid, &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) {
+			/*Skip the first two dentries ('.' and '..')*/
+			pos = 2;
+		}
+
+		for (; pos < mnode->ino_i->i_size / sbi->dirsize; ++pos) {
+			rc = read_dentry(mnode, &d_info, pos);
+			on_error(rc, goto out_error);
+
+			if (d_info.d_inum) {
+				/*Dentry found!*/
+				goto found;
+			}
+		}
+
+		rc = mfs_node_put(fn);
+		async_answer_0(callid, rc != EOK ? rc : ENOENT);
+		async_answer_1(rid, rc != EOK ? rc : ENOENT, 0);
+		return;
+found:
+		async_data_read_finalize(callid, 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) {
+			/*Trying to read beyond the end of file*/
+			bytes = 0;
+			(void) async_data_read_finalize(callid, 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 = read_map(&zone, mnode, pos);
+		on_error(rc, goto out_error);
+
+		if (zone == 0) {
+			/*sparse file*/
+			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(callid,
+						 buf + pos % sbi->block_size, bytes);
+			free(buf);
+			goto out_success;
+		}
+
+		rc = block_get(&b, handle, zone, BLOCK_FLAGS_NONE);
+		on_error(rc, goto out_error);
+
+		async_data_read_finalize(callid, b->data +
+					 pos % sbi->block_size, bytes);
+
+		rc = block_put(b);
+		if (rc != EOK) {
+			mfs_node_put(fn);
+			async_answer_0(rid, rc);
+			return;
+		}
+	}
+out_success:
+	rc = mfs_node_put(fn);
+	async_answer_1(rid, rc, (sysarg_t)bytes);
+	return;
+out_error:
+	;
+	int tmp = mfs_node_put(fn);
+	async_answer_0(callid, tmp != EOK ? tmp : rc);
+	async_answer_0(rid, tmp != EOK ? tmp : rc);
+}
+
+void
+mfs_write(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request),
+					       IPC_GET_ARG4(*request));
+
+	fs_node_t *fn;
+	int r;
+	int flags = BLOCK_FLAGS_NONE;
+
+	r = mfs_node_get(&fn, handle, index);
+	if (r != EOK) {
+		async_answer_0(rid, r);
+		return;
+	}
+
+	if (!fn) {
+		async_answer_0(rid, ENOENT);
+		return;
+	}
+
+	ipc_callid_t callid;
+	size_t len;
+
+	if (!async_data_write_receive(&callid, &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);
+	size_t boundary = ROUND_UP(ino_i->i_size, bs);
+	uint32_t block;
+
+	if (bytes == bs)
+		flags = BLOCK_FLAGS_NOREAD;
+
+	if (pos < boundary) {
+		r = read_map(&block, mnode, pos);
+		on_error(r, goto out_err);
+
+		if (block == 0) {
+			/*Writing in a sparse block*/
+			r = mfs_alloc_zone(mnode->instance, &block);
+			on_error(r, goto out_err);
+			flags = BLOCK_FLAGS_NOREAD;
+		}
+	} else {
+		uint32_t dummy;
+
+		r = mfs_alloc_zone(mnode->instance, &block);
+		on_error(r, goto out_err);
+
+		r = write_map(mnode, pos, block, &dummy);
+		on_error(r, goto out_err);
+	}
+
+	block_t *b;
+	r = block_get(&b, handle, block, flags);
+	on_error(r, goto out_err);
+
+	async_data_write_finalize(callid, b->data + pos % bs, bytes);
+	b->dirty = true;
+
+	r = block_put(b);
+	if (r != EOK) {
+		mfs_node_put(fn);
+		async_answer_0(rid, r);
+		return;
+	}
+
+	ino_i->i_size = pos + bytes;
+	ino_i->dirty = true;
+	r = mfs_node_put(fn);
+	async_answer_2(rid, r, bytes, pos + bytes);
+	return;
+
+out_err:
+	mfs_node_put(fn);
+	async_answer_0(callid, r);
+	async_answer_0(rid, r);
+}
+
+void
+mfs_destroy(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t handle = (devmap_handle_t)IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
+	fs_node_t *fn;
+	int r;
+
+	r = mfs_node_get(&fn, handle, index);
+	if (r != EOK) {
+		async_answer_0(rid, r);
+		return;
+	}
+	if (!fn) {
+		async_answer_0(rid, ENOENT);
+		return;
+	}
+
+	/*Destroy the inode*/
+	r = mfs_destroy_node(fn);
+	async_answer_0(rid, r);
+}
+
+static int
+mfs_destroy_node(fs_node_t *fn)
+{
+	struct mfs_node *mnode = fn->data;
+	bool has_children;
+	int r;
+
+	mfsdebug("mfs_destroy_node %d\n", mnode->ino_i->index);
+
+	r = mfs_has_children(&has_children, fn);
+	on_error(r, goto out);
+
+	assert(!has_children);
+
+	if (mnode->ino_i->i_nlinks > 0) {
+		mfsdebug("nlinks = %d", mnode->ino_i->i_nlinks);
+		r = EOK;
+		goto out;
+	}
+
+	/*Free the entire inode content*/
+	r = inode_shrink(mnode, mnode->ino_i->i_size);
+	on_error(r, goto out);
+	r = mfs_free_inode(mnode->instance, mnode->ino_i->index);
+
+out:
+	mfs_node_put(fn);
+	return r;
+}
+
+void
+mfs_truncate(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	aoff64_t size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request),
+						IPC_GET_ARG4(*request));
+	fs_node_t *fn;
+	int r;
+
+	r = mfs_node_get(&fn, handle, index);
+	if (r != EOK) {
+		async_answer_0(rid, r);
+		return;
+	}
+
+	if (!fn) {
+		async_answer_0(rid, r);
+		return;
+	}
+
+	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 = inode_shrink(mnode, ino_i->i_size - size);
+
+	async_answer_0(rid, r);
+	mfs_node_put(fn);
+}
+
+int mfs_instance_get(devmap_handle_t handle, struct mfs_instance **instance)
+{
+	link_t *link;
+	struct mfs_instance *instance_ptr;
+
+	fibril_mutex_lock(&inst_list_mutex);
+
+	for (link = inst_list.next; link != &inst_list; link = link->next) {
+		instance_ptr = list_get_instance(link, struct mfs_instance,
+						 link);
+
+		if (instance_ptr->handle == handle) {
+			*instance = instance_ptr;
+			fibril_mutex_unlock(&inst_list_mutex);
+			return EOK;
+		}
+	}
+
+	mfsdebug("Instance not found\n");
+
+	fibril_mutex_unlock(&inst_list_mutex);
+	return EINVAL;
+}
+
+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;
+}
+
+void
+mfs_close(ipc_callid_t rid, ipc_call_t *request)
+{
+	async_answer_0(rid, EOK);
+}
+
+void
+mfs_open_node(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_open_node(&mfs_libfs_ops, mfs_reg.fs_handle, rid, request);
+}
+
+void
+mfs_sync(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t devmap = (devmap_handle_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+
+	fs_node_t *fn;
+	int rc = mfs_node_get(&fn, devmap, index);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	if (!fn) {
+		async_answer_0(rid, ENOENT);
+		return;
+	}
+
+	struct mfs_node *mnode = fn->data;
+	mnode->ino_i->dirty = true;
+
+	rc = mfs_node_put(fn);
+	async_answer_0(rid, rc);
+}
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_rw.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_rw.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_rw.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include "mfs.h"
+
+static int
+rw_map_ondisk(uint32_t *b, const struct mfs_node *mnode, int rblock,
+	      bool write_mode, uint32_t w_block);
+
+static int
+reset_zone_content(struct mfs_instance *inst, uint32_t zone);
+
+static int
+alloc_zone_and_clear(struct mfs_instance *inst, uint32_t *zone);
+
+static int
+read_ind_zone(struct mfs_instance *inst, uint32_t zone, uint32_t **ind_zone);
+
+static int
+write_ind_zone(struct mfs_instance *inst, uint32_t zone, uint32_t *ind_zone);
+
+
+/**Given the position in the file expressed in
+ *bytes, this function returns the on-disk block
+ *relative to that position.
+ *Returns zero if the block does not exist.
+ */
+int
+read_map(uint32_t *b, const struct mfs_node *mnode, uint32_t pos)
+{
+	int r;
+	const struct mfs_sb_info *sbi = mnode->instance->sbi;
+	const int block_size = sbi->block_size;
+
+	/*Compute relative block number in file*/
+	int rblock = pos / block_size;
+
+	if (mnode->ino_i->i_size < pos) {
+		/*Trying to read beyond the end of file*/
+		r = EOK;
+		*b = 0;
+		goto out;
+	}
+
+	r = rw_map_ondisk(b, mnode, rblock, false, 0);
+out:
+	return r;
+}
+
+int
+write_map(struct mfs_node *mnode, const uint32_t pos, uint32_t new_zone,
+	  uint32_t *old_zone)
+{
+	const struct mfs_sb_info *sbi = mnode->instance->sbi;
+
+	/*Compute the relative block number in file*/
+	int rblock = pos / sbi->block_size;
+
+	return rw_map_ondisk(old_zone, mnode, rblock, true, new_zone);
+}
+
+static int
+rw_map_ondisk(uint32_t *b, const struct mfs_node *mnode, int rblock,
+	      bool write_mode, uint32_t w_block)
+{
+	int r, nr_direct;
+	int ptrs_per_block;
+	uint32_t *ind_zone, *ind2_zone;
+
+	assert(mnode);
+	struct mfs_ino_info *ino_i = mnode->ino_i;
+
+	assert(ino_i);
+	assert(mnode->instance);
+
+	struct mfs_instance *inst = mnode->instance;
+	struct mfs_sb_info *sbi = inst->sbi;
+	assert(sbi);
+
+	const mfs_version_t fs_version = sbi->fs_version;
+
+	if (fs_version == MFS_VERSION_V1) {
+		nr_direct = V1_NR_DIRECT_ZONES;
+		ptrs_per_block = MFS_BLOCKSIZE / sizeof(uint16_t);
+	} else {
+		nr_direct = V2_NR_DIRECT_ZONES;
+		ptrs_per_block = sbi->block_size / sizeof(uint32_t);
+	}
+
+	/*Check if the wanted block is in the direct zones*/
+	if (rblock < nr_direct) {
+		*b = ino_i->i_dzone[rblock];
+		if (write_mode) {
+			ino_i->i_dzone[rblock] = w_block;
+			ino_i->dirty = true;
+		}
+		return EOK;
+	}
+
+	rblock -= nr_direct;
+
+	if (rblock < ptrs_per_block) {
+		/*The wanted block is in the single indirect zone chain*/
+		if (ino_i->i_izone[0] == 0) {
+			if (write_mode) {
+				uint32_t zone;
+				r = alloc_zone_and_clear(inst, &zone);
+				on_error(r, return r);
+
+				ino_i->i_izone[0] = zone;
+				ino_i->dirty = true;
+			} else {
+				/*Sparse block*/
+				*b = 0;
+				return EOK;
+			}
+		}
+
+		r = read_ind_zone(inst, ino_i->i_izone[0], &ind_zone);
+		on_error(r, return r);
+
+		*b = ind_zone[rblock];
+		if (write_mode) {
+			ind_zone[rblock] = w_block;
+			write_ind_zone(inst, ino_i->i_izone[0], ind_zone);
+		}
+
+		goto out_free_ind1;
+	}
+
+	rblock -= ptrs_per_block;
+
+	/*The wanted block is in the double indirect zone chain*/
+
+	/*read the first indirect zone of the chain*/
+	if (ino_i->i_izone[1] == 0) {
+		if (write_mode) {
+			uint32_t zone;
+			r = alloc_zone_and_clear(inst, &zone);
+			on_error(r, return r);
+
+			ino_i->i_izone[1] = zone;
+			ino_i->dirty = true;
+		} else {
+			/*Sparse block*/
+			*b = 0;
+			return EOK;
+		}
+	}
+
+	r = read_ind_zone(inst, ino_i->i_izone[1], &ind_zone);
+	on_error(r, return r);
+
+	/*
+	 *Compute the position of the second indirect
+	 *zone pointer in the chain.
+	 */
+	uint32_t ind2_off = rblock / ptrs_per_block;
+
+	/*read the second indirect zone of the chain*/
+	if (ind_zone[ind2_off] == 0) {
+		if (write_mode) {
+			uint32_t zone;
+			r = alloc_zone_and_clear(inst, &zone);
+			on_error(r, goto out_free_ind1);
+
+			ind_zone[ind2_off] = zone;
+			write_ind_zone(inst, ino_i->i_izone[1], ind_zone);
+		} else {
+			/*Sparse block*/
+			r = EOK;
+			*b = 0;
+			goto out_free_ind1;
+		}
+	}
+
+	r = read_ind_zone(inst, ind_zone[ind2_off], &ind2_zone);
+	on_error(r, goto out_free_ind1);
+
+	*b = ind2_zone[ind2_off % ptrs_per_block];
+	if (write_mode) {
+		ind2_zone[ind2_off % ptrs_per_block] = w_block;
+		write_ind_zone(inst, ind_zone[ind2_off], ind2_zone);
+	}
+
+	r = EOK;
+
+	free(ind2_zone);
+out_free_ind1:
+	free(ind_zone);
+	return r;
+}
+
+/*Free unused indirect zones*/
+int
+prune_ind_zones(struct mfs_node *mnode, size_t new_size)
+{
+	struct mfs_instance *inst = mnode->instance;
+	struct mfs_sb_info *sbi = inst->sbi;
+	struct mfs_ino_info *ino_i = mnode->ino_i;
+	int nr_direct, ptrs_per_block, rblock, r;
+	int i;
+
+	mfs_version_t fs_version = sbi->fs_version;
+	
+	if (fs_version == MFS_VERSION_V1) {
+		nr_direct = V1_NR_DIRECT_ZONES;
+		ptrs_per_block = MFS_BLOCKSIZE / sizeof(uint16_t);
+	} else {
+		nr_direct = V2_NR_DIRECT_ZONES;
+		ptrs_per_block = sbi->block_size / sizeof(uint32_t);
+	}
+
+	rblock = new_size / sbi->block_size;
+
+	if (rblock < nr_direct) {
+		/*free the single indirect zone*/
+		if (ino_i->i_izone[0]) {
+			r = mfs_free_zone(inst, ino_i->i_izone[0]);
+			on_error(r, return r);
+
+			ino_i->i_izone[0] = 0;
+			ino_i->dirty = true;
+		}
+	}
+
+	rblock -= nr_direct + ptrs_per_block;
+
+	int fzone_to_free = (rblock < 0 ? 0 : rblock) / ptrs_per_block;
+
+	if ((fzone_to_free % ptrs_per_block) != 0)
+		++fzone_to_free;
+
+	/*free the entire double indirect zone*/
+	uint32_t *dbl_zone;
+
+	if (ino_i->i_izone[1] == 0) {
+		/*Nothing to be done*/
+		return EOK;
+	}
+
+	r = read_ind_zone(inst, ino_i->i_izone[1], &dbl_zone);
+	on_error(r, return r);
+
+	for (i = fzone_to_free; i < ptrs_per_block; ++i) {
+		if (dbl_zone[i] == 0)
+			continue;
+
+		r = mfs_free_zone(inst, dbl_zone[i]);
+		on_error(r, goto out);
+	}
+
+	if (fzone_to_free == 0) {
+		r = mfs_free_zone(inst, ino_i->i_izone[1]);
+		ino_i->i_izone[1] = 0;
+		ino_i->dirty = true;
+	}
+out:
+	free(dbl_zone);
+	return r;
+}
+
+static int
+reset_zone_content(struct mfs_instance *inst, uint32_t zone)
+{
+	block_t *b;
+	int r;
+
+	r = block_get(&b, inst->handle, zone, BLOCK_FLAGS_NOREAD);
+	on_error(r, return r);
+
+	memset(b->data, 0, b->size);
+	b->dirty = true;
+
+	return block_put(b);
+}
+
+static int
+alloc_zone_and_clear(struct mfs_instance *inst, uint32_t *zone)
+{
+	int r;
+
+	r = mfs_alloc_zone(inst, zone);
+	on_error(r, return r);
+
+	r = reset_zone_content(inst, *zone);
+	return r;
+}
+
+static int
+read_ind_zone(struct mfs_instance *inst, uint32_t zone, uint32_t **ind_zone)
+{
+	struct mfs_sb_info *sbi = inst->sbi;
+	int r;
+	unsigned i;
+	block_t *b;
+	const int max_ind_zone_ptrs = (MFS_MAX_BLOCKSIZE / sizeof(uint16_t)) *
+				      sizeof(uint32_t);
+
+	*ind_zone = malloc(max_ind_zone_ptrs);
+	if (*ind_zone == NULL)
+		return ENOMEM;
+
+	r = block_get(&b, inst->handle, zone, BLOCK_FLAGS_NONE);
+	if (r != EOK) {
+		free(*ind_zone);
+		return r;
+	}
+
+	if (sbi->fs_version == MFS_VERSION_V1) {
+		uint16_t *src_ptr = b->data;
+
+		for (i = 0; i < sbi->block_size / sizeof(uint16_t); ++i)
+			(*ind_zone)[i] = conv16(sbi->native, src_ptr[i]);
+	} else {
+		uint32_t *src_ptr = b->data;
+
+		for (i = 0; i < sbi->block_size / sizeof(uint32_t); ++i)
+			(*ind_zone)[i] = conv32(sbi->native, src_ptr[i]);
+	}
+
+	return block_put(b);
+}
+
+static int
+write_ind_zone(struct mfs_instance *inst, uint32_t zone, uint32_t *ind_zone)
+{
+	struct mfs_sb_info *sbi = inst->sbi;
+	int r;
+	unsigned i;
+	block_t *b;
+
+	r = block_get(&b, inst->handle, zone, BLOCK_FLAGS_NONE);
+	on_error(r, return r);
+
+	if (sbi->fs_version == MFS_VERSION_V1) {
+		uint16_t *dest_ptr = b->data;
+
+		for (i = 0; i < sbi->block_size / sizeof(uint16_t); ++i)
+			dest_ptr[i] = conv16(sbi->native, ind_zone[i]);
+	} else {
+		uint32_t *dest_ptr = b->data;
+
+		for (i = 0; i < sbi->block_size / sizeof(uint32_t); ++i)
+			dest_ptr[i] = conv32(sbi->native, ind_zone[i]);
+
+	}
+	b->dirty = true;
+
+	return block_put(b);
+}
+
+
+/**
+ * @}
+ */
+
Index: uspace/srv/fs/minixfs/mfs_utils.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_utils.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/fs/minixfs/mfs_utils.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#include <byteorder.h>
+#include "mfs.h"
+
+uint16_t conv16(bool native, uint16_t n)
+{
+	if (native)
+		return n;
+
+	return uint16_t_byteorder_swap(n);
+}
+
+uint32_t conv32(bool native, uint32_t n)
+{
+	if (native)
+		return n;
+
+	return uint32_t_byteorder_swap(n);
+}
+
+uint64_t conv64(bool native, uint64_t n)
+{
+	if (native)
+		return n;
+
+	return uint64_t_byteorder_swap(n);
+}
+
+/**
+ * @}
+ */
+
Index: uspace/srv/hid/input/include/sun.h
===================================================================
--- uspace/srv/hid/input/include/sun.h	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/hid/input/include/sun.h	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup kbdgen generic
+ * @brief Sun keyboard virtual port driver.
+ * @ingroup kbd
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KBD_SUN_H_
+#define KBD_SUN_H_
+
+extern int ns16550_port_init(void);
+extern int z8530_port_init(void);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/port/dummy.c
===================================================================
--- uspace/srv/hid/input/port/dummy.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/hid/input/port/dummy.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup kbd_port
+ * @brief	Dummy keyboard port driver.
+ * @ingroup  kbd
+ * @{
+ */ 
+/** @file
+ */
+
+#include <kbd_port.h>
+#include <kbd.h>
+
+int kbd_port_init(void)
+{
+	return 0;
+}
+
+void kbd_port_yield(void)
+{
+}
+
+void kbd_port_reclaim(void)
+{
+}
+
+void kbd_port_write(uint8_t data)
+{
+	(void) data;
+}
+
+/** @}
+*/
Index: uspace/srv/hid/input/port/sgcn.c
===================================================================
--- uspace/srv/hid/input/port/sgcn.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/hid/input/port/sgcn.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2008 Pavel Rimsky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup kbd_port
+ * @ingroup  kbd
+ * @{
+ */
+/** @file
+ * @brief SGCN (Serengeti Console) keyboard port driver.
+ */
+
+#include <as.h>
+#include <ddi.h>
+#include <async.h>
+#include <kbd.h>
+#include <kbd_port.h>
+#include <sysinfo.h>
+#include <stdio.h>
+#include <thread.h>
+#include <bool.h>
+#include <errno.h>
+
+#define POLL_INTERVAL  10000
+
+/**
+ * SGCN buffer header. It is placed at the very beginning of the SGCN
+ * buffer.
+ */
+typedef struct {
+	/** hard-wired to "CON" */
+	char magic[4];
+	
+	/** we don't need this */
+	char unused[8];
+	
+	/** offset within the SGCN buffer of the input buffer start */
+	uint32_t in_begin;
+	
+	/** offset within the SGCN buffer of the input buffer end */
+	uint32_t in_end;
+	
+	/** offset within the SGCN buffer of the input buffer read pointer */
+	uint32_t in_rdptr;
+	
+	/** offset within the SGCN buffer of the input buffer write pointer */
+	uint32_t in_wrptr;
+} __attribute__ ((packed)) sgcn_buffer_header_t;
+
+/*
+ * Returns a pointer to the object of a given type which is placed at the given
+ * offset from the console buffer beginning.
+ */
+#define SGCN_BUFFER(type, offset) \
+		((type *) (sram_virt_addr + sram_buffer_offset + (offset)))
+
+/** Returns a pointer to the console buffer header. */
+#define SGCN_BUFFER_HEADER	(SGCN_BUFFER(sgcn_buffer_header_t, 0))
+
+/**
+ * Virtual address mapped to SRAM.
+ */
+static uintptr_t sram_virt_addr;
+
+/**
+ * SGCN buffer offset within SGCN.
+ */
+static uintptr_t sram_buffer_offset;
+
+/* polling thread */
+static void sgcn_thread_impl(void *arg);
+
+static volatile bool polling_disabled = false;
+
+/**
+ * Initializes the SGCN driver.
+ * Maps the physical memory (SRAM) and creates the polling thread. 
+ */
+int kbd_port_init(void)
+{
+	sysarg_t sram_paddr;
+	if (sysinfo_get_value("sram.address.physical", &sram_paddr) != EOK)
+		return -1;
+	
+	sysarg_t sram_size;
+	if (sysinfo_get_value("sram.area.size", &sram_size) != EOK)
+		return -1;
+	
+	if (sysinfo_get_value("sram.buffer.offset", &sram_buffer_offset) != EOK)
+		sram_buffer_offset = 0;
+	
+	sram_virt_addr = (uintptr_t) as_get_mappable_page(sram_size);
+	
+	if (physmem_map((void *) sram_paddr, (void *) sram_virt_addr,
+	    sram_size / PAGE_SIZE, AS_AREA_READ | AS_AREA_WRITE) != 0) {
+		printf("SGCN: uspace driver could not map physical memory.");
+		return -1;
+	}
+	
+	thread_id_t tid;
+	int rc = thread_create(sgcn_thread_impl, NULL, "kbd_poll", &tid);
+	if (rc != 0)
+		return rc;
+	
+	return 0;
+}
+
+void kbd_port_yield(void)
+{
+	polling_disabled = true;
+}
+
+void kbd_port_reclaim(void)
+{
+	polling_disabled = false;
+}
+
+void kbd_port_write(uint8_t data)
+{
+	(void) data;
+}
+
+/**
+ * Handler of the "key pressed" event. Reads codes of all the pressed keys from
+ * the buffer. 
+ */
+static void sgcn_key_pressed(void)
+{
+	char c;
+	
+	uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
+	uint32_t end = SGCN_BUFFER_HEADER->in_end;
+	uint32_t size = end - begin;
+	
+	volatile char *buf_ptr = (volatile char *)
+		SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
+	volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
+	volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
+	
+	while (*in_rdptr_ptr != *in_wrptr_ptr) {
+		c = *buf_ptr;
+		*in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
+		buf_ptr = (volatile char *)
+			SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
+		kbd_push_scancode(c);
+	}
+}
+
+/**
+ * Thread to poll SGCN for keypresses.
+ */
+static void sgcn_thread_impl(void *arg)
+{
+	(void) arg;
+
+	while (1) {
+		if (polling_disabled == false)
+			sgcn_key_pressed();
+		usleep(POLL_INTERVAL);
+	}
+}
+
+/** @}
+ */
Index: uspace/srv/hid/input/port/sun.c
===================================================================
--- uspace/srv/hid/input/port/sun.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/hid/input/port/sun.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup kbd_port
+ * @ingroup  kbd
+ * @{
+ */
+/** @file
+ * @brief Sun keyboard virtual port driver.
+ */
+
+#include <kbd.h>
+#include <kbd_port.h>
+#include <sun.h>
+#include <sysinfo.h>
+#include <errno.h>
+#include <bool.h>
+
+/** Sun keyboard virtual port driver.
+ *
+ * This is a virtual port driver which can use
+ * both ns16550_port_init and z8530_port_init
+ * according to the information passed from the
+ * kernel. This is just a temporal hack.
+ *
+ */
+int kbd_port_init(void)
+{
+	sysarg_t z8530;
+	if (sysinfo_get_value("kbd.type.z8530", &z8530) != EOK)
+		z8530 = false;
+	
+	sysarg_t ns16550;
+	if (sysinfo_get_value("kbd.type.ns16550", &ns16550) != EOK)
+		ns16550 = false;
+	
+	if (z8530) {
+		if (z8530_port_init() == 0)
+			return 0;
+	}
+	
+	if (ns16550) {
+		if (ns16550_port_init() == 0)
+			return 0;
+	}
+	
+	return -1;
+}
+
+void kbd_port_yield(void)
+{
+}
+
+void kbd_port_reclaim(void)
+{
+}
+
+void kbd_port_write(uint8_t data)
+{
+	(void) data;
+}
+
+/** @}
+*/
Index: uspace/srv/hid/input/port/z8530.c
===================================================================
--- uspace/srv/hid/input/port/z8530.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
+++ uspace/srv/hid/input/port/z8530.c	(revision 8ff0bd25c8215c84283f8ba039f4cb58b7660280)
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2006 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup kbd_port
+ * @ingroup  kbd
+ * @{
+ */
+/** @file
+ * @brief Z8530 keyboard port driver.
+ */
+
+#include <ipc/irc.h>
+#include <async.h>
+#include <async_obsolete.h>
+#include <sysinfo.h>
+#include <kbd.h>
+#include <kbd_port.h>
+#include <sun.h>
+#include <sys/types.h>
+#include <ddi.h>
+#include <errno.h>
+
+#define CHAN_A_STATUS  4
+#define CHAN_A_DATA    6
+
+#define RR0_RCA  1
+
+static irq_cmd_t z8530_cmds[] = {
+	{
+		.cmd = CMD_PIO_READ_8,
+		.addr = (void *) 0,     /* Will be patched in run-time */
+		.dstarg = 1
+	},
+	{
+		.cmd = CMD_BTEST,
+		.value = RR0_RCA,
+		.srcarg = 1,
+		.dstarg = 3
+	},
+	{
+		.cmd = CMD_PREDICATE,
+		.value = 2,
+		.srcarg = 3
+	},
+	{
+		.cmd = CMD_PIO_READ_8,
+		.addr = (void *) 0,     /* Will be patched in run-time */
+		.dstarg = 2
+	},
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+	
+irq_code_t z8530_kbd = {
+	sizeof(z8530_cmds) / sizeof(irq_cmd_t),
+	z8530_cmds
+};
+
+static void z8530_irq_handler(ipc_callid_t iid, ipc_call_t *call);
+
+int z8530_port_init(void)
+{
+	sysarg_t kaddr;
+	if (sysinfo_get_value("kbd.address.kernel", &kaddr) != EOK)
+		return -1;
+	
+	sysarg_t inr;
+	if (sysinfo_get_value("kbd.inr", &inr) != EOK)
+		return -1;
+	
+	z8530_cmds[0].addr = (void *) kaddr + CHAN_A_STATUS;
+	z8530_cmds[3].addr = (void *) kaddr + CHAN_A_DATA;
+	
+	async_set_interrupt_received(z8530_irq_handler);
+	register_irq(inr, device_assign_devno(), inr, &z8530_kbd);
+	
+	return 0;
+}
+
+static void z8530_irq_handler(ipc_callid_t iid, ipc_call_t *call)
+{
+	int scan_code = IPC_GET_ARG2(*call);
+	kbd_push_scancode(scan_code);
+	
+	if (irc_service)
+		async_obsolete_msg_1(irc_phone, IRC_CLEAR_INTERRUPT,
+		    IPC_GET_IMETHOD(*call));
+}
+
+/** @}
+ */
