Index: uspace/srv/bd/ata_bd/ata_bd.c
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/ata_bd/ata_bd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -51,7 +51,7 @@
 #include <libarch/ddi.h>
 #include <ddi.h>
-#include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <fibril_synch.h>
 #include <stdint.h>
@@ -98,13 +98,20 @@
 
 /** Per-disk state. */
-static disk_t disk[MAX_DISKS];
+static disk_t ata_disk[MAX_DISKS];
 
 static void print_syntax(void);
 static int ata_bd_init(void);
 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
-static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
-    void *buf);
-static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
-    const void *buf);
+
+static int ata_bd_open(bd_srv_t *);
+static int ata_bd_close(bd_srv_t *);
+static int ata_bd_read_blocks(bd_srv_t *, uint64_t ba, size_t cnt, void *buf,
+    size_t);
+static int ata_bd_read_toc(bd_srv_t *, uint8_t session, void *buf, size_t);
+static int ata_bd_write_blocks(bd_srv_t *, uint64_t ba, size_t cnt,
+    const void *buf, size_t);
+static int ata_bd_get_block_size(bd_srv_t *, size_t *);
+static int ata_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
 static int ata_rcmd_read(int disk_id, uint64_t ba, size_t cnt,
     void *buf);
@@ -127,4 +134,19 @@
     unsigned timeout);
 
+static bd_ops_t ata_bd_ops = {
+	.open = ata_bd_open,
+	.close = ata_bd_close,
+	.read_blocks = ata_bd_read_blocks,
+	.read_toc = ata_bd_read_toc,
+	.write_blocks = ata_bd_write_blocks,
+	.get_block_size = ata_bd_get_block_size,
+	.get_num_blocks = ata_bd_get_num_blocks
+};
+
+static disk_t *bd_srv_disk(bd_srv_t *bd)
+{
+	return (disk_t *)bd->arg;
+}
+
 int main(int argc, char **argv)
 {
@@ -161,8 +183,8 @@
 		fflush(stdout);
 
-		rc = disk_init(&disk[i], i);
+		rc = disk_init(&ata_disk[i], i);
 
 		if (rc == EOK) {
-			disk_print_summary(&disk[i]);
+			disk_print_summary(&ata_disk[i]);
 		} else {
 			printf("Not found.\n");
@@ -174,9 +196,9 @@
 	for (i = 0; i < MAX_DISKS; i++) {
 		/* Skip unattached drives. */
-		if (disk[i].present == false)
+		if (ata_disk[i].present == false)
 			continue;
 		
 		snprintf(name, 16, "%s/ata%udisk%d", NAMESPACE, ctl_num, i);
-		rc = loc_service_register(name, &disk[i].service_id);
+		rc = loc_service_register(name, &ata_disk[i].service_id);
 		if (rc != EOK) {
 			printf(NAME ": Unable to register device %s.\n", name);
@@ -217,6 +239,6 @@
 		case am_chs:
 			printf("CHS %u cylinders, %u heads, %u sectors",
-			    disk->geom.cylinders, disk->geom.heads,
-			    disk->geom.sectors);
+			    d->geom.cylinders, d->geom.heads,
+			    d->geom.sectors);
 			break;
 		case am_lba28:
@@ -273,14 +295,5 @@
 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	void *fs_va = NULL;
-	ipc_callid_t callid;
-	ipc_call_t call;
-	sysarg_t method;
 	service_id_t dsid;
-	size_t comm_size;	/**< Size of the communication area. */
-	unsigned int flags;
-	int retval;
-	uint64_t ba;
-	size_t cnt;
 	int disk_id, i;
 
@@ -291,78 +304,13 @@
 	disk_id = -1;
 	for (i = 0; i < MAX_DISKS; i++)
-		if (disk[i].service_id == dsid)
+		if (ata_disk[i].service_id == dsid)
 			disk_id = i;
 
-	if (disk_id < 0 || disk[disk_id].present == false) {
+	if (disk_id < 0 || ata_disk[disk_id].present == false) {
 		async_answer_0(iid, EINVAL);
 		return;
 	}
 
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	(void) async_share_out_finalize(callid, &fs_va);
-	if (fs_va == AS_MAP_FAILED) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	while (true) {
-		callid = async_get_call(&call);
-		method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (method) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * disk[disk_id].block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = ata_bd_read_blocks(disk_id, ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * disk[disk_id].block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = ata_bd_write_blocks(disk_id, ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, disk[disk_id].block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
-			    UPPER32(disk[disk_id].blocks));
-			continue;
-		case BD_READ_TOC:
-			cnt = IPC_GET_ARG1(call);
-			if (disk[disk_id].dev_type == ata_pkt_dev)
-				retval = ata_pcmd_read_toc(disk_id, cnt, fs_va,
-				    disk[disk_id].block_size);
-			else
-				retval = EINVAL;
-			break;
-		default:
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+	bd_conn(iid, icall, &ata_disk[disk_id].bd);
 }
 
@@ -384,6 +332,11 @@
 	unsigned i;
 
+	d->disk_id = disk_id;
 	d->present = false;
 	fibril_mutex_initialize(&d->lock);
+
+	bd_srv_init(&d->bd);
+	d->bd.ops = &ata_bd_ops;
+	d->bd.arg = d;
 
 	/* Try identify command. */
@@ -514,16 +467,30 @@
 }
 
+static int ata_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+static int ata_bd_close(bd_srv_t *bd)
+{
+	return EOK;
+}
+
 /** Read multiple blocks from the device. */
-static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
-    void *buf) {
-
+static int ata_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
 	int rc;
 
+	if (size < cnt * disk->block_size)
+		return EINVAL;
+
 	while (cnt > 0) {
-		if (disk[disk_id].dev_type == ata_reg_dev)
-			rc = ata_rcmd_read(disk_id, ba, 1, buf);
+		if (disk->dev_type == ata_reg_dev)
+			rc = ata_rcmd_read(disk->disk_id, ba, 1, buf);
 		else
-			rc = ata_pcmd_read_12(disk_id, ba, 1, buf,
-			    disk[disk_id].block_size);
+			rc = ata_pcmd_read_12(disk->disk_id, ba, 1, buf,
+			    disk->block_size);
 
 		if (rc != EOK)
@@ -532,21 +499,33 @@
 		++ba;
 		--cnt;
-		buf += disk[disk_id].block_size;
-	}
-
-	return EOK;
+		buf += disk->block_size;
+	}
+
+	return EOK;
+}
+
+/** Read TOC from device. */
+static int ata_bd_read_toc(bd_srv_t *bd, uint8_t session, void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	return ata_pcmd_read_toc(disk->disk_id, session, buf, size);
 }
 
 /** Write multiple blocks to the device. */
-static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
-    const void *buf) {
-
+static int ata_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	disk_t *disk = bd_srv_disk(bd);
 	int rc;
 
-	if (disk[disk_id].dev_type != ata_reg_dev)
+	if (disk->dev_type != ata_reg_dev)
 		return ENOTSUP;
 
+	if (size < cnt * disk->block_size)
+		return EINVAL;
+
 	while (cnt > 0) {
-		rc = ata_rcmd_write(disk_id, ba, 1, buf);
+		rc = ata_rcmd_write(disk->disk_id, ba, 1, buf);
 		if (rc != EOK)
 			return rc;
@@ -554,7 +533,25 @@
 		++ba;
 		--cnt;
-		buf += disk[disk_id].block_size;
-	}
-
+		buf += disk->block_size;
+	}
+
+	return EOK;
+}
+
+/** Get device block size. */
+static int ata_bd_get_block_size(bd_srv_t *bd, size_t *rbsize)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	*rbsize = disk->block_size;
+	return EOK;
+}
+
+/** Get device number of blocks. */
+static int ata_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	disk_t *disk = bd_srv_disk(bd);
+
+	*rnb = disk->blocks;
 	return EOK;
 }
@@ -685,5 +682,5 @@
 	uint16_t val;
 
-	d = &disk[dev_idx];
+	d = &ata_disk[dev_idx];
 	fibril_mutex_lock(&d->lock);
 
@@ -874,5 +871,5 @@
 	block_coord_t bc;
 
-	d = &disk[disk_id];
+	d = &ata_disk[disk_id];
 	
 	/* Silence warning. */
@@ -919,5 +916,5 @@
 		/* Read data from the device buffer. */
 
-		for (i = 0; i < disk[disk_id].block_size / 2; i++) {
+		for (i = 0; i < ata_disk[disk_id].block_size / 2; i++) {
 			data = pio_read_16(&cmd->data_port);
 			((uint16_t *) buf)[i] = data;
@@ -950,5 +947,5 @@
 	block_coord_t bc;
 
-	d = &disk[disk_id];
+	d = &ata_disk[disk_id];
 	
 	/* Silence warning. */
@@ -995,5 +992,5 @@
 		/* Write data to the device buffer. */
 
-		for (i = 0; i < disk[disk_id].block_size / 2; i++) {
+		for (i = 0; i < d->block_size / 2; i++) {
 			pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
 		}
Index: uspace/srv/bd/ata_bd/ata_bd.h
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.h	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/ata_bd/ata_bd.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -36,4 +36,5 @@
 #define __ATA_BD_H__
 
+#include <bd_srv.h>
 #include <sys/types.h>
 #include <fibril_synch.h>
@@ -117,4 +118,6 @@
 	fibril_mutex_t lock;
 	service_id_t service_id;
+	int disk_id;
+	bd_srv_t bd;
 } disk_t;
 
Index: uspace/srv/bd/file_bd/file_bd.c
===================================================================
--- uspace/srv/bd/file_bd/file_bd.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/file_bd/file_bd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -41,7 +41,7 @@
 #include <stdio.h>
 #include <unistd.h>
-#include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <fibril_synch.h>
 #include <loc.h>
@@ -62,4 +62,5 @@
 
 static service_id_t service_id;
+static bd_srv_t bd_srv;
 static fibril_mutex_t dev_lock;
 
@@ -67,6 +68,20 @@
 static int file_bd_init(const char *fname);
 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
-static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf);
-static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
+
+static int file_bd_open(bd_srv_t *);
+static int file_bd_close(bd_srv_t *);
+static int file_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int file_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int file_bd_get_block_size(bd_srv_t *, size_t *);
+static int file_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t file_bd_ops = {
+	.open = file_bd_open,
+	.close = file_bd_close,
+	.read_blocks = file_bd_read_blocks,
+	.write_blocks = file_bd_write_blocks,
+	.get_block_size = file_bd_get_block_size,
+	.get_num_blocks = file_bd_get_num_blocks
+};
 
 int main(int argc, char **argv)
@@ -139,4 +154,7 @@
 static int file_bd_init(const char *fname)
 {
+	bd_srv_init(&bd_srv);
+	bd_srv.ops = &file_bd_ops;
+	
 	async_set_client_connection(file_bd_connection);
 	int rc = loc_server_register(NAME);
@@ -170,79 +188,28 @@
 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	void *fs_va = NULL;
-	ipc_callid_t callid;
-	ipc_call_t call;
-	sysarg_t method;
-	size_t comm_size;
-	unsigned int flags;
-	int retval;
-	uint64_t ba;
-	size_t cnt;
-
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	(void) async_share_out_finalize(callid, &fs_va);
-	if (fs_va == AS_MAP_FAILED) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	while (true) {
-		callid = async_get_call(&call);
-		method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (method) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = file_bd_read_blocks(ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = file_bd_write_blocks(ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(num_blocks),
-			    UPPER32(num_blocks));
-			continue;
-		default:
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+	bd_conn(iid, icall, &bd_srv);
+}
+
+/** Open device. */
+static int file_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int file_bd_close(bd_srv_t *bd)
+{
+	return EOK;
 }
 
 /** Read blocks from the device. */
-static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf)
+static int file_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt, void *buf,
+    size_t size)
 {
 	size_t n_rd;
 	int rc;
+
+	if (size < cnt * block_size)
+		return EINVAL;
 
 	/* Check whether access is within device address bounds. */
@@ -279,8 +246,12 @@
 
 /** Write blocks to the device. */
-static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
+static int file_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
+    const void *buf, size_t size)
 {
 	size_t n_wr;
 	int rc;
+
+	if (size < cnt * block_size)
+		return EINVAL;
 
 	/* Check whether access is within device address bounds. */
@@ -318,4 +289,18 @@
 }
 
+/** Get device block size. */
+static int file_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int file_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	*rnb = num_blocks;
+	return EOK;
+}
+
 /**
  * @}
Index: uspace/srv/bd/gxe_bd/gxe_bd.c
===================================================================
--- uspace/srv/bd/gxe_bd/gxe_bd.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/gxe_bd/gxe_bd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -39,7 +39,7 @@
 #include <libarch/ddi.h>
 #include <ddi.h>
-#include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <fibril_synch.h>
 #include <loc.h>
@@ -65,4 +65,5 @@
 };
 
+/** GXE disk hardware registers */
 typedef struct {
 	uint32_t offset_lo;
@@ -83,25 +84,49 @@
 
 	uint8_t buffer[512];
+} gxe_bd_hw_t;
+
+/** GXE block device soft state */
+typedef struct {
+	/** Block device server structure */
+	bd_srv_t bd;
+	int disk_id;
 } gxe_bd_t;
 
