Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ boot/Makefile.common	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -75,5 +75,8 @@
 	$(USPACE_PATH)/srv/loc/loc \
 	$(USPACE_PATH)/srv/bd/rd/rd \
-	$(USPACE_PATH)/srv/vfs/vfs
+	$(USPACE_PATH)/srv/vfs/vfs \
+	
+# TODO: For comfort ext4fs debugging - delete before merge	
+INIT_TASKS += $(USPACE_PATH)/srv/bd/ata_bd/ata_bd
 
 ifeq ($(RDFMT),tmpfs)
Index: uspace/lib/ext4/libext4.h
===================================================================
--- uspace/lib/ext4/libext4.h	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4.h	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -40,4 +40,7 @@
 #include "libext4_superblock.h"
 
+#include <stdio.h>
+#define EXT4FS_DBG(format, ...) {if (true) printf("ext4fs: %s: " format "\n", __FUNCTION__, ##__VA_ARGS__);}
+
 #endif
 
Index: uspace/lib/ext4/libext4_directory.c
===================================================================
--- uspace/lib/ext4/libext4_directory.c	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_directory.c	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -36,5 +36,175 @@
  */
 
+#include <byteorder.h>
+#include <errno.h>
 #include "libext4.h"
+
+static int ext4_directory_iterator_set(ext4_directory_iterator_t *,
+    uint32_t);
+
+
+uint32_t ext4_directory_entry_ll_get_inode(ext4_directory_entry_ll_t *de)
+{
+	return uint32_t_le2host(de->inode);
+}
+
+uint16_t ext4_directory_entry_ll_get_entry_length(
+    ext4_directory_entry_ll_t *de)
+{
+	return uint16_t_le2host(de->entry_length);
+}
+
+uint16_t ext4_directory_entry_ll_get_name_length(
+    ext4_superblock_t *sb, ext4_directory_entry_ll_t *de)
+{
+	if (ext4_superblock_get_rev_level(sb) == 0 &&
+	    ext4_superblock_get_minor_rev_level(sb) < 5) {
+		return ((uint16_t)de->name_length_high) << 8 |
+		    ((uint16_t)de->name_length);
+	}
+	return de->name_length;
+}
+
+int ext4_directory_iterator_init(ext4_directory_iterator_t *it,
+    ext4_filesystem_t *fs, ext4_inode_ref_t *inode_ref, aoff64_t pos)
+{
+	it->inode_ref = inode_ref;
+	it->fs = fs;
+	it->current = NULL;
+	it->current_offset = 0;
+	it->current_block = NULL;
+
+	return ext4_directory_iterator_seek(it, pos);
+}
+
+
+int ext4_directory_iterator_next(ext4_directory_iterator_t *it)
+{
+	uint16_t skip;
+
+	assert(it->current != NULL);
+
+	skip = ext4_directory_entry_ll_get_entry_length(it->current);
+
+	return ext4_directory_iterator_seek(it, it->current_offset + skip);
+}
+
+
+int ext4_directory_iterator_seek(ext4_directory_iterator_t *it, aoff64_t pos)
+{
+	int rc;
+
+	uint64_t size;
+	aoff64_t current_block_idx;
+	aoff64_t next_block_idx;
+	uint32_t next_block_phys_idx;
+	uint32_t block_size;
+
+	size = ext4_inode_get_size(it->fs->superblock, it->inode_ref->inode);
+
+	/* The iterator is not valid until we seek to the desired position */
+	it->current = NULL;
+
+	/* Are we at the end? */
+	if (pos >= size) {
+		if (it->current_block) {
+			rc = block_put(it->current_block);
+			it->current_block = NULL;
+			if (rc != EOK) {
+				return rc;
+			}
+		}
+
+		it->current_offset = pos;
+		return EOK;
+	}
+
+	block_size = ext4_superblock_get_block_size(it->fs->superblock);
+	current_block_idx = it->current_offset / block_size;
+	next_block_idx = pos / block_size;
+
+	/* If we don't have a block or are moving accross block boundary,
+	 * we need to get another block
+	 */
+	if (it->current_block == NULL || current_block_idx != next_block_idx) {
+		if (it->current_block) {
+			rc = block_put(it->current_block);
+			it->current_block = NULL;
+			if (rc != EOK) {
+				return rc;
+			}
+		}
+
+		rc = ext4_filesystem_get_inode_data_block_index(it->fs,
+		    it->inode_ref->inode, next_block_idx, &next_block_phys_idx);
+		if (rc != EOK) {
+			return rc;
+		}
+
+		rc = block_get(&it->current_block, it->fs->device, next_block_phys_idx,
+		    BLOCK_FLAGS_NONE);
+		if (rc != EOK) {
+			it->current_block = NULL;
+			return rc;
+		}
+	}
+
+	it->current_offset = pos;
+	return ext4_directory_iterator_set(it, block_size);
+}
+
+static int ext4_directory_iterator_set(ext4_directory_iterator_t *it,
+    uint32_t block_size)
+{
+	uint32_t offset_in_block = it->current_offset % block_size;
+
+	it->current = NULL;
+
+	/* Ensure proper alignment */
+	if ((offset_in_block % 4) != 0) {
+		return EIO;
+	}
+
+	/* Ensure that the core of the entry does not overflow the block */
+	if (offset_in_block > block_size - 8) {
+		return EIO;
+	}
+
+	ext4_directory_entry_ll_t *entry = it->current_block->data + offset_in_block;
+
+	/* Ensure that the whole entry does not overflow the block */
+	uint16_t length = ext4_directory_entry_ll_get_entry_length(entry);
+	if (offset_in_block + length > block_size) {
+		return EIO;
+	}
+
+	/* Ensure the name length is not too large */
+	if (ext4_directory_entry_ll_get_name_length(it->fs->superblock,
+	    entry) > length-8) {
+		return EIO;
+	}
+
+	it->current = entry;
+	return EOK;
+}
+
+
+int ext4_directory_iterator_fini(ext4_directory_iterator_t *it)
+{
+	int rc;
+
+	it->fs = NULL;
+	it->inode_ref = NULL;
+	it->current = NULL;
+
+	if (it->current_block) {
+		rc = block_put(it->current_block);
+		if (rc != EOK) {
+			return rc;
+		}
+	}
+
+	return EOK;
+}
 
 
Index: uspace/lib/ext4/libext4_directory.h
===================================================================
--- uspace/lib/ext4/libext4_directory.h	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_directory.h	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -34,4 +34,40 @@
 #define LIBEXT4_LIBEXT4_DIRECTORY_H_
 
