Index: uspace/lib/ext4/libext4_block_group.c
===================================================================
--- uspace/lib/ext4/libext4_block_group.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_block_group.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -36,6 +36,12 @@
  */
 
-#include "libext4_block_group.h"
+#include <byteorder.h>
+#include "libext4.h"
 
+uint64_t ext4_block_group_get_inode_table_first_block(ext4_block_group_t *bg)
+{
+	return ((uint64_t)uint32_t_le2host(bg->inode_table_first_hi) << 32) |
+			uint32_t_le2host(bg->inode_table_first_lo);
+}
 
 /**
Index: uspace/lib/ext4/libext4_block_group.h
===================================================================
--- uspace/lib/ext4/libext4_block_group.h	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_block_group.h	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -34,6 +34,7 @@
 #define LIBEXT4_LIBEXT4_BLOCK_GROUP_H_
 
+#include <libblock.h>
 #include <sys/types.h>
-
+#include "libext4_block_group.h"
 /*
  * Structure of a blocks group descriptor
@@ -42,5 +43,5 @@
 	uint32_t block_bitmap_lo; // Blocks bitmap block
 	uint32_t inode_bitmap_lo; // Inodes bitmap block
-	uint32_t inode_table_lo; // Inodes table block
+	uint32_t inode_table_first_lo; // Inodes table block
 	uint16_t free_blocks_count_lo; // Free blocks count
 	uint16_t free_inodes_count_lo; // Free inodes count
@@ -52,5 +53,5 @@
 	uint32_t block_bitmap_hi; // Blocks bitmap block MSB
 	uint32_t inode_bitmap_hi; // Inodes bitmap block MSB
-	uint32_t inode_table_hi; // Inodes table block MSB
+	uint32_t inode_table_first_hi; // Inodes table block MSB
 	uint16_t free_blocks_count_hi; // Free blocks count MSB
 	uint16_t free_inodes_count_hi; // Free inodes count MSB
@@ -58,5 +59,15 @@
 	uint16_t itable_unused_hi;  // Unused inodes count MSB
 	uint32_t reserved2[3]; // Padding
-} ext4_group_desc_t;
+} ext4_block_group_t;
+
+typedef struct ext4_block_group_ref {
+	block_t *block; // Reference to a block containing this block group descr
+	ext4_block_group_t *block_group;
+} ext4_block_group_ref_t;
+
+// TODO check value
+#define EXT4_BLOCK_GROUP_DESCRIPTOR_SIZE 32
+
+extern uint64_t ext4_block_group_get_inode_table_first_block(ext4_block_group_t *);
 
 #endif
Index: uspace/lib/ext4/libext4_directory.c
===================================================================
--- uspace/lib/ext4/libext4_directory.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_directory.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -36,5 +36,5 @@
  */
 
-#include "libext4_directory.h"
+#include "libext4.h"
 
 
Index: uspace/lib/ext4/libext4_filesystem.c
===================================================================
--- uspace/lib/ext4/libext4_filesystem.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_filesystem.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -38,5 +38,5 @@
 #include <errno.h>
 #include <malloc.h>
-#include "libext4_filesystem.h"
+#include "libext4.h"
 
 /**
@@ -83,4 +83,13 @@
 
 	return EOK;
+}
+
+/**
+ * TODO doxy
+ */
+void ext4_filesystem_fini(ext4_filesystem_t *fs)
+{
+	free(fs->superblock);
+	block_fini(fs->device);
 }
 
@@ -132,10 +141,107 @@
  * TODO doxy
  */