-
 static const size_t block_size = 512;
-static size_t comm_size;
 
 static uintptr_t dev_physical = 0x13000000;
-static gxe_bd_t *dev;
+static gxe_bd_hw_t *dev;
 
 static service_id_t service_id[MAX_DISKS];
 
 static fibril_mutex_t dev_lock[MAX_DISKS];
+
+static gxe_bd_t gxe_bd[MAX_DISKS];
 
 static int gxe_bd_init(void);
 static void gxe_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
-static int gxe_bd_read_blocks(int disk_id, uint64_t ba, unsigned cnt,
-    void *buf);
-static int gxe_bd_write_blocks(int disk_id, uint64_t ba, unsigned cnt,
-    const void *buf);
 static int gxe_bd_read_block(int disk_id, uint64_t ba, void *buf);
 static int gxe_bd_write_block(int disk_id, uint64_t ba, const void *buf);
+
+static int gxe_bd_open(bd_srv_t *);
+static int gxe_bd_close(bd_srv_t *);
+static int gxe_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int gxe_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int gxe_bd_get_block_size(bd_srv_t *, size_t *);
+static int gxe_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t gxe_bd_ops = {
+	.open = gxe_bd_open,
+	.close = gxe_bd_close,
+	.read_blocks = gxe_bd_read_blocks,
+	.write_blocks = gxe_bd_write_blocks,
+	.get_block_size = gxe_bd_get_block_size,
+	.get_num_blocks = gxe_bd_get_num_blocks
+};
+
+static gxe_bd_t *bd_srv_gxe(bd_srv_t *bd)
+{
+	return (gxe_bd_t *)bd->arg;
+}
 
 int main(int argc, char **argv)
@@ -130,5 +155,5 @@
 	
 	void *vaddr;
-	rc = pio_enable((void *) dev_physical, sizeof(gxe_bd_t), &vaddr);
+	rc = pio_enable((void *) dev_physical, sizeof(gxe_bd_hw_t), &vaddr);
 	if (rc != EOK) {
 		printf("%s: Could not initialize device I/O space.\n", NAME);
@@ -140,4 +165,8 @@
 	for (unsigned int i = 0; i < MAX_DISKS; i++) {
 		char name[16];
+		
+		bd_srv_init(&gxe_bd[i].bd);
+		gxe_bd[i].bd.ops = &gxe_bd_ops;
+		gxe_bd[i].bd.arg = (void *)&gxe_bd[i];
 		
 		snprintf(name, 16, "%s/disk%u", NAMESPACE, i);
@@ -157,13 +186,5 @@
 static void gxe_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	void *fs_va = NULL;
-	ipc_callid_t callid;
-	ipc_call_t call;
-	sysarg_t method;
 	service_id_t dsid;
-	unsigned int flags;
-	int retval;
-	uint64_t ba;
-	unsigned cnt;
 	int disk_id, i;
 
@@ -182,73 +203,28 @@
 	}
 
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	if (comm_size < block_size) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	(void) async_share_out_finalize(callid, &fs_va);
-	if (fs_va == AS_MAP_FAILED) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	while (true) {
-		callid = async_get_call(&call);
-		method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (method) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = gxe_bd_read_blocks(disk_id, ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = gxe_bd_write_blocks(disk_id, ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			retval = ENOTSUP;
-			break;
-		default:
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+	bd_conn(iid, icall, &gxe_bd[disk_id].bd);
+}
+
+/** Open device. */
+static int gxe_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int gxe_bd_close(bd_srv_t *bd)
+{
+	return EOK;
 }
 
 /** Read multiple blocks from the device. */
-static int gxe_bd_read_blocks(int disk_id, uint64_t ba, unsigned cnt,
-    void *buf) {
-
+static int gxe_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    void *buf, size_t size)
+{
+	int disk_id = bd_srv_gxe(bd)->disk_id;
 	int rc;
+
+	if (size < cnt * block_size)
+		return EINVAL;
 
 	while (cnt > 0) {
@@ -266,8 +242,12 @@
 
 /** Write multiple blocks to the device. */
-static int gxe_bd_write_blocks(int disk_id, uint64_t ba, unsigned cnt,
-    const void *buf) {
-
+static int gxe_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	int disk_id = bd_srv_gxe(bd)->disk_id;
 	int rc;
+
+	if (size < cnt * block_size)
+		return EINVAL;
 
 	while (cnt > 0) {
@@ -282,4 +262,17 @@
 
 	return EOK;
+}
+
+/** Get device block size. */
+static int gxe_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int gxe_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	return ENOTSUP;
 }
 
Index: uspace/srv/bd/part/guid_part/guid_part.c
===================================================================
--- uspace/srv/bd/part/guid_part/guid_part.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/part/guid_part/guid_part.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -47,7 +47,7 @@
 #include <stdlib.h>
 #include <unistd.h>
-#include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <fibril_synch.h>
 #include <loc.h>
@@ -83,4 +83,6 @@
 	/** Service representing the partition (outbound device) */
 	service_id_t dsid;
+	/** Block device server structure */
+	bd_srv_t bd;
 	/** Points to next partition structure. */
 	struct part *next;
@@ -100,7 +102,26 @@
 static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
 static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
-static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf);
-static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf);
 static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
+
+static int gpt_bd_open(bd_srv_t *);
+static int gpt_bd_close(bd_srv_t *);
+static int gpt_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int gpt_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int gpt_bd_get_block_size(bd_srv_t *, size_t *);
+static int gpt_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t gpt_bd_ops = {
+	.open = gpt_bd_open,
+	.close = gpt_bd_close,
+	.read_blocks = gpt_bd_read_blocks,
+	.write_blocks = gpt_bd_write_blocks,
+	.get_block_size = gpt_bd_get_block_size,
+	.get_num_blocks = gpt_bd_get_num_blocks
+};
+
+static part_t *bd_srv_part(bd_srv_t *bd)
+{
+	return (part_t *)bd->arg;
+}
 
 int main(int argc, char **argv)
@@ -304,4 +325,8 @@
 	}
 
+	bd_srv_init(&part->bd);
+	part->bd.ops = &gpt_bd_ops;
+	part->bd.arg = part;
+
 	part->dsid = 0;
 	part->next = NULL;
@@ -310,14 +335,5 @@
 static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	size_t comm_size;
-	void *fs_va = NULL;
-	ipc_callid_t callid;
-	ipc_call_t call;
-	sysarg_t method;
 	service_id_t dh;
-	unsigned int flags;
-	int retval;
-	aoff64_t ba;
-	size_t cnt;
 	part_t *part;
 
@@ -341,68 +357,28 @@
 	assert(part->present == true);
 
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	(void) async_share_out_finalize(callid, &fs_va);
-	if (fs_va == AS_MAP_FAILED) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	while (true) {
-		callid = async_get_call(&call);
-		method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (method) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = gpt_bd_read(part, ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = gpt_bd_write(part, ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(part->length),
-			    UPPER32(part->length));
-			continue;
-		default:
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+	bd_conn(iid, icall, &part->bd);
+}
+
+/** Open device. */
+static int gpt_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int gpt_bd_close(bd_srv_t *bd)
+{
+	return EOK;
 }
 
 /** Read blocks from partition. */
-static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf)
-{
+static int gpt_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	part_t *p = bd_srv_part(bd);
 	aoff64_t gba;
+
+	if (cnt * block_size < size)
+		return EINVAL;
 
 	if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
@@ -413,7 +389,12 @@
 
 /** Write blocks to partition. */
-static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf)
-{
+static int gpt_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	part_t *p = bd_srv_part(bd);
 	aoff64_t gba;
+
+	if (cnt * block_size < size)
+		return EINVAL;
 
 	if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
@@ -422,4 +403,21 @@
 	return block_write_direct(indev_sid, gba, cnt, buf);
 }
+
+/** Get device block size. */
+static int gpt_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int gpt_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	part_t *part = bd_srv_part(bd);
+
+	*rnb = part->length;
+	return EOK;
+}
+
 
 /** Translate block segment address with range checking. */
Index: uspace/srv/bd/part/mbr_part/mbr_part.c
===================================================================
--- uspace/srv/bd/part/mbr_part/mbr_part.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/part/mbr_part/mbr_part.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -44,5 +44,5 @@
  *
  * Referemces:
- *	
+ *
  * The source of MBR structures for this driver have been the following
  * Wikipedia articles:
@@ -57,7 +57,7 @@
 #include <stdlib.h>
 #include <unistd.h>
-#include <ipc/bd.h>
 #include <async.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <fibril_synch.h>
 #include <loc.h>
@@ -100,4 +100,6 @@
 	/** Device representing the partition (outbound device) */
 	service_id_t dsid;
+	/** Block device server structure */
+	bd_srv_t bd;
 	/** Points to next partition structure. */
 	struct part *next;
@@ -140,5 +142,5 @@
 
 /** Partitioned device (inbound device) */
-static service_id_t indef_sid;
+static service_id_t indev_sid;
 
 /** List of partitions. This structure is an empty head. */
@@ -150,7 +152,26 @@
 static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part);
 static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
-static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf);
-static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf);
 static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