+#include "libext4_filesystem.h"
+#include "libext4_inode.h"
+
+/**
+ * Linked list directory entry structure
+ */
+typedef struct ext4_directory_entry_ll {
+	uint32_t inode; // Inode for the entry
+	uint16_t entry_length; // Distance to the next directory entry
+	uint8_t name_length; // Lower 8 bits of name length
+	union {
+		uint8_t name_length_high; // Higher 8 bits of name length
+		uint8_t inode_type; // Type of referenced inode (in rev >= 0.5)
+	} __attribute__ ((packed));
+	uint8_t name; // First byte of name, if present
+} __attribute__ ((packed)) ext4_directory_entry_ll_t;
+
+typedef struct ext4_directory_iterator {
+	ext4_filesystem_t *fs;
+	ext4_inode_ref_t *inode_ref;
+	block_t *current_block;
+	aoff64_t current_offset;
+	ext4_directory_entry_ll_t *current;
+} ext4_directory_iterator_t;
+
+extern uint32_t	ext4_directory_entry_ll_get_inode(ext4_directory_entry_ll_t *);
+extern uint16_t	ext4_directory_entry_ll_get_entry_length(
+    ext4_directory_entry_ll_t *);
+extern uint16_t	ext4_directory_entry_ll_get_name_length(
+    ext4_superblock_t *, ext4_directory_entry_ll_t *);
+
+extern int ext4_directory_iterator_init(ext4_directory_iterator_t *,
+		ext4_filesystem_t *, ext4_inode_ref_t *, aoff64_t);
+extern int ext4_directory_iterator_next(ext4_directory_iterator_t *);
+extern int ext4_directory_iterator_seek(ext4_directory_iterator_t *, aoff64_t pos);
+extern int ext4_directory_iterator_fini(ext4_directory_iterator_t *);
 
 #endif
Index: uspace/lib/ext4/libext4_filesystem.c
===================================================================
--- uspace/lib/ext4/libext4_filesystem.c	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_filesystem.c	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -36,4 +36,5 @@
  */
 
+#include <byteorder.h>
 #include <errno.h>
 #include <malloc.h>
