Index: uspace/srv/fs/minixfs/mfs_ops.c
===================================================================
--- uspace/srv/fs/minixfs/mfs_ops.c	(revision e8b6b6a49ef17c13949c93ca1ec32032f4946b9b)
+++ uspace/srv/fs/minixfs/mfs_ops.c	(revision ee257b2ad4bce5ef4faef849fa4acf038a8e20fb)
@@ -34,5 +34,11 @@
 #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,
@@ -57,4 +63,8 @@
 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,
@@ -64,4 +74,6 @@
 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 = {
@@ -85,4 +97,47 @@
 };
 
+/* 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)
 {
@@ -138,4 +193,6 @@
 		return;
 	}
+
+	instance->open_nodes_cnt = 0;
 
 	sb = malloc(MFS_SUPERBLOCK_SIZE);
@@ -265,4 +322,9 @@
 	}
 
+	if (inst->open_nodes_cnt != 0) {
+		async_answer_0(rid, EBUSY);
+		return;
+	}
+
 	(void) block_cache_fini(devmap);
 	block_fini(devmap);
@@ -292,4 +354,6 @@
 	fs_node_t *fsnode;
 	uint32_t inum;
+
+	mfsdebug("%s()\n", __FUNCTION__);
 
 	r = mfs_instance_get(handle, &inst);
@@ -342,10 +406,23 @@
 	mnode->ino_i = ino_i;
 	mnode->instance = inst;
-
-	r = put_inode(mnode);
-	on_error(r, goto out_err_2);
+	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;
 
@@ -366,4 +443,6 @@
 	struct mfs_dentry_info d_info;
 	int r;
+
+	mfsdebug("%s()\n", __FUNCTION__);
 
 	if (!S_ISDIR(ino_i->i_mode))
@@ -420,4 +499,6 @@
 	struct mfs_instance *instance;
 
+	mfsdebug("%s()\n", __FUNCTION__);
+
 	rc = mfs_instance_get(devmap_handle, &instance);
 	on_error(rc, return rc);
@@ -428,12 +509,29 @@
 static int mfs_node_put(fs_node_t *fsnode)
 {
+	int rc = EOK;
 	struct mfs_node *mnode = fsnode->data;
 
-	put_inode(mnode);
-	free(mnode->ino_i);
-	free(mnode);
-	free(fsnode);
-
-	return EOK;
+	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;
 }
 
@@ -459,8 +557,10 @@
 	struct mfs_node *mnode = fsnode->data;
 
+	mfsdebug("%s()\n", __FUNCTION__);
+
 	assert(mnode);
 	assert(mnode->ino_i);
 
-	return mnode->ino_i->i_nlinks;;
+	return mnode->ino_i->i_nlinks;
 }
 
@@ -471,4 +571,24 @@
 	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));
@@ -493,8 +613,16 @@
 	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;
@@ -505,4 +633,5 @@
 	if (mnode)
 		free(mnode);
+	fibril_mutex_unlock(&open_nodes_lock);
 	return rc;
 }
@@ -542,4 +671,6 @@
 	struct mfs_sb_info *sbi = parent->instance->sbi;
 
+	mfsdebug("%s()\n", __FUNCTION__);
+
 	if (str_size(name) > sbi->max_name_len)
 		return ENAMETOOLONG;
@@ -551,5 +682,10 @@
 		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;
 	}
 
@@ -566,4 +702,6 @@
 	int r;
 
+	mfsdebug("%s()\n", __FUNCTION__);
+
 	if (!parent)
 		return EBUSY;
@@ -583,7 +721,12 @@
 	--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 EOK;
+	return r;
 }
 