+
+static int mbr_bd_open(bd_srv_t *);
+static int mbr_bd_close(bd_srv_t *);
+static int mbr_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int mbr_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int mbr_bd_get_block_size(bd_srv_t *, size_t *);
+static int mbr_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t mbr_bd_ops = {
+	.open = mbr_bd_open,
+	.close = mbr_bd_close,
+	.read_blocks = mbr_bd_read_blocks,
+	.write_blocks = mbr_bd_write_blocks,
+	.get_block_size = mbr_bd_get_block_size,
+	.get_num_blocks = mbr_bd_get_num_blocks
+};
+
+static part_t *bd_srv_part(bd_srv_t *bd)
+{
+	return (part_t *)bd->arg;
+}
 
 int main(int argc, char **argv)
@@ -183,5 +204,5 @@
 	part_t *part;
 
-	rc = loc_service_get_id(dev_name, &indef_sid, 0);
+	rc = loc_service_get_id(dev_name, &indev_sid, 0);
 	if (rc != EOK) {
 		printf(NAME ": could not resolve device `%s'.\n", dev_name);
@@ -189,5 +210,5 @@
 	}
 
-	rc = block_init(EXCHANGE_SERIALIZE, indef_sid, 2048);
+	rc = block_init(EXCHANGE_SERIALIZE, indev_sid, 2048);
 	if (rc != EOK)  {
 		printf(NAME ": could not init libblock.\n");
@@ -197,5 +218,5 @@
 	/* Determine and verify block size. */
 
-	rc = block_get_bsize(indef_sid, &block_size);
+	rc = block_get_bsize(indev_sid, &block_size);
 	if (rc != EOK) {
 		printf(NAME ": error getting block size.\n");
@@ -281,5 +302,5 @@
 	 */
 
-	rc = block_read_direct(indef_sid, 0, 1, brb);
+	rc = block_read_direct(indev_sid, 0, 1, brb);
 	if (rc != EOK) {
 		printf(NAME ": Failed reading MBR block.\n");
@@ -332,5 +353,5 @@
 		 */
 		ba = cp.start_addr;
-		rc = block_read_direct(indef_sid, ba, 1, brb);
+		rc = block_read_direct(indev_sid, ba, 1, brb);
 		if (rc != EOK) {
 			printf(NAME ": Failed reading EBR block at %"
@@ -381,4 +402,8 @@
 	part->present = (pte->ptype != PT_UNUSED) ? true : false;
 
+	bd_srv_init(&part->bd);
+	part->bd.ops = &mbr_bd_ops;
+	part->bd.arg = part;
+
 	part->dsid = 0;
 	part->next = NULL;
@@ -387,14 +412,5 @@
 static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	size_t comm_size;
-	void *fs_va = NULL;
-	ipc_callid_t callid;
-	ipc_call_t call;
-	sysarg_t method;
 	service_id_t dh;
-	unsigned int flags;
-	int retval;
-	uint64_t ba;
-	size_t cnt;
 	part_t *part;
 
@@ -417,85 +433,65 @@
 
 	assert(part->present == true);
-
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	if (!async_share_out_receive(&callid, &comm_size, &flags)) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	(void) async_share_out_finalize(callid, &fs_va);
-	if (fs_va == AS_MAP_FAILED) {
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-
-	while (1) {
-		callid = async_get_call(&call);
-		method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (method) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = mbr_bd_read(part, ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = mbr_bd_write(part, ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(part->length),
-			    UPPER32(part->length));
-			continue;
-		default:
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+	bd_conn(iid, icall, &part->bd);
+}
+
+/** Open device. */
+static int mbr_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int mbr_bd_close(bd_srv_t *bd)
+{
+	return EOK;
 }
 
 /** Read blocks from partition. */
-static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
-{
-	uint64_t gba;
+static int mbr_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	part_t *p = bd_srv_part(bd);
+	aoff64_t gba;
+
+	if (cnt * block_size < size)
+		return EINVAL;
 
 	if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
 		return ELIMIT;
 
-	return block_read_direct(indef_sid, gba, cnt, buf);
+	return block_read_direct(indev_sid, gba, cnt, buf);
 }
 
 /** Write blocks to partition. */
-static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
-{
-	uint64_t gba;
+static int mbr_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	part_t *p = bd_srv_part(bd);
+	aoff64_t gba;
+
+	if (cnt * block_size < size)
+		return EINVAL;
 
 	if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
 		return ELIMIT;
 
-	return block_write_direct(indef_sid, gba, cnt, buf);
+	return block_write_direct(indev_sid, gba, cnt, buf);
+}
+
+/** Get device block size. */
+static int mbr_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int mbr_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	part_t *part = bd_srv_part(bd);
+
+	*rnb = part->length;
+	return EOK;
 }
 
Index: uspace/srv/bd/rd/rd.c
===================================================================
--- uspace/srv/bd/rd/rd.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/bd/rd/rd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -43,4 +43,5 @@
 #include <sysinfo.h>
 #include <as.h>
+#include <bd_srv.h>
 #include <ddi.h>
 #include <align.h>
@@ -53,5 +54,4 @@
 #include <stdio.h>
 #include <loc.h>
-#include <ipc/bd.h>
 #include <macros.h>
 #include <inttypes.h>
@@ -68,6 +68,10 @@
 static const size_t block_size = 512;
 
-static int rd_read_blocks(uint64_t ba, size_t cnt, void *buf);
-static int rd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
+static int rd_open(bd_srv_t *);
+static int rd_close(bd_srv_t *);
+static int rd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int rd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int rd_get_block_size(bd_srv_t *, size_t *);
+static int rd_get_num_blocks(bd_srv_t *, aoff64_t *);
 
 /** This rwlock protects the ramdisk's data.
@@ -78,102 +82,37 @@
  *
  */
-fibril_rwlock_t rd_lock;
-
-/** Handle one connection to ramdisk.
- *
- * @param iid   Hash of the request that opened the connection.
- * @param icall Call data of the request that opened the connection.
- */
-static void rd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	ipc_callid_t callid;
-	ipc_call_t call;
-	int retval;
-	void *fs_va = NULL;
-	uint64_t ba;
-	size_t cnt;
-	size_t comm_size;
-	
-	/*
-	 * Answer the first IPC_M_CONNECT_ME_TO call.
-	 */
-	async_answer_0(iid, EOK);
-	
-	/*
-	 * Now we wait for the client to send us its communication as_area.
-	 */
-	unsigned int flags;
-	if (async_share_out_receive(&callid, &comm_size, &flags)) {
-		(void) async_share_out_finalize(callid, &fs_va);
-		if (fs_va == AS_MAP_FAILED) {
-			async_answer_0(callid, EHANGUP);
-			return;
-		}
-	} else {
-		/*
-		 * The client doesn't speak the same protocol.
-		 * At this point we can't handle protocol variations.
-		 * Close the connection.
-		 */
-		async_answer_0(callid, EHANGUP);
-		return;
-	}
-	
-	while (true) {
-		callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			/*
-			 * The other side has hung up.
-			 * Exit the fibril.
-			 */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case BD_READ_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = rd_read_blocks(ba, cnt, fs_va);
-			break;
-		case BD_WRITE_BLOCKS:
-			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			cnt = IPC_GET_ARG3(call);
-			if (cnt * block_size > comm_size) {
-				retval = ELIMIT;
-				break;
-			}
-			retval = rd_write_blocks(ba, cnt, fs_va);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			async_answer_1(callid, EOK, block_size);
-			continue;
-		case BD_GET_NUM_BLOCKS:
-			async_answer_2(callid, EOK, LOWER32(rd_size / block_size),
-			    UPPER32(rd_size / block_size));
-			continue;
-		default:
-			/*
-			 * The client doesn't speak the same protocol.
-			 * Instead of closing the connection, we just ignore
-			 * the call. This can be useful if the client uses a
-			 * newer version of the protocol.
-			 */
-			retval = EINVAL;
-			break;
-		}
-		async_answer_0(callid, retval);
-	}
+static fibril_rwlock_t rd_lock;
+
+static bd_ops_t rd_bd_ops = {
+	.open = rd_open,
+	.close = rd_close,
+	.read_blocks = rd_read_blocks,
+	.write_blocks = rd_write_blocks,
+	.get_block_size = rd_get_block_size,
+	.get_num_blocks = rd_get_num_blocks
+};
+
+static bd_srv_t bd_srv;
+
+static void rd_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	bd_conn(iid, icall, &bd_srv);
+}
+
+/** Open device. */
+static int rd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int rd_close(bd_srv_t *bd)
+{
+	return EOK;
 }
 
 /** Read blocks from the device. */
-static int rd_read_blocks(uint64_t ba, size_t cnt, void *buf)
+static int rd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
 {
 	if ((ba + cnt) * block_size > rd_size) {
@@ -183,5 +122,5 @@
 	
 	fibril_rwlock_read_lock(&rd_lock);
-	memcpy(buf, rd_addr + ba * block_size, block_size * cnt);
+	memcpy(buf, rd_addr + ba * block_size, min(block_size * cnt, size));
 	fibril_rwlock_read_unlock(&rd_lock);
 	
@@ -190,5 +129,6 @@
 
 /** Write blocks to the device. */
-static int rd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
+static int rd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
 {
 	if ((ba + cnt) * block_size > rd_size) {
@@ -198,5 +138,5 @@
 	
 	fibril_rwlock_write_lock(&rd_lock);
-	memcpy(rd_addr + ba * block_size, buf, block_size * cnt);
+	memcpy(rd_addr + ba * block_size, buf, min(block_size * cnt, size));
 	fibril_rwlock_write_unlock(&rd_lock);
 	
@@ -235,5 +175,8 @@
 	    (void *) addr_phys, size);
 	
-	async_set_client_connection(rd_connection);
+	bd_srv_init(&bd_srv);
+	bd_srv.ops = &rd_bd_ops;
+	
+	async_set_client_connection(rd_client_conn);
 	ret = loc_server_register(NAME);
 	if (ret != EOK) {
@@ -254,4 +197,18 @@
 }
 
+/** Get device block size. */
+static int rd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	*rsize = block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int rd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	*rnb = rd_size / block_size;
+	return EOK;
+}
+
 int main(int argc, char **argv)
 {
Index: uspace/srv/bd/sata_bd/Makefile
===================================================================
--- uspace/srv/bd/sata_bd/Makefile	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/bd/sata_bd/Makefile	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2012 Petr Jerman
+# 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 = ../../..
+BINARY = sata_bd
+
+SOURCES = \
+	sata_bd.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/bd/sata_bd/sata_bd.c
===================================================================
--- uspace/srv/bd/sata_bd/sata_bd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/bd/sata_bd/sata_bd.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2012 Petr Jerman
+ * 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 bd
+ * @{
+ */
+
+/**
+ * @file
+ * @brief SATA disk driver
+ *
+ */
+
+#include <sys/types.h>
+#include <bd_srv.h>
+#include <errno.h>
+#include <stdio.h>
+#include <str.h>
+#include <loc.h>
+#include <macros.h>
+
+#include <device/ahci.h>
+#include "sata_bd.h"
+
+#define NAME       "sata_bd"
+#define NAMESPACE  "bd"
+
+/** Maximum number of disks handled */
+#define MAXDISKS  256
+
+static sata_bd_dev_t disk[MAXDISKS];
+static int disk_count;
+
+static int sata_bd_open(bd_srv_t *);
+static int sata_bd_close(bd_srv_t *);
+static int sata_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int sata_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+static int sata_bd_get_block_size(bd_srv_t *, size_t *);
+static int sata_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static bd_ops_t sata_bd_ops = {
+	.open = sata_bd_open,
+	.close = sata_bd_close,
+	.read_blocks = sata_bd_read_blocks,
+	.write_blocks = sata_bd_write_blocks,
+	.get_block_size = sata_bd_get_block_size,
+	.get_num_blocks = sata_bd_get_num_blocks
+};
+
+static sata_bd_dev_t *bd_srv_sata(bd_srv_t *bd)
+{
+	return (sata_bd_dev_t *)bd->arg;
+}
+
+/** Find SATA devices in device tree.
+ *
+ *  @param Device manager handle describing container for searching.  
+ *
+ *  @return EOK if succeed, error code otherwise.
+ *
+ */
+static int scan_device_tree(devman_handle_t funh)
+{
+	devman_handle_t devh;
+	devman_handle_t *cfuns;
+	size_t count, i;
+	int rc;
+		
+	/* If device is SATA, add device to the disk array. */
+	disk[disk_count].sess = ahci_get_sess(funh, &disk[disk_count].dev_name);
+	if(disk[disk_count].sess != NULL) {
+		
+		ahci_get_sata_device_name(disk[disk_count].sess,
+		    SATA_DEV_NAME_LENGTH, disk[disk_count].sata_dev_name);
+		
+		ahci_get_block_size(disk[disk_count].sess,
+		    &disk[disk_count].block_size);
+		
+		ahci_get_num_blocks(disk[disk_count].sess, &disk[disk_count].blocks);
+		
+		bd_srv_init(&disk[disk_count].bd);
+		disk[disk_count].bd.ops = &sata_bd_ops;
+		disk[disk_count].bd.arg = &disk[disk_count];
+		
+		printf("Device %s - %s , blocks: %lu, block_size: %lu\n", 
+		    disk[disk_count].dev_name, disk[disk_count].sata_dev_name,
+			    (long unsigned int) disk[disk_count].blocks,
+				(long unsigned int) disk[disk_count].block_size);
+
+		++disk_count;
+	}
+	
+	/* search children */
+	rc = devman_fun_get_child(funh, &devh);
+	if (rc == ENOENT)
+		return EOK;
+
+	if (rc != EOK) {
+		printf(NAME ": Failed getting child device for function %s.\n", "xxx");
+		return rc;
+	}
+
+	rc = devman_dev_get_functions(devh, &cfuns, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting list of functions for device %s.\n",
+		    "xxx");
+		return rc;
+	}
+
+	for (i = 0; i < count; i++)
+		scan_device_tree(cfuns[i]);
+
+	free(cfuns);
+	return EOK;
+}
+
+/** Find sata devices in device tree from root.
+ *
+ *  @return EOK if succeed, error code otherwise.
+ *
+ */
+static int get_sata_disks()
+{
+	devman_handle_t root_fun;
+	int rc;
+	
+	disk_count = 0;
+
+	rc = devman_fun_get_handle("/", &root_fun, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving root function.\n");
+		return EIO;
+	}
+	
+	scan_device_tree(root_fun);
+	
+	return EOK;
+}
+
+/** Block device connection handler. */
+static void sata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	service_id_t dsid;
+	int disk_id, i;
+
+	/* Get the device service ID. */
+	dsid = IPC_GET_ARG1(*icall);
+
+	/* Determine which disk device is the client connecting to. */
+	disk_id = -1;
+	for (i = 0; i < MAXDISKS; i++)
+		if (disk[i].service_id == dsid)
+			disk_id = i;
+
+	if (disk_id < 0) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	bd_conn(iid, icall, &disk[disk_id].bd);
+}
+
+/** Open device. */
+static int sata_bd_open(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Close device. */
+static int sata_bd_close(bd_srv_t *bd)
+{
+	return EOK;
+}
+
+/** Read blocks from partition. */
+static int sata_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	sata_bd_dev_t *sbd = bd_srv_sata(bd);
+
+	if (size < cnt * sbd->block_size)
+		return EINVAL;
+
+	return ahci_read_blocks(sbd->sess, ba, cnt, buf);
+}
+
+/** Write blocks to partition. */
+static int sata_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	sata_bd_dev_t *sbd = bd_srv_sata(bd);
+
+	if (size < cnt * sbd->block_size)
+		return EINVAL;
+
+	return ahci_write_blocks(sbd->sess, ba, cnt, (void *)buf);
+}
+
+/** Get device block size. */
+static int sata_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	sata_bd_dev_t *sbd = bd_srv_sata(bd);
+
+	*rsize = sbd->block_size;
+	return EOK;
+}
+
+/** Get number of blocks on device. */
+static int sata_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	sata_bd_dev_t *sbd = bd_srv_sata(bd);
+
+	*rnb = sbd->blocks;
+	return EOK;
+}
+
+
+int main(int argc, char **argv)
+{
+	int rc;
+	
+	async_set_client_connection(sata_bd_connection);
+	rc = loc_server_register(NAME);
+	if (rc < 0) {
+		printf(NAME ": Unable to register driver.\n");
+		return rc;
+	}
+	
+	rc = get_sata_disks();
+	if (rc != EOK) {
+		return rc;
+	}
+
+	for(int i=0; i < disk_count; i++) {
+		char name[1024];
+		snprintf(name, 1024, "%s/%s", NAMESPACE, disk[i].dev_name);
+		rc = loc_service_register(name, &disk[i].service_id);
+		if (rc != EOK) {
+			printf(NAME ": Unable to register device %s.\n", name);
+			return rc;
+		}
+	}
+		
+	printf(NAME ": Accepting connections\n");
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/bd/sata_bd/sata_bd.h
===================================================================
--- uspace/srv/bd/sata_bd/sata_bd.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/bd/sata_bd/sata_bd.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012 Petr Jerman
+ * 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 bd
+ * @{
+ */
+/** @file SATA block device driver definitions.
+ */
+
+#ifndef __SATA_BD_H__
+#define __SATA_BD_H__
+
+#define SATA_DEV_NAME_LENGTH 256
+
+#include <async.h>
+#include <bd_srv.h>
+#include <loc.h>
+#include <sys/types.h>
+
+/** SATA Block Device. */
+typedef struct {
+	/** Device name in device tree. */
+	char *dev_name;
+	/** SATA Device name. */
+	char sata_dev_name[SATA_DEV_NAME_LENGTH];
+	/** Session to device methods. */
+	async_sess_t *sess;
+	/** Loc service id. */
+	service_id_t service_id;
+	/** Number of blocks. */
+	uint64_t blocks;
+	/** Size of block. */
+	size_t block_size;
+	/** Block device server structure */
+	bd_srv_t bd;
+} sata_bd_dev_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/devman/devman.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -1052,5 +1052,4 @@
 }
 
-
 /** Find the device node structure of the device witch has the specified handle.
  *
@@ -1142,4 +1141,5 @@
 	fun->state = FUN_INIT;
 	atomic_set(&fun->refcnt, 0);
+	fibril_mutex_initialize(&fun->busy_lock);
 	link_initialize(&fun->dev_functions);
 	list_initialize(&fun->match_ids.ids);
@@ -1184,4 +1184,16 @@
 	if (atomic_predec(&fun->refcnt) == 0)
 		delete_fun_node(fun);
+}
+
+/** Make function busy for reconfiguration operations. */
+void fun_busy_lock(fun_node_t *fun)
+{
+	fibril_mutex_lock(&fun->busy_lock);
+}
+
+/** Mark end of reconfiguration operation. */
+void fun_busy_unlock(fun_node_t *fun)
+{
+	fibril_mutex_unlock(&fun->busy_lock);
 }
 
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/devman/devman.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -174,4 +174,6 @@
 	/** State */
 	fun_state_t state;
+	/** Locked while performing reconfiguration operations */
+	fibril_mutex_t busy_lock;
 	
 	/** The global unique identifier of the function */
@@ -279,4 +281,5 @@
 extern void dev_add_ref(dev_node_t *);
 extern void dev_del_ref(dev_node_t *);
+
 extern dev_node_t *find_dev_node_no_lock(dev_tree_t *tree,
     devman_handle_t handle);
@@ -290,4 +293,6 @@
 extern void fun_add_ref(fun_node_t *);
 extern void fun_del_ref(fun_node_t *);
+extern void fun_busy_lock(fun_node_t *);
+extern void fun_busy_unlock(fun_node_t *);
 extern fun_node_t *find_fun_node_no_lock(dev_tree_t *tree,
     devman_handle_t handle);
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/devman/main.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -432,10 +432,20 @@
 	
 	fun_node_t *fun = create_fun_node();
+	/* One reference for creation, one for us */
+	fun_add_ref(fun);
 	fun_add_ref(fun);
 	fun->ftype = ftype;
+	
+	/*
+	 * We can lock the function here even when holding the tree because
+	 * we know it cannot be held by anyone else yet.
+	 */
+	fun_busy_lock(fun);
 	
 	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
 		fibril_rwlock_write_unlock(&tree->rwlock);
 		dev_del_ref(pdev);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
 		delete_fun_node(fun);
 		async_answer_0(callid, ENOMEM);
@@ -450,8 +460,13 @@
 	rc = online_function(fun);
 	if (rc != EOK) {
-		/* XXX clean up */
+		/* XXX Set some failed state? */
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
 		async_answer_0(callid, rc);
 		return;
 	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
 	
 	/* Return device handle to parent's driver. */
@@ -522,7 +537,10 @@
 	}
 	
+	fun_busy_lock(fun);
+	
 	fibril_rwlock_read_lock(&device_tree.rwlock);
 	if (fun->dev == NULL || fun->dev->drv != drv) {
 		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		fun_busy_unlock(fun);
 		fun_del_ref(fun);
 		async_answer_0(iid, ENOENT);
@@ -533,4 +551,5 @@
 	rc = online_function(fun);
 	if (rc != EOK) {
+		fun_busy_unlock(fun);
 		fun_del_ref(fun);
 		async_answer_0(iid, (sysarg_t) rc);
@@ -538,4 +557,5 @@
 	}
 	
+	fun_busy_unlock(fun);
 	fun_del_ref(fun);
 	
@@ -559,6 +579,9 @@
 	}
 	
+	fun_busy_lock(fun);
+	
 	fibril_rwlock_write_lock(&device_tree.rwlock);
 	if (fun->dev == NULL || fun->dev->drv != drv) {
+		fun_busy_unlock(fun);
 		fun_del_ref(fun);
 		async_answer_0(iid, ENOENT);
@@ -569,4 +592,5 @@
 	rc = offline_function(fun);
 	if (rc != EOK) {
+		fun_busy_unlock(fun);
 		fun_del_ref(fun);
 		async_answer_0(iid, (sysarg_t) rc);
@@ -574,4 +598,5 @@
 	}
 	
+	fun_busy_unlock(fun);
 	fun_del_ref(fun);
 	async_answer_0(iid, (sysarg_t) EOK);
@@ -591,4 +616,6 @@
 	}
 	
+	fun_busy_lock(fun);
+	
 	fibril_rwlock_write_lock(&tree->rwlock);
 	
@@ -598,4 +625,6 @@
 	if (fun->state == FUN_REMOVED) {
 		fibril_rwlock_write_unlock(&tree->rwlock);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
 		async_answer_0(callid, ENOENT);
 		return;
@@ -638,4 +667,6 @@
 				if (gone_rc == EOK)
 					gone_rc = ENOTSUP;
+				fun_busy_unlock(fun);
+				fun_del_ref(fun);
 				async_answer_0(callid, gone_rc);
 				return;
@@ -664,4 +695,5 @@
 				    "service.");
 				fibril_rwlock_write_unlock(&tree->rwlock);
+				fun_busy_unlock(fun);
 				fun_del_ref(fun);
 				async_answer_0(callid, EIO);
@@ -673,4 +705,5 @@
 	remove_fun_node(&device_tree, fun);
 	fibril_rwlock_write_unlock(&tree->rwlock);
+	fun_busy_unlock(fun);
 	
 	/* Delete ref added when inserting function into tree */
Index: uspace/srv/fs/ext4fs/Makefile
===================================================================
--- uspace/srv/fs/ext4fs/Makefile	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/fs/ext4fs/Makefile	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2012 Frantisek Princ
+# 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 $(LIBEXT4_PREFIX)/libext4.a
+EXTRA_CFLAGS += -I$(LIBBLOCK_PREFIX) -I$(LIBFS_PREFIX) -I$(LIBEXT4_PREFIX)
+BINARY = ext4fs
+
+SOURCES = \
+	ext4fs.c \
+	ext4fs_ops.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/ext4fs/ext4fs.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/fs/ext4fs/ext4fs.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 Frantisek Princ
+ * 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  ext4fs.c
+ * @brief Ext4 file system driver for HelenOS.
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <libfs.h>
+#include <ns.h>
+#include <stdio.h>
+#include <task.h>
+#include <ipc/services.h>
+#include "ext4fs.h"
+#include "../../vfs/vfs.h"
+
+#define NAME  "ext4fs"
+
+vfs_info_t ext4fs_vfs_info = {
+	.name = NAME,
+	.instance = 0
+};
+
+int main(int argc, char **argv)
+{
+	printf("%s: HelenOS ext4 file system server\n", NAME);
+	
+	if (argc == 3) {
+		if (!str_cmp(argv[1], "--instance"))
+			ext4fs_vfs_info.instance = strtol(argv[2], NULL, 10);
+		else {
+			printf("%s: Unrecognized parameters\n", NAME);
+			return 1;
+		}
+	}
+	
+	async_sess_t *vfs_sess = service_connect_blocking(EXCHANGE_SERIALIZE,
+	    SERVICE_VFS, 0, 0);
+	if (!vfs_sess) {
+		printf("%s: Failed to connect to VFS\n", NAME);
+		return 2;
+	}
+	
+	int rc = ext4fs_global_init();
+	if (rc != EOK) {
+		printf("%s: Global initialization failed\n", NAME);
+		return rc;
+	}
+	
+	rc = fs_register(vfs_sess, &ext4fs_vfs_info, &ext4fs_ops,
+	    &ext4fs_libfs_ops);
+	if (rc != EOK) {
+		printf("%s: Failed to register file system\n", NAME);
+		return rc;
+	}
+	
+	printf("%s: Accepting connections\n", NAME);
+	task_retval(0);
+	async_manager();
+	
+	/* Not reached */
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/ext4fs/ext4fs.h
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/fs/ext4fs/ext4fs.h	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Frantisek Princ
+ * 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 EXT4FS_EXT4FS_H_
+#define EXT4FS_EXT4FS_H_
+
+#include <libfs.h>
+
+extern vfs_out_ops_t ext4fs_ops;
+extern libfs_ops_t ext4fs_libfs_ops;
+
+extern int ext4fs_global_init(void);
+extern int ext4fs_global_fini(void);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,1511 @@
+/*
+ * Copyright (c) 2012 Frantisek Princ
+ * 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  ext4fs_ops.c
+ * @brief VFS operations for ext4 filesystem.
+ */
+
+#include <errno.h>
+#include <fibril_synch.h>
+#include <libext4.h>
+#include <libfs.h>
+#include <macros.h>
+#include <malloc.h>
+#include <adt/hash_table.h>
+#include <ipc/loc.h>
+#include "ext4fs.h"
+#include "../../vfs/vfs.h"
+
+#define EXT4FS_NODE(node) \
+	((node) ? (ext4fs_node_t *) (node)->data : NULL)
+
+#define OPEN_NODES_KEYS  2
+
+#define OPEN_NODES_DEV_HANDLE_KEY  0
+#define OPEN_NODES_INODE_KEY       1
+
+#define OPEN_NODES_BUCKETS  256
+
+/**
+ * Type for holding an instance of mounted partition.
+ */
+typedef struct ext4fs_instance {
+	link_t link;
+	service_id_t service_id;
+	ext4_filesystem_t *filesystem;
+	unsigned int open_nodes_count;
+} ext4fs_instance_t;
+
+/**
+ * Type for wrapping common fs_node and add some useful pointers.
+ */
+typedef struct ext4fs_node {
+	ext4fs_instance_t *instance;
+	ext4_inode_ref_t *inode_ref;
+	fs_node_t *fs_node;
+	link_t link;
+	unsigned int references;
+} ext4fs_node_t;
+
+/* 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);
+static int ext4fs_node_put_core(ext4fs_node_t *);
+
+/* Forward declarations of ext4 libfs operations. */
+
+static int ext4fs_root_get(fs_node_t **, service_id_t);
+static int ext4fs_match(fs_node_t **, fs_node_t *, const char *);
+static int ext4fs_node_get(fs_node_t **, service_id_t, fs_index_t);
+static int ext4fs_node_open(fs_node_t *);
+static int ext4fs_node_put(fs_node_t *);
+static int ext4fs_create_node(fs_node_t **, service_id_t, int);
+static int ext4fs_destroy_node(fs_node_t *);
+static int ext4fs_link(fs_node_t *, fs_node_t *, const char *);
+static int ext4fs_unlink(fs_node_t *, fs_node_t *, const char *);
+static int ext4fs_has_children(bool *, fs_node_t *);
+static fs_index_t ext4fs_index_get(fs_node_t *);
+static aoff64_t ext4fs_size_get(fs_node_t *);
+static unsigned ext4fs_lnkcnt_get(fs_node_t *);
+static bool ext4fs_is_directory(fs_node_t *);
+static bool ext4fs_is_file(fs_node_t *node);
+static service_id_t ext4fs_service_get(fs_node_t *node);
+
+/* Static variables */
+
+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);
+
+/* 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;
+}
+
+/** Compare given item with values in hash table.
+ *
+ */
+static int open_nodes_compare(unsigned long key[], hash_count_t keys,
+    link_t *item)
+{
+	assert(keys > 0);
+	
+	ext4fs_node_t *enode =
+	    hash_table_get_instance(item, ext4fs_node_t, link);
+	
+	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]);
+}
+
+/** Empty callback to correct hash table initialization.
+ *
+ */
+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,
+};
+
+/** Basic initialization of the driver.
+ *
+ * This is only needed to create the hash table
+ * for storing open nodes.
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_global_init(void)
+{
+	if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
+	    OPEN_NODES_KEYS, &open_nodes_ops))
+		return ENOMEM;
+	
+	return EOK;
+}
+
+/* Finalization of the driver.
+ *
+ * This is only needed to destroy the hash table.
+ *
+ * @return Error code
+ */
+int ext4fs_global_fini(void)
+{
+	hash_table_destroy(&open_nodes);
+	return EOK;
+}
+
+/*
+ * Ext4 libfs operations.
+ */
+
+/** Get instance from internal table by service_id.
+ *
+ * @param service_id Device identifier
+ * @param inst       Output instance if successful operation
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_instance_get(service_id_t service_id, ext4fs_instance_t **inst)
+{
+	fibril_mutex_lock(&instance_list_mutex);
+	
+	if (list_empty(&instance_list)) {
+		fibril_mutex_unlock(&instance_list_mutex);
+		return EINVAL;
+	}
+	
+	list_foreach(instance_list, link) {
+		ext4fs_instance_t *tmp =
+		    list_get_instance(link, ext4fs_instance_t, link);
+		
+		if (tmp->service_id == service_id) {
+			*inst = tmp;
+			fibril_mutex_unlock(&instance_list_mutex);
+			return EOK;
+		}
+	}
+	
+	fibril_mutex_unlock(&instance_list_mutex);
+	return EINVAL;
+}
+
+/** Get root node of filesystem specified by service_id.
+ * 
+ * @param rfn        Output pointer to loaded node
+ * @param service_id Device to load root node from
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_root_get(fs_node_t **rfn, service_id_t service_id)
+{
+	return ext4fs_node_get(rfn, service_id, EXT4_INODE_ROOT_INDEX);
+}
+
+/** Check if specified name (component) matches with any directory entry.
+ * 
+ * If match is found, load and return matching node.
+ *
+ * @param rfn       Output pointer to node if operation successful
+ * @param pfn       Parent directory node
+ * @param component Name to check directory for
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
+{
+	ext4fs_node_t *eparent = EXT4FS_NODE(pfn);
+	ext4_filesystem_t *fs = eparent->instance->filesystem;
+	
+	if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode,
+	    EXT4_INODE_MODE_DIRECTORY))
+		return ENOTDIR;
+	
+	/* Try to find entry */
+	ext4_directory_search_result_t result;
+	int rc = ext4_directory_find_entry(&result, eparent->inode_ref,
+	    component);
+	if (rc != EOK) {
+		if (rc == ENOENT) {
+			*rfn = NULL;
+			return EOK;
+		}
+		
+		return rc;
+	}
+	
+	/* Load node from search result */
+	uint32_t inode = ext4_directory_entry_ll_get_inode(result.dentry);
+	rc = ext4fs_node_get_core(rfn, eparent->instance, inode);
+	if (rc != EOK)
+		return rc;
+	
+	/* Destroy search result structure */
+	return ext4_directory_destroy_result(&result);
+}
+
+/** Get node specified by index
+ *
+ * It's wrapper for node_put_core operation
+ *
+ * @param rfn        Output pointer to loaded node if operation successful
+ * @param service_id Device identifier
+ * @param index      Node index (here i-node number)
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+	
+	return ext4fs_node_get_core(rfn, inst, index);
+}
+
+/** Main function for getting node from the filesystem. 
+ *
+ * @param rfn   Output point to loaded node if operation successful
+ * @param inst  Instance of filesystem
+ * @param index Index of node (i-node number)
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_node_get_core(fs_node_t **rfn, ext4fs_instance_t *inst,
+    fs_index_t index)
+{
+	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);
+	ext4fs_node_t *enode = NULL;
+	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;
+	}
+	
+	/* Prepare new enode */
+	enode = malloc(sizeof(ext4fs_node_t));
+	if (enode == NULL) {
+		fibril_mutex_unlock(&open_nodes_lock);
+		return ENOMEM;
+	}
+	
+	/* Prepare new fs_node and initialize */
+	fs_node_t *fs_node = malloc(sizeof(fs_node_t));
+	if (fs_node == NULL) {
+		free(enode);
+		fibril_mutex_unlock(&open_nodes_lock);
+		return ENOMEM;
+	}
+	
+	fs_node_initialize(fs_node);
+	
+	/* Load i-node from filesystem */
+	ext4_inode_ref_t *inode_ref;
+	int rc = ext4_filesystem_get_inode_ref(inst->filesystem, index,
+	    &inode_ref);
+	if (rc != EOK) {
+		free(enode);
+		free(fs_node);
+		fibril_mutex_unlock(&open_nodes_lock);
+		return rc;
+	}
+	
+	/* Initialize enode */
+	enode->inode_ref = inode_ref;
+	enode->instance = inst;
+	enode->references = 1;
+	enode->fs_node = fs_node;
+	link_initialize(&enode->link);
+	
+	fs_node->data = enode;
+	*rfn = fs_node;
+	
+	hash_table_insert(&open_nodes, key, &enode->link);
+	inst->open_nodes_count++;
+	
+	fibril_mutex_unlock(&open_nodes_lock);
+	
+	return EOK;
+}
+
+/** Put previously loaded node.
+ *
+ * @param enode Node to put back
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_node_put_core(ext4fs_node_t *enode)
+{
+	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--;
+	
+	/* Put inode back in filesystem */
+	int rc = ext4_filesystem_put_inode_ref(enode->inode_ref);
+	if (rc != EOK)
+		return rc;
+	
+	/* Destroy data structure */
+	free(enode->fs_node);
+	free(enode);
+	
+	return EOK;
+}
+
+/** Open node.
+ *
+ * This operation is stateless in this driver.
+ *
+ * @param fn Node to open
+ *
+ * @return EOK
+ *
+ */
+int ext4fs_node_open(fs_node_t *fn)
+{
+	/* Stateless operation */
+	return EOK;
+}
+
+/** Put previously loaded node.
+ *
+ * A wrapper for node_put_core operation
+ *
+ * @param fn Node to put back
+ * @return Error code
+ *
+ */
+int ext4fs_node_put(fs_node_t *fn)
+{
+	fibril_mutex_lock(&open_nodes_lock);
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	assert(enode->references > 0);
+	enode->references--;
+	if (enode->references == 0) {
+		int rc = ext4fs_node_put_core(enode);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&open_nodes_lock);
+			return rc;
+		}
+	}
+	
+	fibril_mutex_unlock(&open_nodes_lock);
+	
+	return EOK;
+}
+
+/** Create new node in filesystem.
+ *
+ * @param rfn        Output pointer to newly created node if successful
+ * @param service_id Device identifier, where the filesystem is
+ * @param flags      Flags for specification of new node parameters
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
+{
+	/* Allocate enode */
+	ext4fs_node_t *enode;
+	enode = malloc(sizeof(ext4fs_node_t));
+	if (enode == NULL)
+		return ENOMEM;
+	
+	/* Allocate fs_node */
+	fs_node_t *fs_node;
+	fs_node = malloc(sizeof(fs_node_t));
+	if (fs_node == NULL) {
+		free(enode);
+		return ENOMEM;
+	}
+	
+	/* Load instance */
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK) {
+		free(enode);
+		free(fs_node);
+		return rc;
+	}
+	
+	/* Allocate new i-node in filesystem */
+	ext4_inode_ref_t *inode_ref;
+	rc = ext4_filesystem_alloc_inode(inst->filesystem, &inode_ref, flags);
+	if (rc != EOK) {
+		free(enode);
+		free(fs_node);
+		return rc;
+	}
+	
+	/* Do some interconnections in references */
+	enode->inode_ref = inode_ref;
+	enode->instance = inst;
+	enode->references = 1;
+	
+	link_initialize(&enode->link);
+	
+	unsigned long key[] = {
+		[OPEN_NODES_DEV_HANDLE_KEY] = inst->service_id,
+		[OPEN_NODES_INODE_KEY] = inode_ref->index
+	};
+	
+	fibril_mutex_lock(&open_nodes_lock);
+	hash_table_insert(&open_nodes, key, &enode->link);
+	fibril_mutex_unlock(&open_nodes_lock);
+	inst->open_nodes_count++;
+	
+	enode->inode_ref->dirty = true;
+	
+	fs_node_initialize(fs_node);
+	fs_node->data = enode;
+	enode->fs_node = fs_node;
+	*rfn = fs_node;
+	
+	return EOK;
+}
+
+/** Destroy existing node.
+ *
+ * @param fs Node to destroy
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_destroy_node(fs_node_t *fn)
+{
+	/* If directory, check for children */
+	bool has_children;
+	int rc = ext4fs_has_children(&has_children, fn);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		return rc;
+	}
+	
+	if (has_children) {
+		ext4fs_node_put(fn);
+		return EINVAL;
+	}
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_inode_ref_t *inode_ref = enode->inode_ref;
+	
+	/* Release data blocks */
+	rc = ext4_filesystem_truncate_inode(inode_ref, 0);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		return rc;
+	}
+	
+	/*
+	 * TODO: Sset real deletion time when it will be supported.
+	 * Temporary set fake deletion time.
+	 */
+	ext4_inode_set_deletion_time(inode_ref->inode, 0xdeadbeef);
+	inode_ref->dirty = true;
+	
+	/* Free inode */
+	rc = ext4_filesystem_free_inode(inode_ref);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		return rc;
+	}
+	
+	return ext4fs_node_put(fn);
+}
+
+/** Link the specfied node to directory.
+ *
+ * @param pfn  Parent node to link in
+ * @param cfn  Node to be linked
+ * @param name Name which will be assigned to directory entry
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
+{
+	/* Check maximum name length */
+	if (str_size(name) > EXT4_DIRECTORY_FILENAME_LEN)
+		return ENAMETOOLONG;
+	
+	ext4fs_node_t *parent = EXT4FS_NODE(pfn);
+	ext4fs_node_t *child = EXT4FS_NODE(cfn);
+	ext4_filesystem_t *fs = parent->instance->filesystem;
+	
+	/* Add entry to parent directory */
+	int rc = ext4_directory_add_entry(parent->inode_ref, name,
+	    child->inode_ref);
+	if (rc != EOK)
+		return rc;
+	
+	/* Fill new dir -> add '.' and '..' entries */
+	if (ext4_inode_is_type(fs->superblock, child->inode_ref->inode,
+	    EXT4_INODE_MODE_DIRECTORY)) {
+		rc = ext4_directory_add_entry(child->inode_ref, ".",
+		    child->inode_ref);
+		if (rc != EOK) {
+			ext4_directory_remove_entry(parent->inode_ref, name);
+			return rc;
+		}
+		
+		rc = ext4_directory_add_entry(child->inode_ref, "..",
+		    parent->inode_ref);
+		if (rc != EOK) {
+			ext4_directory_remove_entry(parent->inode_ref, name);
+			ext4_directory_remove_entry(child->inode_ref, ".");
+			return rc;
+		}
+		
+		/* Initialize directory index if supported */
+		if (ext4_superblock_has_feature_compatible(fs->superblock,
+		    EXT4_FEATURE_COMPAT_DIR_INDEX)) {
+			rc = ext4_directory_dx_init(child->inode_ref);
+			if (rc != EOK)
+				return rc;
+			
+			ext4_inode_set_flag(child->inode_ref->inode,
+			    EXT4_INODE_FLAG_INDEX);
+			child->inode_ref->dirty = true;
+		}
+	
+		uint16_t parent_links =
+		    ext4_inode_get_links_count(parent->inode_ref->inode);
+		parent_links++;
+		ext4_inode_set_links_count(parent->inode_ref->inode, parent_links);
+		
+		parent->inode_ref->dirty = true;
+	}
+	
+	uint16_t child_links =
+	    ext4_inode_get_links_count(child->inode_ref->inode);
+	child_links++;
+	ext4_inode_set_links_count(child->inode_ref->inode, child_links);
+	
+	child->inode_ref->dirty = true;
+	
+	return EOK;
+}
+
+/** Unlink node from specified directory.
+ *
+ * @param pfn  Parent node to delete node from
+ * @param cfn  Child node to be unlinked from directory
+ * @param name Name of entry that will be removed
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *name)
+{
+	bool has_children;
+	int rc = ext4fs_has_children(&has_children, cfn);
+	if (rc != EOK)
+		return rc;
+	
+	/* Cannot unlink non-empty node */
+	if (has_children)
+		return ENOTEMPTY;
+	
+	/* Remove entry from parent directory */
+	ext4_inode_ref_t *parent = EXT4FS_NODE(pfn)->inode_ref;
+	rc = ext4_directory_remove_entry(parent, name);
+	if (rc != EOK)
+		return rc;
+	
+	/* Decrement links count */
+	ext4_inode_ref_t *child_inode_ref = EXT4FS_NODE(cfn)->inode_ref;
+	
+	uint32_t lnk_count =
+	    ext4_inode_get_links_count(child_inode_ref->inode);
+	lnk_count--;
+	
+	/* If directory - handle links from parent */
+	if ((lnk_count <= 1) && (ext4fs_is_directory(cfn))) {
+		assert(lnk_count == 1);
+		
+		lnk_count--;
+		
+		ext4_inode_ref_t *parent_inode_ref = EXT4FS_NODE(pfn)->inode_ref;
+		
+		uint32_t parent_lnk_count = ext4_inode_get_links_count(
+		    parent_inode_ref->inode);
+		
+		parent_lnk_count--;
+		ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count);
+		
+		parent->dirty = true;
+	}
+
+	/*
+	 * TODO: Update timestamps of the parent
+	 * (when we have wall-clock time).
+	 *
+	 * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
+	 * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
+	 * parent->dirty = true;
+	 */
+	
+	/*
+	 * TODO: Update timestamp for inode.
+	 *
+	 * ext4_inode_set_change_inode_time(child_inode_ref->inode,
+	 *     (uint32_t) now);
+	 */
+	
+	ext4_inode_set_links_count(child_inode_ref->inode, lnk_count);
+	child_inode_ref->dirty = true;
+	
+	return EOK;
+}
+
+/** Check if specified node has children.
+ *
+ * For files is response allways false and check is executed only for directories.
+ *
+ * @param has_children Output value for response
+ * @param fn           Node to check
+ *
+ * @return Error code
+ *
+ */
+int ext4fs_has_children(bool *has_children, fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_filesystem_t *fs = enode->instance->filesystem;
+	
+	/* Check if node is directory */
+	if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode,
+	    EXT4_INODE_MODE_DIRECTORY)) {
+		*has_children = false;
+		return EOK;
+	}
+	
+	ext4_directory_iterator_t it;
+	int rc = ext4_directory_iterator_init(&it, enode->inode_ref, 0);
+	if (rc != EOK)
+		return rc;
+	
+	/* Find a non-empty directory entry */
+	bool found = false;
+	while (it.current != NULL) {
+		if (it.current->inode != 0) {
+			uint16_t name_size =
+			    ext4_directory_entry_ll_get_name_length(fs->superblock,
+			    it.current);
+			if (!ext4fs_is_dots(it.current->name, name_size)) {
+				found = true;
+				break;
+			}
+		}
+		
+		rc = ext4_directory_iterator_next(&it);
+		if (rc != EOK) {
+			ext4_directory_iterator_fini(&it);
+			return rc;
+		}
+	}
+	
+	rc = ext4_directory_iterator_fini(&it);
+	if (rc != EOK)
+		return rc;
+	
+	*has_children = found;
+	
+	return EOK;
+}
+
+/** Unpack index number from node.
+ *
+ * @param fn Node to load index from
+ *
+ * @return Index number of i-node
+ *
+ */
+fs_index_t ext4fs_index_get(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	return enode->inode_ref->index;
+}
+
+/** Get real size of file / directory.
+ *
+ * @param fn Node to get size of
+ *
+ * @return Real size of node
+ *
+ */
+aoff64_t ext4fs_size_get(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_superblock_t *sb = enode->instance->filesystem->superblock;
+	return ext4_inode_get_size(sb, enode->inode_ref->inode);
+}
+
+/** Get number of links to specified node.
+ *
+ * @param fn Node to get links to
+ *
+ * @return Number of links
+ *
+ */
+unsigned ext4fs_lnkcnt_get(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode);
+	
+	if (ext4fs_is_directory(fn)) {
+		if (lnkcnt > 1)
+			return 1;
+		else
+			return 0;
+	}
+	
+	/* For regular files return real links count */
+	return lnkcnt;
+}
+
+/** Check if node is directory.
+ *
+ * @param fn Node to check
+ *
+ * @return Result of check
+ *
+ */
+bool ext4fs_is_directory(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_superblock_t *sb = enode->instance->filesystem->superblock;
+	
+	return ext4_inode_is_type(sb, enode->inode_ref->inode,
+	    EXT4_INODE_MODE_DIRECTORY);
+}
+
+/** Check if node is regular file.
+ *
+ * @param fn Node to check
+ *
+ * @return Result of check
+ *
+ */
+bool ext4fs_is_file(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_superblock_t *sb = enode->instance->filesystem->superblock;
+	
+	return ext4_inode_is_type(sb, enode->inode_ref->inode,
+	    EXT4_INODE_MODE_FILE);
+}
+
+/** Extract device identifier from node.
+ *
+ * @param node Node to extract id from
+ *
+ * @return id of device, where is the filesystem
+ *
+ */
+service_id_t ext4fs_service_get(fs_node_t *fn)
+{
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	return enode->instance->service_id;
+}
+
+/*
+ * libfs operations.
+ */
+libfs_ops_t ext4fs_libfs_ops = {
+	.root_get = ext4fs_root_get,
+	.match = ext4fs_match,
+	.node_get = ext4fs_node_get,
+	.node_open = ext4fs_node_open,
+	.node_put = ext4fs_node_put,
+	.create = ext4fs_create_node,
+	.destroy = ext4fs_destroy_node,
+	.link = ext4fs_link,
+	.unlink = ext4fs_unlink,
+	.has_children = ext4fs_has_children,
+	.index_get = ext4fs_index_get,
+	.size_get = ext4fs_size_get,
+	.lnkcnt_get = ext4fs_lnkcnt_get,
+	.is_directory = ext4fs_is_directory,
+	.is_file = ext4fs_is_file,
+	.service_get = ext4fs_service_get
+};
+
+/*
+ * VFS operations.
+ */
+
+/** Mount operation.
+ *
+ * Try to mount specified filesystem from device.
+ *
+ * @param service_id Identifier of device
+ * @param opts       Mount options
+ * @param index      Output value - index of root node
+ * @param size       Output value - size of root node
+ * @param lnkcnt     Output value - link count of root node
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_mounted(service_id_t service_id, const char *opts,
+    fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
+{
+	/* Allocate libext4 filesystem structure */
+	ext4_filesystem_t *fs = (ext4_filesystem_t *)
+	    malloc(sizeof(ext4_filesystem_t));
+	if (fs == NULL)
+		return ENOMEM;
+	
+	/* Allocate instance structure */
+	ext4fs_instance_t *inst = (ext4fs_instance_t *)
+	    malloc(sizeof(ext4fs_instance_t));
+	if (inst == NULL) {
+		free(fs);
+		return ENOMEM;
+	}
+	
+	enum cache_mode cmode;
+	if (str_cmp(opts, "wtcache") == 0)
+		cmode = CACHE_MODE_WT;
+	else
+		cmode = CACHE_MODE_WB;
+	
+	/* Initialize the filesystem */
+	int rc = ext4_filesystem_init(fs, service_id, cmode);
+	if (rc != EOK) {
+		free(fs);
+		free(inst);
+		return rc;
+	}
+	
+	/* Do some sanity checking */
+	rc = ext4_filesystem_check_sanity(fs);
+	if (rc != EOK) {
+		ext4_filesystem_fini(fs);
+		free(fs);
+		free(inst);
+		return rc;
+	}
+	
+	/* Check flags */
+	bool read_only;
+	rc = ext4_filesystem_check_features(fs, &read_only);
+	if (rc != EOK) {
+		ext4_filesystem_fini(fs);
+		free(fs);
+		free(inst);
+		return rc;
+	}
+	
+	/* Initialize instance */
+	link_initialize(&inst->link);
+	inst->service_id = service_id;
+	inst->filesystem = fs;
+	inst->open_nodes_count = 0;
+	
+	/* Read root node */
+	fs_node_t *root_node;
+	rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
+	if (rc != EOK) {
+		ext4_filesystem_fini(fs);
+		free(fs);
+		free(inst);
+		return rc;
+	}
+	
+	/* Add instance to the list */
+	fibril_mutex_lock(&instance_list_mutex);
+	list_append(&inst->link, &instance_list);
+	fibril_mutex_unlock(&instance_list_mutex);
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(root_node);
+	
+	*index = EXT4_INODE_ROOT_INDEX;
+	*size = ext4_inode_get_size(fs->superblock, enode->inode_ref->inode);
+	*lnkcnt = 1;
+	
+	ext4fs_node_put(root_node);
+	
+	return EOK;
+}
+
+/** Unmount operation.
+ *
+ * Correctly release the filesystem.
+ *
+ * @param service_id Device to be unmounted
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_unmounted(service_id_t service_id)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+	
+	fibril_mutex_lock(&open_nodes_lock);
+	
+	if (inst->open_nodes_count != 0) {
+		fibril_mutex_unlock(&open_nodes_lock);
+		return EBUSY;
+	}
+	
+	/* Remove the instance from the list */
+	fibril_mutex_lock(&instance_list_mutex);
+	list_remove(&inst->link);
+	fibril_mutex_unlock(&instance_list_mutex);
+	
+	fibril_mutex_unlock(&open_nodes_lock);
+	
+	return ext4_filesystem_fini(inst->filesystem);
+}
+
+/** Read bytes from node.
+ *
+ * @param service_id Device to read data from
+ * @param index      Number of node to read from
+ * @param pos        Position where the read should be started
+ * @param rbytes     Output value, where the real size was returned
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
+	/*
+	 * 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;
+	}
+	
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+	
+	/* Load i-node */
+	ext4_inode_ref_t *inode_ref;
+	rc = ext4_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+	
+	/* Read from i-node by type */
+	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;
+}
+
+/** Check if filename is dot or dotdot (reserved names).
+ *
+ * @param name      Name to check
+ * @param name_size Length of string name
+ *
+ * @return Result of the check
+ *
+ */
+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;
+}
+
+/** Read data from directory.
+ *
+ * @param callid    IPC id of call (for communication)
+ * @param pos       Position to start reading from
+ * @param size      How many bytes to read
+ * @param inst      Filesystem instance
+ * @param inode_ref Node to read data from
+ * @param rbytes    Output value to return real number of bytes was read
+ *
+ * @return Error code
+ *
+ */
+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)
+{
+	ext4_directory_iterator_t it;
+	int rc = ext4_directory_iterator_init(&it, 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
+	 */
+	bool found = false;
+	while (it.current != NULL) {
+		if (it.current->inode == 0)
+			goto skip;
+		
+		uint16_t 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
+		 */
+		uint8_t *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;
+		}
+	}
+	
+	uint64_t next;
+	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;
+	
+	/* Prepare return values */
+	if (found) {
+		*rbytes = next - pos;
+		return EOK;
+	} else {
+		async_answer_0(callid, ENOENT);
+		return ENOENT;
+	}
+}
+
+/** Read data from file.
+ *
+ * @param callid    IPC id of call (for communication)
+ * @param pos       Position to start reading from
+ * @param size      How many bytes to read
+ * @param inst      Filesystem instance
+ * @param inode_ref Node to read data from
+ * @param rbytes    Output value to return real number of bytes was read
+ *
+ * @return Error code
+ *
+ */
+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)
+{
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	uint64_t file_size = ext4_inode_get_size(sb, 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 */
+	uint32_t block_size = ext4_superblock_get_block_size(sb);
+	aoff64_t file_block = pos / block_size;
+	uint32_t offset_in_block = pos % block_size;
+	uint32_t 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 */
+	uint32_t fs_block;
+	int rc = ext4_filesystem_get_inode_data_block_index(inode_ref,
+	    file_block, &fs_block);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return rc;
+	}
+	
+	/*
+	 * Check for sparse file.
+	 * If ext4_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
+	 */
+	uint8_t *buffer;
+	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 */
+	block_t *block;
+	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;
+}
+
+/** Write bytes to file
+ *
+ * @param service_id Device identifier
+ * @param index      I-node number of file
+ * @param pos        Position in file to start reading from
+ * @param wbytes     Output value - real number of written bytes
+ * @param nsize      Output value - new size of i-node
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
+	fs_node_t *fn;
+	int rc = ext4fs_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	
+	ipc_callid_t callid;
+	size_t len;
+	if (!async_data_write_receive(&callid, &len)) {
+		ext4fs_node_put(fn);
+		async_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_filesystem_t *fs = enode->instance->filesystem;
+	
+	uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
+	
+	/* Prevent writing to more than one block */
+	uint32_t bytes = min(len, block_size - (pos % block_size));
+	
+	int flags = BLOCK_FLAGS_NONE;
+	if (bytes == block_size)
+		flags = BLOCK_FLAGS_NOREAD;
+	
+	uint32_t iblock =  pos / block_size;
+	uint32_t fblock;
+	
+	/* Load inode */
+	ext4_inode_ref_t *inode_ref = enode->inode_ref;
+	rc = ext4_filesystem_get_inode_data_block_index(inode_ref, iblock,
+	    &fblock);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		async_answer_0(callid, rc);
+		return rc;
+	}
+	
+	/* Check for sparse file */
+	if (fblock == 0) {
+		if ((ext4_superblock_has_feature_incompatible(fs->superblock,
+		    EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
+		    (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
+			uint32_t last_iblock =
+			    ext4_inode_get_size(fs->superblock, inode_ref->inode) /
+			    block_size;
+			
+			while (last_iblock < iblock) {
+				rc = ext4_extent_append_block(inode_ref, &last_iblock,
+				    &fblock, true);
+				if (rc != EOK) {
+					ext4fs_node_put(fn);
+					async_answer_0(callid, rc);
+					return rc;
+				}
+			}
+			
+			rc = ext4_extent_append_block(inode_ref, &last_iblock,
+			    &fblock, false);
+			if (rc != EOK) {
+				ext4fs_node_put(fn);
+				async_answer_0(callid, rc);
+				return rc;
+			}
+		} else {
+			rc = ext4_balloc_alloc_block(inode_ref, &fblock);
+			if (rc != EOK) {
+				ext4fs_node_put(fn);
+				async_answer_0(callid, rc);
+				return rc;
+			}
+			
+			rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
+			    iblock, fblock);
+			if (rc != EOK) {
+				ext4_balloc_free_block(inode_ref, fblock);
+				ext4fs_node_put(fn);
+				async_answer_0(callid, rc);
+				return rc;
+			}
+		}
+		
+		flags = BLOCK_FLAGS_NOREAD;
+		inode_ref->dirty = true;
+	}
+	
+	/* Load target block */
+	block_t *write_block;
+	rc = block_get(&write_block, service_id, fblock, flags);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		async_answer_0(callid, rc);
+		return rc;
+	}
+	
+	if (flags == BLOCK_FLAGS_NOREAD)
+		memset(write_block->data, 0, block_size);
+	
+	rc = async_data_write_finalize(callid, write_block->data +
+	    (pos % block_size), bytes);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		return rc;
+	}
+	
+	write_block->dirty = true;
+	
+	rc = block_put(write_block);
+	if (rc != EOK) {
+		ext4fs_node_put(fn);
+		return rc;
+	}
+	
+	/* Do some counting */
+	uint32_t old_inode_size = ext4_inode_get_size(fs->superblock,
+	    inode_ref->inode);
+	if (pos + bytes > old_inode_size) {
+		ext4_inode_set_size(inode_ref->inode, pos + bytes);
+		inode_ref->dirty = true;
+	}
+	
+	*nsize = ext4_inode_get_size(fs->superblock, inode_ref->inode);
+	*wbytes = bytes;
+	
+	return ext4fs_node_put(fn);
+}
+
+/** Truncate file.
+ *
+ * Only the direction to shorter file is supported.
+ *
+ * @param service_id Device identifier
+ * @param index      Index if node to truncated
+ * @param new_size   New size of file
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_truncate(service_id_t service_id, fs_index_t index,
+    aoff64_t new_size)
+{
+	fs_node_t *fn;
+	int rc = ext4fs_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	ext4_inode_ref_t *inode_ref = enode->inode_ref;
+	
+	rc = ext4_filesystem_truncate_inode(inode_ref, new_size);
+	ext4fs_node_put(fn);
+	
+	return rc;
+}
+
+/** Close file.
+ *
+ * @param service_id Device identifier
+ * @param index      I-node number
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_close(service_id_t service_id, fs_index_t index)
+{
+	return EOK;
+}
+
+/** Destroy node specified by index.
+ *
+ * @param service_id Device identifier
+ * @param index      I-node to destroy
+ *
+ * @return Error code
+ *
+ */
+static int ext4fs_destroy(service_id_t service_id, fs_index_t index)
+{
+	fs_node_t *fn;
+	int rc = ext4fs_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	
+	/* Destroy the inode */
+	return ext4fs_destroy_node(fn);
+}
+
+/** Enforce inode synchronization (write) to device.
+ *
+ * @param service_id Device identifier
+ * @param index      I-node number.
+ *
+ */
+static int ext4fs_sync(service_id_t service_id, fs_index_t index)
+{
+	fs_node_t *fn;
+	int rc = ext4fs_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	
+	ext4fs_node_t *enode = EXT4FS_NODE(fn);
+	enode->inode_ref->dirty = true;
+	
+	return ext4fs_node_put(fn);
+}
+
+/** VFS operations
+ *
+ */
+vfs_out_ops_t ext4fs_ops = {
+	.mounted = ext4fs_mounted,
+	.unmounted = ext4fs_unmounted,
+	.read = ext4fs_read,
+	.write = ext4fs_write,
+	.truncate = ext4fs_truncate,
+	.close = ext4fs_close,
+	.destroy = ext4fs_destroy,
+	.sync = ext4fs_sync
+};
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/tmpfs/tmpfs_dump.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_dump.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/fs/tmpfs/tmpfs_dump.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -49,4 +49,6 @@
 #define TMPFS_COMM_SIZE		1024
 