@@ -144,4 +145,6 @@
     ext4_block_group_ref_t **ref)
 {
+	EXT4FS_DBG("");
+
 	int rc;
 	aoff64_t block_id;
@@ -161,17 +164,31 @@
 	block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
 
+	EXT4FS_DBG("block_size = \%d", ext4_superblock_get_block_size(fs->superblock));
+	EXT4FS_DBG("descriptors_per_block = \%d", descriptors_per_block);
+	EXT4FS_DBG("bgid = \%d", bgid);
+	EXT4FS_DBG("first_data_block: \%d", (uint32_t)block_id);
+
 	/* 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;
 
+	EXT4FS_DBG("updated block_id: \%d", (uint32_t)block_id);
+
 	rc = block_get(&newref->block, fs->device, block_id, 0);
 	if (rc != EOK) {
+
+		EXT4FS_DBG("block_get error: \%d", rc);
+
 		free(newref);
 		return rc;
 	}
 
+	EXT4FS_DBG("block read");
+
 	newref->block_group = newref->block->data + offset;
 
 	*ref = newref;
+
+	EXT4FS_DBG("finished");
 
 	return EOK;
@@ -184,4 +201,6 @@
     ext4_inode_ref_t **ref)
 {
+	EXT4FS_DBG("");
+
 	int rc;
 	aoff64_t block_id;
@@ -202,5 +221,9 @@
 	}
 
+	EXT4FS_DBG("allocated");
+
 	inodes_per_group = ext4_superblock_get_inodes_per_group(fs->superblock);
+
+	EXT4FS_DBG("inodes_per_group_loaded");
 
 	/* inode numbers are 1-based, but it is simpler to work with 0-based
@@ -211,4 +234,8 @@
 	offset_in_group = index % inodes_per_group;
 
+	EXT4FS_DBG("index: \%d", index);
+	EXT4FS_DBG("inodes_per_group: \%d", inodes_per_group);
+	EXT4FS_DBG("bg_id: \%d", block_group);
+
 	rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
 	if (rc != EOK) {
@@ -216,8 +243,12 @@
 		return rc;
 	}
+
+	EXT4FS_DBG("block_group_ref loaded");
 
 	inode_table_start = ext4_block_group_get_inode_table_first_block(
 	    bg_ref->block_group);
 
+	EXT4FS_DBG("inode_table block loaded");
+
 	inode_size = ext4_superblock_get_inode_size(fs->superblock);
 	block_size = ext4_superblock_get_block_size(fs->superblock);
@@ -228,4 +259,6 @@
 	offset_in_block = byte_offset_in_group % block_size;
 
+	EXT4FS_DBG("superblock info loaded");
+
 	rc = block_get(&newref->block, fs->device, block_id, 0);
 	if (rc != EOK) {
@@ -233,4 +266,6 @@
 		return rc;
 	}
+
+	EXT4FS_DBG("block got");
 
 	newref->inode = newref->block->data + offset_in_block;
@@ -242,4 +277,109 @@
 	*ref = newref;
 
+	EXT4FS_DBG("finished");
+
+	return EOK;
+}
+
+int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
+{
+	int rc;
+
+	rc = block_put(ref->block);
+	free(ref);
+
+	return rc;
+}
+
+int ext4_filesystem_get_inode_data_block_index(ext4_filesystem_t *fs, ext4_inode_t* inode,
+    aoff64_t iblock, uint32_t* fblock)
+{
+	int rc;
+	aoff64_t limits[4];
+	uint32_t block_ids_per_block;
+	aoff64_t blocks_per_level[4];
+	uint32_t offset_in_block;
+	uint32_t current_block;
+	aoff64_t block_offset_in_level;
+	int i;
+	int level;
+	block_t *block;
+
+	/* Handle simple case when we are dealing with direct reference */
+	if (iblock < EXT4_INODE_DIRECT_BLOCKS) {
+		current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
+		*fblock = current_block;
+		return EOK;
+	}
+
+	/* Compute limits for indirect block levels
+	 * TODO: compute this once when loading filesystem and store in ext2_filesystem_t
+	 */
+	block_ids_per_block = ext4_superblock_get_block_size(fs->superblock) / sizeof(uint32_t);
+	limits[0] = EXT4_INODE_DIRECT_BLOCKS;
+	blocks_per_level[0] = 1;
+	for (i = 1; i < 4; i++) {
+		blocks_per_level[i]  = blocks_per_level[i-1] *
+		    block_ids_per_block;
+		limits[i] = limits[i-1] + blocks_per_level[i];
+	}
+
+	/* Determine the indirection level needed to get the desired block */
+	level = -1;
+	for (i = 1; i < 4; i++) {
+		if (iblock < limits[i]) {
+			level = i;
+			break;
+		}
+	}
+
+	if (level == -1) {
+		return EIO;
+	}
+
+	/* Compute offsets for the topmost level */
+	block_offset_in_level = iblock - limits[level-1];
+	current_block = ext4_inode_get_indirect_block(inode, level-1);
+	offset_in_block = block_offset_in_level / blocks_per_level[level-1];
+
+	/* Navigate through other levels, until we find the block number
+	 * or find null reference meaning we are dealing with sparse file
+	 */
+	while (level > 0) {
+		rc = block_get(&block, fs->device, current_block, 0);
+		if (rc != EOK) {
+			return rc;
+		}
+
+		assert(offset_in_block < block_ids_per_block);
+		current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
+
+		rc = block_put(block);
+		if (rc != EOK) {
+			return rc;
+		}
+
+		if (current_block == 0) {
+			/* This is a sparse file */
+			*fblock = 0;
+			return EOK;
+		}
+
+		level -= 1;
+
+		/* If we are on the last level, break here as
+		 * there is no next level to visit
+		 */
+		if (level == 0) {
+			break;
+		}
+
+		/* Visit the next level */
+		block_offset_in_level %= blocks_per_level[level];
+		offset_in_block = block_offset_in_level / blocks_per_level[level-1];
+	}
+
+	*fblock = current_block;
+
 	return EOK;
 }