-void ext4_filesystem_fini(ext4_filesystem_t *fs)
-{
-	free(fs->superblock);
-	block_fini(fs->device);
-}
-
+int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
+    ext4_block_group_ref_t **ref)
+{
+	int rc;
+	aoff64_t block_id;
+	uint32_t descriptors_per_block;
+	size_t offset;
+	ext4_block_group_ref_t *newref;
+
+	newref = malloc(sizeof(ext4_block_group_ref_t));
+	if (newref == NULL) {
+		return ENOMEM;
+	}
+
+	descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
+	    / EXT4_BLOCK_GROUP_DESCRIPTOR_SIZE;
+
+	/* Block group descriptor table starts at the next block after superblock */
+	block_id = ext4_superblock_get_first_block(fs->superblock) + 1;
+
+	/* Find the block containing the descriptor we are looking for */
+	block_id += bgid / descriptors_per_block;
+	offset = (bgid % descriptors_per_block) * EXT4_BLOCK_GROUP_DESCRIPTOR_SIZE;
+
+	rc = block_get(&newref->block, fs->device, block_id, 0);
+	if (rc != EOK) {
+		free(newref);
+		return rc;
+	}
+
+	newref->block_group = newref->block->data + offset;
+
+	*ref = newref;
+
+	return EOK;
+}
+
+/**
+ * TODO doxy
+ */
+int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
+    ext4_inode_ref_t **ref)
+{
+	int rc;
+	aoff64_t block_id;
+	uint32_t block_group;
+	uint32_t offset_in_group;
+	uint32_t byte_offset_in_group;
+	size_t offset_in_block;
+	uint32_t inodes_per_group;
+	uint32_t inode_table_start;
+	uint16_t inode_size;
+	uint32_t block_size;
+	ext4_block_group_ref_t *bg_ref;
+	ext4_inode_ref_t *newref;
+
+	newref = malloc(sizeof(ext4_inode_ref_t));
+	if (newref == NULL) {
+		return ENOMEM;
+	}
+
+	inodes_per_group = ext4_superblock_get_inodes_per_group(fs->superblock);
+
+	/* inode numbers are 1-based, but it is simpler to work with 0-based
+	 * when computing indices
+	 */
+	index -= 1;
+	block_group = index / inodes_per_group;
+	offset_in_group = index % inodes_per_group;
+
+	rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
+	if (rc != EOK) {
+		free(newref);
+		return rc;
+	}
+
+	inode_table_start = ext4_block_group_get_inode_table_first_block(
+	    bg_ref->block_group);
+
+	inode_size = ext4_superblock_get_inode_size(fs->superblock);
+	block_size = ext4_superblock_get_block_size(fs->superblock);
+
+	byte_offset_in_group = offset_in_group * inode_size;
+
+	block_id = inode_table_start + (byte_offset_in_group / block_size);
+	offset_in_block = byte_offset_in_group % block_size;
+
+	rc = block_get(&newref->block, fs->device, block_id, 0);
+	if (rc != EOK) {
+		free(newref);
+		return rc;
+	}
+
+	newref->inode = newref->block->data + offset_in_block;
+	/* we decremented index above, but need to store the original value
+	 * in the reference
+	 */
+	newref->index = index+1;
+
+	*ref = newref;
+
+	return EOK;
+}
 
 /**
Index: uspace/lib/ext4/libext4_filesystem.h
===================================================================
--- uspace/lib/ext4/libext4_filesystem.h	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_filesystem.h	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -35,4 +35,6 @@
 
 #include <libblock.h>
+#include "libext4_block_group.h"
+#include "libext4_inode.h"
 #include "libext4_superblock.h"
 
@@ -42,6 +44,6 @@
 } ext4_filesystem_t;
 
-#define EXT4_MAX_BLOCK_SIZE 65536 //64 KiB
-
+#define EXT4_MAX_BLOCK_SIZE 	65536 //64 KiB
+#define EXT4_REV0_INODE_SIZE	128
 
 /* Compatible features */
@@ -99,7 +101,12 @@
 
 extern int ext4_filesystem_init(ext4_filesystem_t *, service_id_t);
+extern void ext4_filesystem_fini(ext4_filesystem_t *fs);
 extern int ext4_filesystem_check_sanity(ext4_filesystem_t *fs);
 extern int ext4_filesystem_check_features(ext4_filesystem_t *, bool *);