+static uint8_t tmpfs_buf[TMPFS_COMM_SIZE];
+
 struct rdentry {
 	uint8_t type;
@@ -68,5 +70,5 @@
 		uint32_t size;
 		
-		if (block_seqread(dsid, bufpos, buflen, pos, &entry,
+		if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, &entry,
 		    sizeof(entry)) != EOK)
 			return false;
@@ -88,5 +90,5 @@
 			}
 			
-			if (block_seqread(dsid, bufpos, buflen, pos, fname,
+			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, fname,
 			    entry.len) != EOK) {
 				(void) ops->destroy(fn);
@@ -104,5 +106,5 @@
 			free(fname);
 			
-			if (block_seqread(dsid, bufpos, buflen, pos, &size,
+			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, &size,
 			    sizeof(size)) != EOK)
 				return false;
@@ -116,5 +118,5 @@
 			
 			nodep->size = size;
-			if (block_seqread(dsid, bufpos, buflen, pos, nodep->data,
+			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, nodep->data,
 			    size) != EOK)
 				return false;
@@ -132,5 +134,5 @@
 			}
 			
-			if (block_seqread(dsid, bufpos, buflen, pos, fname,
+			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, fname,
 			    entry.len) != EOK) {
 				(void) ops->destroy(fn);
@@ -176,5 +178,5 @@
 	
 	char tag[6];
-	if (block_seqread(dsid, &bufpos, &buflen, &pos, tag, 5) != EOK)
+	if (block_seqread(dsid, tmpfs_buf, &bufpos, &buflen, &pos, tag, 5) != EOK)
 		goto error;
 	
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/hid/console/console.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -617,5 +617,5 @@
 	
 	size_t pos = 0;
-
+	
 	/*
 	 * Read input from keyboard and copy it to the buffer.
@@ -628,15 +628,17 @@
 			buf[pos] = cons->char_remains[0];
 			pos++;
+			
 			/* Unshift the array. */
-			for (size_t i = 1; i < cons->char_remains_len; i++) {
+			for (size_t i = 1; i < cons->char_remains_len; i++)
 				cons->char_remains[i - 1] = cons->char_remains[i];
-			}
+			
 			cons->char_remains_len--;
 		}