Index: uspace/lib/ext4/libext4_filesystem.h
===================================================================
--- uspace/lib/ext4/libext4_filesystem.h	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_filesystem.h	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -108,5 +108,7 @@
 extern int ext4_filesystem_get_inode_ref(ext4_filesystem_t *, uint32_t,
 		ext4_inode_ref_t **);
-
+extern int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *);
+extern int ext4_filesystem_get_inode_data_block_index(ext4_filesystem_t *,
+	ext4_inode_t *, aoff64_t iblock, uint32_t *);
 
 #endif
Index: uspace/lib/ext4/libext4_inode.c
===================================================================
--- uspace/lib/ext4/libext4_inode.c	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_inode.c	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -39,13 +39,32 @@
 #include "libext4.h"
 
+uint32_t ext4_inode_get_mode(ext4_superblock_t *sb, ext4_inode_t *inode)
+{
+	if (ext4_superblock_get_creator_os(sb) == EXT4_SUPERBLOCK_OS_HURD) {
+		return ((uint32_t)uint16_t_le2host(inode->osd2.hurd2.mode_high)) << 16 |
+		    ((uint32_t)uint16_t_le2host(inode->mode));
+	}
+	return uint16_t_le2host(inode->mode);
+}
+
+bool ext4_inode_is_type(ext4_superblock_t *sb, ext4_inode_t *inode, uint32_t type)
+{
+	uint32_t mode = ext4_inode_get_mode(sb, inode);
+	return (mode & EXT4_INODE_MODE_TYPE_MASK) == type;
+}
+
 /*
-uint32_t ext4_inode_get_mode(ext4_inode_t *inode)
 uint32_t ext4_inode_get_uid(ext4_inode_t *inode)
 */
 
-uint64_t ext4_inode_get_size(ext4_inode_t *inode)
+uint64_t ext4_inode_get_size(ext4_superblock_t *sb, ext4_inode_t *inode)
 {
-	return ((uint64_t)uint32_t_le2host(inode->size_hi)) << 32 |
-			((uint64_t)uint32_t_le2host(inode->size_lo));
+	uint32_t major_rev = ext4_superblock_get_rev_level(sb);
+
+	if (major_rev > 0 && ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) {
+		return ((uint64_t)uint32_t_le2host(inode->size_hi)) << 32 |
+			    ((uint64_t)uint32_t_le2host(inode->size_lo));
+		}
+	return uint32_t_le2host(inode->size_lo);
 }
 
@@ -68,4 +87,16 @@
 */
 