-extern void ext4_filesystem_fini(ext4_filesystem_t *fs);
+extern int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *, uint32_t,
+    ext4_block_group_ref_t **);
+extern int ext4_filesystem_get_inode_ref(ext4_filesystem_t *, uint32_t,
+		ext4_inode_ref_t **);
+
 
 #endif
Index: uspace/lib/ext4/libext4_inode.c
===================================================================
--- uspace/lib/ext4/libext4_inode.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_inode.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -37,5 +37,5 @@
 
 #include <byteorder.h>
-#include "libext4_inode.h"
+#include "libext4.h"
 
 // TODO check return type
Index: uspace/lib/ext4/libext4_superblock.c
===================================================================
--- uspace/lib/ext4/libext4_superblock.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_superblock.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -40,5 +40,5 @@
 #include <libblock.h>
 #include <malloc.h>
-#include "libext4_superblock.h"
+#include "libext4.h"
 
 /**
@@ -80,4 +80,23 @@
 {
 	return uint32_t_le2host(sb->rev_level);
+}
+
+/**
+ * TODO doxy
+ */
+uint16_t ext4_superblock_get_inode_size(ext4_superblock_t *sb)
+{
+	if (ext4_superblock_get_rev_level(sb) == 0) {
+		return EXT4_REV0_INODE_SIZE;
+	}
+	return uint16_t_le2host(sb->inode_size);
+}
+
+/**
+ * TODO doxy
+ */
+uint32_t ext4_superblock_get_inodes_per_group(ext4_superblock_t *sb)
+{
+	return uint32_t_le2host(sb->inodes_per_group);
 }
 
Index: uspace/lib/ext4/libext4_superblock.h
===================================================================
--- uspace/lib/ext4/libext4_superblock.h	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/lib/ext4/libext4_superblock.h	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -51,5 +51,5 @@
 	uint32_t s_blocks_per_group; // Number of blocks per group
 	uint32_t s_obso_frags_per_group; // Obsoleted fragments per group
-	uint32_t s_inodes_per_group; // Number of inodes per group
+	uint32_t inodes_per_group; // Number of inodes per group
 	uint32_t s_mtime; // Mount time
 	uint32_t s_wtime; // Write time
@@ -69,5 +69,5 @@
 	// Fields for EXT4_DYNAMIC_REV superblocks only.
 	uint32_t s_first_ino; // First non-reserved inode
-	uint16_t s_inode_size; // Size of inode structure
+	uint16_t inode_size; // Size of inode structure
 	uint16_t s_block_group_nr; // Block group number of this superblock
 	uint32_t features_compatible; // Compatible feature set
@@ -147,4 +147,6 @@
 extern uint32_t ext4_superblock_get_block_size(ext4_superblock_t *);
 extern uint32_t	ext4_superblock_get_rev_level(ext4_superblock_t *);
+extern uint16_t	ext4_superblock_get_inode_size(ext4_superblock_t *);
+extern uint32_t	ext4_superblock_get_inodes_per_group(ext4_superblock_t *);
 extern uint32_t	ext4_superblock_get_features_compatible(ext4_superblock_t *);
 extern uint32_t	ext4_superblock_get_features_incompatible(ext4_superblock_t *);
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision cfa1a8a78358769cc6887ad85112d4e22b218b83)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 3711e7ecc46c08bafffe8145e4284827e7a1deb0)
@@ -42,4 +42,5 @@
 #include <malloc.h>
 #include <stdio.h>
+#include <adt/hash_table.h>
 #include <ipc/loc.h>
 #include "ext4fs.h"
@@ -48,4 +49,9 @@
 #define EXT4FS_NODE(node)	((node) ? (ext4fs_node_t *) (node)->data : NULL)
 #define EXT4FS_DBG(format, ...) {if (true) printf("ext4fs: %s: " format "\n", __FUNCTION__, ##__VA_ARGS__);}