+		
 		/* Still not enough? Then get another key from the queue. */
 		if (pos < size) {
 			link_t *link = prodcons_consume(&cons->input_pc);
 			kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
-
+			
 			/* Accept key presses of printable chars only. */
 			if ((event->type == KEY_PRESS) && (event->c != 0)) {
@@ -645,5 +647,5 @@
 				cons->char_remains_len = str_size(cons->char_remains);
 			}
-
+			
 			free(event);
 		}
Index: uspace/srv/hid/input/port/ns16550.c
===================================================================
--- uspace/srv/hid/input/port/ns16550.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/hid/input/port/ns16550.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -84,5 +84,5 @@
 	},
 	{
-		.cmd = CMD_BTEST,
+		.cmd = CMD_AND,
 		.value = LSR_DATA_READY,
 		.srcarg = 1,
Index: uspace/srv/hid/input/port/pl050.c
===================================================================
--- uspace/srv/hid/input/port/pl050.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/hid/input/port/pl050.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -80,5 +80,5 @@
 	},
 	{
-		.cmd = CMD_BTEST,
+		.cmd = CMD_AND,
 		.value = PL050_STAT_RXFULL,
 		.srcarg = 1,
Index: uspace/srv/hw/bus/cuda_adb/cuda_adb.c
===================================================================
--- uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -116,9 +116,9 @@
 	{
 		.cmd = CMD_PIO_READ_8,
-		.addr = NULL,	/* will be patched in run-time */
+		.addr = NULL,
 		.dstarg = 1
 	},
 	{
-		.cmd = CMD_BTEST,
+		.cmd = CMD_AND,
 		.value = SR_INT,
 		.srcarg = 1,
Index: uspace/srv/loader/Makefile
===================================================================
--- uspace/srv/loader/Makefile	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/loader/Makefile	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -44,5 +44,5 @@
 GENERIC_SOURCES = \
 	main.c \
-	interp.s
+	interp.S
 
 SOURCES = \
Index: uspace/srv/loader/interp.S
===================================================================
--- uspace/srv/loader/interp.S	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
+++ uspace/srv/loader/interp.S	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -0,0 +1,21 @@
+#
+# Provide a string to be included in a special DT_INTERP header, even though
+# this is a statically-linked executable. This will mark the binary as
+# the program loader.
+#
+
+#if ((defined(UARCH_abs32le)) && (defined(COMPILER_gcc_cross)) \
+    && (defined(CROSS_TARGET_arm32)))
+	#define ATSIGN(arg)  % ## arg
+#endif
+
+#ifdef UARCH_arm32
+	#define ATSIGN(arg)  % ## arg
+#endif
+
+#ifndef ATSIGN
+	#define ATSIGN(arg)  @ ## arg
+#endif
+
+.section .interp, "a", ATSIGN(progbits)
+	.string "kernel"
Index: uspace/srv/loader/interp.s
===================================================================
--- uspace/srv/loader/interp.s	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ 	(revision )
@@ -1,7 +1,0 @@
-#
-# Provide a string to be included in a special DT_INTERP header, even though
-# this is a statically-linked executable. This will mark the binary as
-# the program loader.
-#
-.section .interp , ""
-	.string "kernel"
Index: uspace/srv/net/tcp/sock.c
===================================================================
--- uspace/srv/net/tcp/sock.c	(revision 76d92db112f9f96e53f73ce4e71c158bbd030916)
+++ uspace/srv/net/tcp/sock.c	(revision cddcc4a3904768c1032861a518e712bd08ea3156)
@@ -779,4 +779,9 @@
 	}
 