+uint32_t ext4_inode_get_direct_block(ext4_inode_t *inode, uint8_t idx)
+{
+	assert(idx < EXT4_INODE_DIRECT_BLOCK_COUNT);
+	return uint32_t_le2host(inode->blocks[idx]);
+}
+
+uint32_t ext4_inode_get_indirect_block(ext4_inode_t *inode, uint8_t idx)
+{
+	assert(idx < EXT4_INODE_INDIRECT_BLOCK_COUNT);
+	return uint32_t_le2host(inode->blocks[idx + EXT4_INODE_INDIRECT_BLOCK]);
+}
+
 /**
  * @}
Index: uspace/lib/ext4/libext4_inode.h
===================================================================
--- uspace/lib/ext4/libext4_inode.h	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_inode.h	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -36,10 +36,13 @@
 #include <libblock.h>
 #include <sys/types.h>
+#include "libext4_superblock.h"
 
-#define EXT4_DIRECT_BLOCK_COUNT		12
-#define EXT4_INDIRECT_BLOCK 		EXT4_DIRECT_BLOCK_COUNT
-#define EXT4_DOUBLE_INDIRECT_BLOCK	(EXT4_INDIRECT_BLOCK + 1)
-#define EXT4_TRIPPLE_INDIRECT_BLOCK	(EXT4_DOUBLE_INDIRECT_BLOCK + 1)
-#define EXT4_INODE_BLOCKS			(EXT4_TRIPPLE_INDIRECT_BLOCK + 1)
+
+#define EXT4_INODE_DIRECT_BLOCK_COUNT		12
+#define EXT4_INODE_INDIRECT_BLOCK 			EXT4_INODE_DIRECT_BLOCK_COUNT
+#define EXT4_INODE_DOUBLE_INDIRECT_BLOCK	(EXT4_INODE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_TRIPPLE_INDIRECT_BLOCK	(EXT4_INODE_DOUBLE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_BLOCKS					(EXT4_INODE_TRIPPLE_INDIRECT_BLOCK + 1)
+#define EXT4_INODE_INDIRECT_BLOCK_COUNT		(EXT4_INODE_BLOCKS - EXT4_INODE_DIRECT_BLOCK_COUNT)
 
 /*
@@ -64,5 +67,26 @@
     uint32_t size_hi;
     uint32_t obso_faddr; // Obsoleted fragment address
-    uint32_t unused_osd2[3]; // OS dependent - not used in HelenOS
+    union {
+    	struct {
+    		uint16_t blocks_high; /* were l_i_reserved1 */
+    		uint16_t file_acl_high;
+    		uint16_t uid_high;   /* these 2 fields */
+    		uint16_t gid_high;   /* were reserved2[0] */
+    		uint32_t reserved2;
+    	} linux2;
+    	struct {
+    		uint16_t reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
+    		uint16_t mode_high;
+    		uint16_t uid_high;
+    		uint16_t gid_high;
+    		uint32_t author;
+    	} hurd2;
+    	struct {
+    		uint16_t reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
+    		uint16_t file_acl_high;
+    		uint32_t reserved2[2];
+    	} masix2;
+    } __attribute__ ((packed)) osd2;
+
     uint16_t extra_isize;
     uint16_t pad1;
@@ -75,4 +99,12 @@
 } __attribute__ ((packed)) ext4_inode_t;
 
+#define EXT4_INODE_MODE_FIFO		0x1000
+#define EXT4_INODE_MODE_CHARDEV		0x2000
+#define EXT4_INODE_MODE_DIRECTORY	0x4000
+#define EXT4_INODE_MODE_BLOCKDEV	0x6000
+#define EXT4_INODE_MODE_FILE		0x8000
+#define EXT4_INODE_MODE_SOFTLINK	0xA000
+#define EXT4_INODE_MODE_SOCKET		0xC000
+#define EXT4_INODE_MODE_TYPE_MASK	0xF000
 
 #define EXT4_INODE_ROOT_INDEX	2
@@ -84,9 +116,11 @@
 } ext4_inode_ref_t;
 
+
+extern uint32_t ext4_inode_get_mode(ext4_superblock_t *, ext4_inode_t *);
+extern bool ext4_inode_is_type(ext4_superblock_t *, ext4_inode_t *, uint32_t);
 /*
-extern uint16_t ext4_inode_get_mode(ext4_inode_t *);
 extern uint32_t ext4_inode_get_uid(ext4_inode_t *);
 */
-extern uint64_t ext4_inode_get_size(ext4_inode_t *);
+extern uint64_t ext4_inode_get_size(ext4_superblock_t *, ext4_inode_t *);
 /*
 extern uint32_t ext4_inode_get_access_time(ext4_inode_t *);
@@ -101,4 +135,7 @@
 extern uint32_t ext4_inode_get_flags(ext4_inode_t *);
 */