+
+#define OPEN_NODES_KEYS 2
+#define OPEN_NODES_DEV_HANDLE_KEY 0
+#define OPEN_NODES_INODE_KEY 1
+#define OPEN_NODES_BUCKETS 256
 
 typedef struct ext4fs_instance {
@@ -96,22 +102,55 @@
 static LIST_INITIALIZE(instance_list);
 static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
+static hash_table_t open_nodes;
 static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
 
-
-/**
- *	TODO doxy
- */
+/* 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)
+{
+	ext4fs_node_t *enode = hash_table_get_instance(item, ext4fs_node_t, link);
+	assert(keys > 0);
+	if (enode->instance->service_id !=
+	    ((service_id_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
+		return false;
+	}
+	if (keys == 1) {
+		return true;
+	}
+	assert(keys == 2);
+	return (enode->inode_ref->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 ext4fs_global_init(void)
 {
-	// TODO
-	return EOK;
-}
-
-/**
- * TODO doxy
- */
+	if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
+	    OPEN_NODES_KEYS, &open_nodes_ops)) {
+		return ENOMEM;
+	}
+	return EOK;
+}
+
+
 int ext4fs_global_fini(void)
 {
-	// TODO
+	hash_table_destroy(&open_nodes);
 	return EOK;
 }
@@ -122,12 +161,13 @@
  */
 
-/**
- * TODO doxy
- */
 int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
 {
+	EXT4FS_DBG("");
+
 	ext4fs_instance_t *tmp;
 
 	fibril_mutex_lock(&instance_list_mutex);
+
+	EXT4FS_DBG("Checking lists");
 
 	if (list_empty(&instance_list)) {
@@ -135,4 +175,6 @@
 		return EINVAL;
 	}
+
+	EXT4FS_DBG("checked");
 
 	list_foreach(instance_list, link) {
@@ -146,11 +188,11 @@
 	}
 
+	EXT4FS_DBG("Not found");
+
 	fibril_mutex_unlock(&instance_list_mutex);
 	return EINVAL;
 }
 
-/**
- * TODO doxy
- */
+
 int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
 {
@@ -158,7 +200,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 {
@@ -167,7 +207,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
 {
@@ -183,17 +221,73 @@
 }
 
-/**
- * TODO doxy
- */
+
 int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
 		fs_index_t index)
 {
-	// TODO
-	return EOK;
-}
-
-/**
- * TODO doxy
- */
+
+	int rc;
+	fs_node_t *node = NULL;
+	ext4fs_node_t *enode = NULL;
+
+	ext4_inode_ref_t *inode_ref = NULL;
+
+	fibril_mutex_lock(&open_nodes_lock);
+
+	/* Check if the node is not already open */
+	unsigned long key[] = {
+		[OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
+		[OPEN_NODES_INODE_KEY] = index,
+	};
+	link_t *already_open = hash_table_find(&open_nodes, key);
+
+	if (already_open) {
+		enode = hash_table_get_instance(already_open, ext4fs_node_t, link);
+		*rfn = enode->fs_node;
+		enode->references++;
+
+		fibril_mutex_unlock(&open_nodes_lock);
+		return EOK;
+	}
+
+	enode = malloc(sizeof(ext4fs_node_t));
+	if (enode == NULL) {
+		fibril_mutex_unlock(&open_nodes_lock);
+		return ENOMEM;
+	}
+
+	node = malloc(sizeof(fs_node_t));
+	if (node == NULL) {
+		free(enode);
+		fibril_mutex_unlock(&open_nodes_lock);
+		return ENOMEM;
+	}
+	fs_node_initialize(node);
+
+	rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
+	if (rc != EOK) {
+		free(enode);
+		free(node);
+		fibril_mutex_unlock(&open_nodes_lock);
+		return rc;
+	}
+
+	enode->inode_ref = inode_ref;
+	enode->instance = inst;
+	enode->references = 1;
+	enode->fs_node = node;
+	link_initialize(&enode->link);
+
+	node->data = enode;
+	*rfn = node;
+
+	hash_table_insert(&open_nodes, key, &enode->link);
+	inst->open_nodes_count++;
+
+	fibril_mutex_unlock(&open_nodes_lock);
+
+	return EOK;
+}
+
+
 int ext4fs_node_put_core(ext4fs_node_t *enode) {
 	// TODO
@@ -201,7 +295,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 int ext4fs_node_open(fs_node_t *fn)
 {
@@ -233,4 +325,5 @@
 }
 
+
 int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
 {
@@ -239,4 +332,5 @@
 }
 
+
 int ext4fs_destroy_node(fs_node_t *fn)
 {
@@ -245,4 +339,5 @@
 }
 
+
 int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
 {
@@ -251,4 +346,5 @@
 }
 
+
 int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
 {
@@ -257,4 +353,5 @@
 }
 
+
 int ext4fs_has_children(bool *has_children, fs_node_t *fn)
 {
@@ -270,4 +367,5 @@
 }
 
+
 aoff64_t ext4fs_size_get(fs_node_t *fn)
 {
@@ -276,4 +374,5 @@
 }
 
+
 unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
 {
@@ -282,4 +381,5 @@
 }
 
+
 bool ext4fs_is_directory(fs_node_t *fn)
 {
@@ -288,4 +388,5 @@
 }
 
+
 bool ext4fs_is_file(fs_node_t *fn)
 {
@@ -293,4 +394,5 @@
 	return false;
 }
+
 
 service_id_t ext4fs_service_get(fs_node_t *fn)
@@ -327,12 +429,7 @@
  */
 
-/**
- * TODO doxy
- */
 static int ext4fs_mounted(service_id_t service_id, const char *opts,
    fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
 {
-
-	EXT4FS_DBG("Mounting...");
 
 	int rc;
@@ -353,6 +450,4 @@
 		return ENOMEM;
 	}
-
-	EXT4FS_DBG("Basic structures allocated");
 
 	/* Initialize the filesystem */
@@ -363,6 +458,4 @@
 		return rc;
 	}
-
-	EXT4FS_DBG("initialized");
 
 	/* Do some sanity checking */
@@ -375,6 +468,4 @@
 	}
 
-	EXT4FS_DBG("Checked and clean");
-
 	/* Check flags */
 	rc = ext4_filesystem_check_features(fs, &read_only);
@@ -386,6 +477,4 @@
 	}
 