+	/* Grab recv_buffer_lock because of CV wait in tcp_sock_recv_fibril() */
+	fibril_mutex_lock(&socket->recv_buffer_lock);
+	socket->sock_core = NULL;
+	fibril_mutex_unlock(&socket->recv_buffer_lock);
+
 	rc = socket_destroy(NULL, socket_id, &client->sockets, &gsock,
 	    tcp_free_sock_data);
@@ -839,8 +844,9 @@
 	log_msg(LVL_DEBUG, "tcp_sock_recv_fibril()");
 
+	fibril_mutex_lock(&sock->recv_buffer_lock);
+
 	while (true) {
 		log_msg(LVL_DEBUG, "call tcp_uc_receive()");
-		fibril_mutex_lock(&sock->recv_buffer_lock);
-		while (sock->recv_buffer_used != 0)
+		while (sock->recv_buffer_used != 0 && sock->sock_core != NULL)
 			fibril_condvar_wait(&sock->recv_buffer_cv,
 			    &sock->recv_buffer_lock);
@@ -852,6 +858,6 @@
 			sock->recv_error = trc;
 			fibril_condvar_broadcast(&sock->recv_buffer_cv);
-			fibril_mutex_unlock(&sock->recv_buffer_lock);
-			tcp_sock_notify_data(sock->sock_core);
+			if (sock->sock_core != NULL)
+				tcp_sock_notify_data(sock->sock_core);
 			break;
 		}
@@ -861,7 +867,9 @@
 		sock->recv_buffer_used = data_len;
 		fibril_condvar_broadcast(&sock->recv_buffer_cv);
-		fibril_mutex_unlock(&sock->recv_buffer_lock);
-		tcp_sock_notify_data(sock->sock_core);
-	}
+		if (sock->sock_core != NULL)
+			tcp_sock_notify_data(sock->sock_core);
+	}
+
+	fibril_mutex_unlock(&sock->recv_buffer_lock);
 
 	tcp_uc_delete(sock->conn);