+
+uint32_t ext4_inode_get_direct_block(ext4_inode_t *, uint8_t);
+uint32_t ext4_inode_get_indirect_block(ext4_inode_t *, uint8_t);
 
 /*
Index: uspace/lib/ext4/libext4_superblock.h
===================================================================
--- uspace/lib/ext4/libext4_superblock.h	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/lib/ext4/libext4_superblock.h	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -140,4 +140,7 @@
 #define EXT4_SUPERBLOCK_SIZE		1024
 #define EXT4_SUPERBLOCK_OFFSET		1024
+
+#define EXT4_SUPERBLOCK_OS_LINUX	0
+#define EXT4_SUPERBLOCK_OS_HURD		1
 
 extern uint32_t ext4_superblock_get_inodes_count(ext4_superblock_t *);
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 3712434180942faca7a9305462e63eaffa18debb)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 9b9d37bb7640f27fb362f617f494669ddd3490d3)
@@ -40,6 +40,6 @@
 #include <libext4.h>
 #include <libfs.h>
+#include <macros.h>
 #include <malloc.h>
-#include <stdio.h>
 #include <adt/hash_table.h>
 #include <ipc/loc.h>
@@ -48,5 +48,4 @@
 
 #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
@@ -73,4 +72,10 @@
  * Forward declarations of auxiliary functions
  */
+
+static int ext4fs_read_directory(ipc_callid_t, aoff64_t, size_t,
+    ext4fs_instance_t *, ext4_inode_ref_t *, size_t *);
+static int ext4fs_read_file(ipc_callid_t, aoff64_t, size_t, ext4fs_instance_t *,
+    ext4_inode_ref_t *, size_t *);
+static bool ext4fs_is_dots(const uint8_t *, size_t);
 static int ext4fs_instance_get(service_id_t, ext4fs_instance_t **);
 static int ext4fs_node_get_core(fs_node_t **, ext4fs_instance_t *, fs_index_t);
@@ -142,4 +147,6 @@
 int ext4fs_global_init(void)
 {
+	EXT4FS_DBG("");
+
 	if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
 	    OPEN_NODES_KEYS, &open_nodes_ops)) {
@@ -152,4 +159,6 @@
 int ext4fs_global_fini(void)
 {
+	EXT4FS_DBG("");
+
 	hash_table_destroy(&open_nodes);
 	return EOK;
@@ -169,12 +178,8 @@
 	fibril_mutex_lock(&instance_list_mutex);
 
-	EXT4FS_DBG("Checking lists");
-
 	if (list_empty(&instance_list)) {
 		fibril_mutex_unlock(&instance_list_mutex);
 		return EINVAL;
 	}
-
-	EXT4FS_DBG("checked");
 
 	list_foreach(instance_list, link) {
@@ -188,6 +193,4 @@
 	}
 
-	EXT4FS_DBG("Not found");
-
 	fibril_mutex_unlock(&instance_list_mutex);
 	return EINVAL;
@@ -197,4 +200,6 @@
 int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
 {
+	EXT4FS_DBG("");
+
 	return ext4fs_node_get(rfn, service_id, EXT4_INODE_ROOT_INDEX);
 }
@@ -203,5 +208,69 @@
 int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 {
-	// TODO
+	EXT4FS_DBG("");
+
+	ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
+	ext4_filesystem_t *fs;
+	ext4_directory_iterator_t it;
+	int rc;
+	size_t name_size;
+	size_t component_size;
+	bool found = false;
+	uint32_t inode;
+
+	fs = eparent->instance->filesystem;
+
+	if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
+	    EXT4_INODE_MODE_DIRECTORY)) {
+		return ENOTDIR;
+	}
+
+	rc = ext4_directory_iterator_init(&it, fs, eparent->inode_ref, 0);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Find length of component in bytes
+	 * TODO: check for library function call that does this
+	 */
+	component_size = 0;
+	while (*(component+component_size) != 0) {
+		component_size++;
+	}
+
+	while (it.current != NULL) {
+		inode = ext4_directory_entry_ll_get_inode(it.current);
+
+		/* ignore empty directory entries */
+		if (inode != 0) {
+			name_size = ext4_directory_entry_ll_get_name_length(fs->superblock,
+				it.current);
+
+			if (name_size == component_size && bcmp(component, &it.current->name,
+				    name_size) == 0) {
+				rc = ext4fs_node_get_core(rfn, eparent->instance,
+					inode);
+				if (rc != EOK) {
+					ext4_directory_iterator_fini(&it);
+					return rc;
+				}
+				found = true;
+				break;
+			}
+		}
+
+		rc = ext4_directory_iterator_next(&it);
+		if (rc != EOK) {
+			ext4_directory_iterator_fini(&it);
+			return rc;
+		}
+	}
+
+	ext4_directory_iterator_fini(&it);
+
+	if (!found) {
+		return ENOENT;
+	}
+
 	return EOK;
 }