-	EXT4FS_DBG("Features checked");
-
 	/* Initialize instance */
 	link_initialize(&inst->link);
@@ -394,9 +483,7 @@
 	inst->open_nodes_count = 0;
 
-	EXT4FS_DBG("Instance initialized");
-
 	/* Read root node */
 	fs_node_t *root_node;
-	rc = ext4fs_root_get(&root_node, inst->service_id);
+	rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
 	if (rc != EOK) {
 		ext4_filesystem_fini(fs);
@@ -407,6 +494,4 @@
 	ext4fs_node_t *enode = EXT4FS_NODE(root_node);
 
-	EXT4FS_DBG("Root node found");
-
 	/* Add instance to the list */
 	fibril_mutex_lock(&instance_list_mutex);
@@ -414,22 +499,14 @@
 	fibril_mutex_unlock(&instance_list_mutex);
 
-	EXT4FS_DBG("Instance added");
-
 	*index = EXT4_INODE_ROOT_INDEX;
 	*size = 0;
 	*lnkcnt = ext4_inode_get_usage_count(enode->inode_ref->inode);
 
-	EXT4FS_DBG("Return values set");
-
 	ext4fs_node_put(root_node);
 
-	EXT4FS_DBG("Mounting finished");
-
-	return EOK;
-}
-
-/**
- * TODO doxy
- */
+	return EOK;
+}
+
+
 static int ext4fs_unmounted(service_id_t service_id)
 {
@@ -463,7 +540,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int
 ext4fs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
@@ -474,7 +549,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int
 ext4fs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
@@ -485,7 +558,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int
 ext4fs_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
@@ -495,7 +566,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int ext4fs_close(service_id_t service_id, fs_index_t index)
 {
@@ -504,7 +573,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
 {
@@ -513,7 +580,5 @@
 }
 
-/**
- * TODO doxy
- */
+
 static int ext4fs_sync(service_id_t service_id, fs_index_t index)
 {