@@ -225,5 +294,4 @@
 		fs_index_t index)
 {
-
 	int rc;
 	fs_node_t *node = NULL;
@@ -290,6 +358,24 @@
 
 
-int ext4fs_node_put_core(ext4fs_node_t *enode) {
-	// TODO
+int ext4fs_node_put_core(ext4fs_node_t *enode)
+{
+	int rc;
+	unsigned long key[] = {
+		[OPEN_NODES_DEV_HANDLE_KEY] = enode->instance->service_id,
+		[OPEN_NODES_INODE_KEY] = enode->inode_ref->index,
+	};
+
+	hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
+	assert(enode->instance->open_nodes_count > 0);
+	enode->instance->open_nodes_count--;
+
+	rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	free(enode->fs_node);
+	free(enode);
+
 	return EOK;
 }
@@ -304,5 +390,4 @@
 int ext4fs_node_put(fs_node_t *fn)
 {
-	EXT4FS_DBG("");
 	int rc;
 	ext4fs_node_t *enode = EXT4FS_NODE(fn);
@@ -328,4 +413,6 @@
 int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return ENOTSUP;
@@ -335,4 +422,6 @@
 int ext4fs_destroy_node(fs_node_t *fn)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return ENOTSUP;
@@ -342,4 +431,6 @@
 int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return ENOTSUP;
@@ -349,4 +440,6 @@
 int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return ENOTSUP;
@@ -356,4 +449,6 @@
 int ext4fs_has_children(bool *has_children, fs_node_t *fn)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return EOK;
@@ -363,6 +458,6 @@
 fs_index_t ext4fs_index_get(fs_node_t *fn)
 {
-	// TODO
-	return 0;
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	return enode->inode_ref->index;
 }
 
@@ -370,6 +465,8 @@
 aoff64_t ext4fs_size_get(fs_node_t *fn)
 {
-	// TODO
-	return 0;
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	aoff64_t size = ext4_inode_get_size(enode->instance->filesystem->superblock,
+	    enode->inode_ref->inode);
+	return size;
 }
 
@@ -377,6 +474,7 @@
 unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
 {
-	// TODO
-	return 0;
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	unsigned count = ext4_inode_get_links_count(enode->inode_ref->inode);
+	return count;
 }
 
@@ -384,4 +482,6 @@
 bool ext4fs_is_directory(fs_node_t *fn)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return false;
@@ -391,6 +491,8 @@
 bool ext4fs_is_file(fs_node_t *fn)
 {
-	// TODO
-	return false;
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	bool is_file = ext4_inode_is_type(enode->instance->filesystem->superblock,
+	    enode->inode_ref->inode, EXT4_INODE_MODE_FILE);
+	return is_file;
 }
 
@@ -398,4 +500,6 @@
 service_id_t ext4fs_service_get(fs_node_t *fn)
 {
+	EXT4FS_DBG("");
+
 	// TODO
 	return 0;
@@ -432,4 +536,5 @@
    fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
 {
+	EXT4FS_DBG("");
 
 	int rc;
@@ -511,4 +616,5 @@
 static int ext4fs_unmounted(service_id_t service_id)
 {
+	EXT4FS_DBG("");
 
 	int rc;
@@ -545,8 +651,226 @@
     size_t *rbytes)
 {
-	// TODO
-	return 0;
-}
-
+	EXT4FS_DBG("");
+
+	ext4fs_instance_t *inst;
+	ext4_inode_ref_t *inode_ref;
+	int rc;
+
+	/*
+	 * Receive the read request.
+	 */
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+
+	rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
+			EXT4_INODE_MODE_FILE)) {
+		rc = ext4fs_read_file(callid, pos, size, inst, inode_ref,
+				rbytes);
+	} else if (ext4_inode_is_type(inst->filesystem->superblock,
+			inode_ref->inode, EXT4_INODE_MODE_DIRECTORY)) {
+		rc = ext4fs_read_directory(callid, pos, size, inst, inode_ref,
+				rbytes);
+	} else {
+		/* Other inode types not supported */
+		async_answer_0(callid, ENOTSUP);
+		rc = ENOTSUP;
+	}
+
+	ext4_filesystem_put_inode_ref(inode_ref);
+	return rc;
+}
+
+bool ext4fs_is_dots(const uint8_t *name, size_t name_size) {
+	if (name_size == 1 && name[0] == '.') {
+		return true;
+	}
+
+	if (name_size == 2 && name[0] == '.' && name[1] == '.') {
+		return true;
+	}
+
+	return false;
+}
+
+int ext4fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
+    ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
+{
+	EXT4FS_DBG("");
+
+	ext4_directory_iterator_t it;
+	aoff64_t next;
+	uint8_t *buf;
+	size_t name_size;
+	int rc;
+	bool found = false;
+
+	rc = ext4_directory_iterator_init(&it, inst->filesystem, inode_ref, pos);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	/* Find next interesting directory entry.
+	 * We want to skip . and .. entries
+	 * as these are not used in HelenOS
+	 */
+	while (it.current != NULL) {
+		if (it.current->inode == 0) {
+			goto skip;
+		}
+
+		name_size = ext4_directory_entry_ll_get_name_length(
+		    inst->filesystem->superblock, it.current);
+
+		/* skip . and .. */
+		if (ext4fs_is_dots(&it.current->name, name_size)) {
+			goto skip;
+		}
+
+		/* The on-disk entry does not contain \0 at the end
+		 * end of entry name, so we copy it to new buffer
+		 * and add the \0 at the end
+		 */
+		buf = malloc(name_size+1);
+		if (buf == NULL) {
+			ext4_directory_iterator_fini(&it);
+			async_answer_0(callid, ENOMEM);
+			return ENOMEM;
+		}
+		memcpy(buf, &it.current->name, name_size);
+		*(buf + name_size) = 0;
+		found = true;
+		(void) async_data_read_finalize(callid, buf, name_size + 1);
+		free(buf);
+		break;
+
+skip:
+		rc = ext4_directory_iterator_next(&it);
+		if (rc != EOK) {
+			ext4_directory_iterator_fini(&it);
+			async_answer_0(callid, rc);
+			return rc;
+		}
+	}
+
+	if (found) {
+		rc = ext4_directory_iterator_next(&it);
+		if (rc != EOK)
+			return rc;
+		next = it.current_offset;
+	}
+
+	rc = ext4_directory_iterator_fini(&it);
+	if (rc != EOK)
+		return rc;
+
+	if (found) {
+		*rbytes = next - pos;
+		return EOK;
+	} else {
+		async_answer_0(callid, ENOENT);
+		return ENOENT;
+	}
+}
+
+int ext4fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
+    ext4fs_instance_t *inst, ext4_inode_ref_t *inode_ref, size_t *rbytes)
+{
+	EXT4FS_DBG("");
+
+	int rc;
+	uint32_t block_size;
+	aoff64_t file_block;
+	uint64_t file_size;
+	uint32_t fs_block;
+	size_t offset_in_block;
+	size_t bytes;
+	block_t *block;
+	uint8_t *buffer;
+
+	file_size = ext4_inode_get_size(inst->filesystem->superblock,
+		inode_ref->inode);
+
+	if (pos >= file_size) {
+		/* Read 0 bytes successfully */
+		async_data_read_finalize(callid, NULL, 0);
+		*rbytes = 0;
+		return EOK;
+	}
+
+	/* For now, we only read data from one block at a time */
+	block_size = ext4_superblock_get_block_size(inst->filesystem->superblock);
+	file_block = pos / block_size;
+	offset_in_block = pos % block_size;
+	bytes = min(block_size - offset_in_block, size);
+
+	/* Handle end of file */
+	if (pos + bytes > file_size) {
+		bytes = file_size - pos;
+	}
+
+	/* Get the real block number */
+	rc = ext4_filesystem_get_inode_data_block_index(inst->filesystem,
+		inode_ref->inode, file_block, &fs_block);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	/* Check for sparse file
+	 * If ext2_filesystem_get_inode_data_block_index returned
+	 * fs_block == 0, it means that the given block is not allocated for the
+	 * file and we need to return a buffer of zeros
+	 */
+	if (fs_block == 0) {
+		buffer = malloc(bytes);
+		if (buffer == NULL) {
+			async_answer_0(callid, ENOMEM);
+			return ENOMEM;
+		}
+
+		memset(buffer, 0, bytes);
+
+		async_data_read_finalize(callid, buffer, bytes);
+		*rbytes = bytes;
+
+		free(buffer);
+
+		return EOK;
+	}
+
+	/* Usual case - we need to read a block from device */
+	rc = block_get(&block, inst->service_id, fs_block, BLOCK_FLAGS_NONE);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	assert(offset_in_block + bytes <= block_size);
+	async_data_read_finalize(callid, block->data + offset_in_block, bytes);
+
+	rc = block_put(block);
+	if (rc != EOK)
+		return rc;
+
+	*rbytes = bytes;
+	return EOK;
+}
 
 static int
