Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ HelenOS.config	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -88,4 +88,5 @@
 @ "tmpfs" TMPFS image
 @ "fat" FAT16 image
+@ "ext2fs" EXT2 image
 ! RDFMT (choice)
 
Index: boot/Makefile
===================================================================
--- boot/Makefile	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ boot/Makefile	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -50,4 +50,7 @@
 	$(MKFAT) 1048576 $(DIST_PATH) $@
 endif
+ifeq ($(RDFMT),ext2fs)
+	$(MKEXT2) 1048576 $(DIST_PATH) $@
+endif
 
 build_dist: clean_dist
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ boot/Makefile.common	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -56,4 +56,5 @@
 MKTMPFS = $(TOOLS_PATH)/mktmpfs.py
 MKFAT = $(TOOLS_PATH)/mkfat.py
+MKEXT2 = $(TOOLS_PATH)/mkext2.py
 MKUIMAGE = $(TOOLS_PATH)/mkuimage.py
 
@@ -82,4 +83,8 @@
 ifeq ($(RDFMT),fat)
 	INIT_TASKS += $(USPACE_PATH)/srv/fs/fat/fat
+endif
+
+ifeq ($(RDFMT),ext2fs)
+	INIT_TASKS += $(USPACE_PATH)/srv/fs/ext2fs/ext2fs
 endif
 
Index: tools/imgutil.py
===================================================================
--- tools/imgutil.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
+++ tools/imgutil.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2008 Martin Decky
+# Copyright (c) 2011 Martin Sucha
+# 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.
+#
+
+"""
+Utilities for filesystem image creators
+"""
+
+import os
+import stat
+
+exclude_names = set(['.svn', '.bzr'])
+
+def align_up(size, alignment):
+	"Return size aligned up to alignment"
+	
+	if (size % alignment == 0):
+		return size
+	
+	return ((size // alignment) + 1) * alignment
+
+def count_up(size, alignment):
+	"Return units necessary to fit the size"
+	
+	if (size % alignment == 0):
+		return (size // alignment)
+	
+	return ((size // alignment) + 1)
+
+def num_of_trailing_bin_zeros(num):
+	"Return number of trailing zeros in binary representation"
+	
+	i = 0
+	if (num == 0): raise ValueError()
+	while num & 1 == 0:
+		i += 1
+		num = num >> 1
+	return i
+
+def get_bit(number, n):
+	"Return True if n-th least-significant bit is set in the given number"
+	
+	return bool((number >> n) & 1)
+
+def set_bit(number, n):
+	"Return the number with n-th least-significant bit set"
+	
+	return number | (1 << n)
+
+class ItemToPack:
+	"Stores information about one directory item to be added to the image"
+	
+	def __init__(self, parent, name):
+		self.parent = parent
+		self.name = name
+		self.path = os.path.join(parent, name)
+		self.stat = os.stat(self.path)
+		self.is_dir = stat.S_ISDIR(self.stat.st_mode)
+		self.is_file = stat.S_ISREG(self.stat.st_mode)
+		self.size = self.stat.st_size
+
+def listdir_items(path):
+	"Return a list of items to be packed inside a fs image"
+	
+	for name in os.listdir(path):
+		if name in exclude_names:
+			continue
+		item = ItemToPack(path, name)
+		if not (item.is_dir or item.is_file):
+			continue
+		yield item
+
+def chunks(item, chunk_size):
+	"Iterate contents of a file in chunks of a given size"
+	
+	inf = open(item.path, 'rb')
+	rd = 0
+	while (rd < item.size):
+		data = bytes(inf.read(chunk_size))
+		yield data
+		rd += len(data)
+	inf.close()
Index: tools/mkext2.py
===================================================================
--- tools/mkext2.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
+++ tools/mkext2.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -0,0 +1,676 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2011 Martin Sucha
+# 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.
+#
+
+"""
+EXT2 creator
+"""
+
+import sys
+import os
+import xstruct
+import array
+import time
+import uuid
+from imgutil import *
+
+GDE_SIZE = 32
+
+STRUCT_DIR_ENTRY_HEAD = """little:
+	uint32_t inode       /* inode number */
+	uint16_t skip        /* byte offset to the next record */
+	uint8_t  name_length /* file attributes */
+	uint8_t  inode_type  /* type of the referenced inode */
+"""
+
+STRUCT_SUPERBLOCK = """little:
+	uint32_t total_inode_count    /* Total number of inodes */
+	uint32_t total_block_count    /* Total number of blocks */
+	uint32_t reserved_block_count /* Total number of reserved blocks */
+	uint32_t free_block_count     /* Total number of free blocks */
+	uint32_t free_inode_count     /* Total number of free inodes */
+	uint32_t first_block          /* Block containing the superblock */
+	uint32_t block_size_log2      /* log_2(block_size) */
+	int32_t  fragment_size_log2   /* log_2(fragment size) */
+	uint32_t blocks_per_group     /* Number of blocks in one block group */
+	uint32_t fragments_per_group  /* Number of fragments per block group */
+	uint32_t inodes_per_group     /* Number of inodes per block group */
+	uint32_t mount_time           /* Time when the filesystem was last mounted */
+	uint32_t write_time           /* Time when the filesystem was last written */
+	uint16_t mount_count          /* Mount count since last full filesystem check */
+	uint16_t max_mount_count      /* Number of mounts after which the fs must be checked */
+	uint16_t magic                /* Magic value */
+	uint16_t state                /* State (mounted/unmounted) */
+	uint16_t error_behavior       /* What to do when errors are encountered */
+	uint16_t rev_minor            /* Minor revision level */
+	uint32_t last_check_time      /* Unix time of last fs check */
+	uint32_t max_check_interval   /* Max unix time interval between checks */
+	uint32_t os                   /* OS that created the filesystem */
+	uint32_t rev_major            /* Major revision level */
+	padding[4] /* default reserved uid and gid */
+	
+	/* Following is for ext2 revision 1 only */
+	uint32_t first_inode
+	uint16_t inode_size
+	uint16_t block_group_number /* Block group where this SB is stored */
+	uint32_t features_compatible
+	uint32_t features_incompatible
+	uint32_t features_read_only
+	char     uuid[16]
+	char     volume_name[16]
+"""
+
+STRUCT_BLOCK_GROUP_DESCRIPTOR = """little:
+	uint32_t block_bitmap_block      /* Block ID for block bitmap */
+	uint32_t inode_bitmap_block      /* Block ID for inode bitmap */
+	uint32_t inode_table_first_block /* Block ID of first block of inode table */
+	uint16_t free_block_count        /* Count of free blocks */
+	uint16_t free_inode_count        /* Count of free inodes */
+	uint16_t directory_inode_count   /* Number of inodes allocated to directories */
+"""
+
+STRUCT_INODE = """little:
+	uint16_t mode
+	uint16_t user_id
+	uint32_t size
+	uint32_t access_time
+	uint32_t creation_time
+	uint32_t modification_time
+	uint32_t deletion_time
+	uint16_t group_id
+	uint16_t usage_count /* Hard link count, when 0 the inode is to be freed */
+	uint32_t reserved_512_blocks /* Size of this inode in 512-byte blocks */
+	uint32_t flags
+	padding[4]
+	uint32_t direct_blocks[12] /* Direct block ids stored in this inode */
+	uint32_t indirect_blocks[3]
+	uint32_t version
+	uint32_t file_acl
+	uint32_t size_high /* For regular files in version >= 1, dir_acl if dir */
+	padding[6]
+	uint16_t mode_high /* Hurd only */
+	uint16_t user_id_high /* Linux/Hurd only */
+	uint16_t group_id_high /* Linux/Hurd only */
+"""
+
+# The following is here to handle byte-order conversion in indirect block lookups
+STRUCT_BLOCK_REFERENCE = """little:
+	uint32_t block_id /* Referenced block ID */
+"""
+
+class Filesystem:	
+	def __init__(self, filename, block_groups, blocks_per_group, inodes_per_group, block_size, inode_size, reserved_inode_count):
+		"Initialize the filesystem writer"
+		
+		outf = open(filename, "w+b")
+		# Set the correct size of the image, so that we can read arbitrary position
+		outf.truncate(block_size * blocks_per_group * block_groups)
+		self.outf = outf
+		self.uuid = uuid.uuid4()
+		self.block_size = block_size
+		self.inode_size = inode_size
+		self.block_groups = block_groups
+		self.blocks_per_group = blocks_per_group
+		self.inodes_per_group = inodes_per_group
+		self.reserved_inode_count = reserved_inode_count
+		self.gdt_blocks = count_up(block_groups * GDE_SIZE, block_size)
+		self.inode_table_blocks_per_group = (inodes_per_group * inode_size) // block_size
+		self.inode_bitmap_blocks_per_group = count_up(inodes_per_group // 8, block_size)
+		self.block_bitmap_blocks_per_group = count_up(blocks_per_group // 8, block_size)
+		self.total_block_count = self.blocks_per_group * self.block_groups
+		self.total_inode_count = self.inodes_per_group * self.block_groups
+		self.inode_table_blocks = count_up(self.total_inode_count * inode_size, block_size)
+		self.inodes = {}
+		self.superblock_at_block = 2047 // block_size
+		self.block_ids_per_block = self.block_size // 4
+		self.indirect_limits = [12, None, None, None]
+		self.indirect_blocks_per_level = [1, None, None, None]
+		for i in range(1,4):
+			self.indirect_blocks_per_level[i] = self.indirect_blocks_per_level[i-1] * self.block_ids_per_block
+			self.indirect_limits[i] = self.indirect_limits[i-1] + self.indirect_blocks_per_level[i]
+		self.block_allocator = Allocator(0, self.total_block_count)
+		self.inode_allocator = Allocator(1, self.total_inode_count)
+		self.init_gdt()
+		# Set the callbacks after GDT has been initialized
+		self.block_allocator.mark_cb = self.mark_block_cb
+		self.inode_allocator.mark_cb = self.mark_inode_cb
+		self.block_allocator.mark_used_all(range(self.superblock_at_block))
+		self.inode_allocator.mark_used_all(range(1, self.reserved_inode_count + 1))
+		self.root_inode = Inode(self, 2, Inode.TYPE_DIR)
+		self.gdt[0].directory_inode_count += 1
+		self.lost_plus_found = Inode(self, self.inode_allocator.alloc(directory=True), Inode.TYPE_DIR)
+		lpf_dir = DirWriter(self.lost_plus_found)
+		lpf_dir.add(self.lost_plus_found.as_dirent('.'))
+		lpf_dir.add(self.root_inode.as_dirent('..'))
+		lpf_dir.finish()
+	
+	def init_gdt(self):
+		"Initialize block group descriptor table"
+		
+		self.superblock_positions = []
+		self.gdt = []
+		consumed_blocks_per_group = (1 + self.gdt_blocks +
+			    self.inode_bitmap_blocks_per_group +
+			    self.block_bitmap_blocks_per_group +
+			    self.inode_table_blocks_per_group)
+		initial_free_blocks = self.blocks_per_group - consumed_blocks_per_group
+		for bg in range(self.block_groups):
+			base = bg * self.blocks_per_group
+			if (bg == 0):
+				base = self.superblock_at_block
+				self.superblock_positions.append(1024)
+			else:
+				self.superblock_positions.append(base * self.block_size)
+			self.block_allocator.mark_used_all(range(base, base+consumed_blocks_per_group))
+			pos = base + 1 + self.gdt_blocks
+			gde = xstruct.create(STRUCT_BLOCK_GROUP_DESCRIPTOR)
+			gde.block_bitmap_block = pos
+			pos += self.block_bitmap_blocks_per_group
+			gde.inode_bitmap_block = pos
+			pos += self.inode_bitmap_blocks_per_group
+			gde.inode_table_first_block = pos
+			gde.free_block_count = initial_free_blocks
+			gde.free_inode_count = self.inodes_per_group
+			gde.directory_inode_count = 0
+			self.gdt.append(gde)
+	
+	def mark_block_cb(self, block):
+		"Called after a block has been allocated"
+		
+		self.gdt[block // self.blocks_per_group].free_block_count -= 1
+	
+	def mark_inode_cb(self, index, directory=False):
+		"Called after an inode has been allocated"
+		
+		index -= 1
+		gde = self.gdt[index // self.inodes_per_group]
+		gde.free_inode_count -= 1
+		if directory:
+			gde.directory_inode_count += 1		
+	
+	def seek_to_block(self, block, offset=0):
+		"Seek to offset bytes after the start of the given block"
+		
+		if offset < 0 or offset > self.block_size:
+			raise Exception("Invalid in-block offset")
+		self.outf.seek(block * self.block_size + offset)
+	
+	def seek_to_inode(self, index):
+		"Seek to the start of the inode structure for the inode number index"
+		
+		index -= 1
+		if index < 0:
+			raise Exception("Invalid inode number")
+		gde = self.gdt[index // self.inodes_per_group]
+		base_block = gde.inode_table_first_block
+		offset = (index % self.inodes_per_group) * self.inode_size
+		block = base_block + (offset // self.block_size)
+		self.seek_to_block(block, offset % self.block_size)	
+	
+	def subtree_add(self, inode, parent_inode, dirpath, is_root=False):
+		"Recursively add files to the filesystem"
+		
+		dir_writer = DirWriter(inode)	
+		dir_writer.add(inode.as_dirent('.'))
+		dir_writer.add(parent_inode.as_dirent('..'))
+		
+		if is_root:
+			dir_writer.add(self.lost_plus_found.as_dirent('lost+found'))
+
+		for item in listdir_items(dirpath):
+			newidx = self.inode_allocator.alloc(directory = item.is_dir)
+			if item.is_file:
+				child_inode = Inode(self, newidx, Inode.TYPE_FILE)
+				for data in chunks(item, self.block_size):
+					child_inode.write(data)
+			elif item.is_dir:
+				child_inode = Inode(self, newidx, Inode.TYPE_DIR)
+				self.subtree_add(child_inode, inode, item.path)
+		
+			dir_writer.add(child_inode.as_dirent(item.name))
+			self.write_inode(child_inode)
+
+		dir_writer.finish()
+	
+	def write_inode(self, inode):
+		"Write inode information into the inode table"
+		
+		self.seek_to_inode(inode.index)
+		self.outf.write(inode.pack())
+
+	def write_gdt(self):
+		"Write group descriptor table at the current file position"
+		
+		for gde in self.gdt:
+			data = bytes(gde.pack())
+			self.outf.write(data)
+			self.outf.seek(GDE_SIZE-len(data), os.SEEK_CUR)
+	
+	def write_superblock(self, block_group):
+		"Write superblock at the current position"
+		
+		sb = xstruct.create(STRUCT_SUPERBLOCK)
+		sb.total_inode_count = self.total_inode_count
+		sb.total_block_count = self.total_block_count
+		sb.reserved_block_count = 0
+		sb.free_block_count = self.block_allocator.free
+		sb.free_inode_count = self.inode_allocator.free
+		sb.first_block = self.superblock_at_block
+		sb.block_size_log2 = num_of_trailing_bin_zeros(self.block_size) - 10
+		sb.fragment_size_log2 = sb.block_size_log2
+		sb.blocks_per_group = self.blocks_per_group
+		sb.fragments_per_group = self.blocks_per_group
+		sb.inodes_per_group = self.inodes_per_group
+		curtime = int(time.time())
+		sb.mount_time = curtime
+		sb.write_time = curtime
+		sb.mount_count = 0
+		sb.max_mount_count = 21
+		sb.magic = 0xEF53
+		sb.state = 5 # clean
+		sb.error_behavior = 1 # continue on errors
+		sb.rev_minor = 0
+		sb.last_check_time = curtime
+		sb.max_check_interval = 15552000 # 6 months
+		sb.os = 0 # Linux
+		sb.rev_major = 1
+		sb.first_inode = self.reserved_inode_count + 1
+		sb.inode_size = self.inode_size
+		sb.block_group_number = block_group
+		sb.features_compatible = 0
+		sb.features_incompatible = 2 # filetype
+		sb.features_read_only = 0
+		sb.uuid = self.uuid.bytes_le
+		sb.volume_name = 'HelenOS rdimage\0'
+		self.outf.write(bytes(sb.pack()))
+	
+	def write_all_metadata(self):
+		"Write superblocks, block group tables, block and inode bitmaps"
+		
+		bbpg = self.blocks_per_group // 8
+		bipg = self.inodes_per_group // 8
+		def window(arr, index, size):
+			return arr[index * size:(index + 1) * size]
+		
+		for bg_index in xrange(len(self.gdt)):
+			sbpos = self.superblock_positions[bg_index]
+			sbblock = (sbpos + 1023) // self.block_size
+			gde = self.gdt[bg_index]
+			
+			self.outf.seek(sbpos)
+			self.write_superblock(bg_index)
+			
+			self.seek_to_block(sbblock+1)
+			self.write_gdt()
+			
+			self.seek_to_block(gde.block_bitmap_block)
+			self.outf.write(window(self.block_allocator.bitmap, bg_index, bbpg))
+			
+			self.seek_to_block(gde.inode_bitmap_block)
+			self.outf.write(window(self.inode_allocator.bitmap, bg_index, bipg))
+	
+	def close(self):
+		"Write all remaining data to the filesystem and close the file"
+		
+		self.write_inode(self.root_inode)
+		self.write_inode(self.lost_plus_found)
+		self.write_all_metadata()
+		self.outf.close()
+
+class Allocator:
+	def __init__(self, base, count):
+		self.count = count
+		self.base = base
+		self.free = count
+		self.nextidx = 0
+		self.bitmap = array.array('B', [0] * (count // 8))
+		self.mark_cb = None
+	
+	def __contains__(self, item):
+		"Check if the item is already used"
+		
+		bitidx = item - self.base
+		return get_bit(self.bitmap[bitidx // 8], bitidx % 8)
+	
+	def alloc(self, **options):
+		"Allocate a new item"
+		
+		while self.nextidx < self.count and (self.base + self.nextidx) in self:
+			self.nextidx += 1
+		if self.nextidx == self.count:
+			raise Exception("No free item available")
+		item = self.base + self.nextidx
+		self.nextidx += 1
+		self.mark_used(item, **options)
+		return item
+	
+	def mark_used(self, item, **options):
+		"Mark the specified item as used"
+		
+		bitidx = item - self.base
+		if item in self:
+			raise Exception("Item already used: " + str(item))
+		self.free -= 1
+		index = bitidx // 8
+		self.bitmap[index] = set_bit(self.bitmap[index], bitidx % 8)
+		if self.mark_cb:
+			self.mark_cb(item, **options)
+	
+	def mark_used_all(self, items, **options):
+		"Mark all specified items as used"
+		
+		for item in items:
+			self.mark_used(item, **options)
+
+class Inode:
+	TYPE_FILE = 1
+	TYPE_DIR = 2
+	TYPE2MODE = {TYPE_FILE: 8, TYPE_DIR: 4}
+	
+	def __init__(self, fs, index, typ):
+		self.fs = fs
+		self.direct = [None] * 12
+		self.indirect = [None] * 3
+		self.pos = 0
+		self.size = 0
+		self.blocks = 0
+		self.index = index
+		self.type = typ
+		self.refcount = 0
+	
+	def as_dirent(self, name):
+		"Return a DirEntry corresponding to this inode"
+		self.refcount += 1
+		return DirEntry(name, self.index, self.type)
+	
+	def new_block(self, data=True):
+		"Get a new block index from allocator and count it here as belonging to the file"
+		
+		block = self.fs.block_allocator.alloc()
+		self.blocks += 1
+		return block
+	
+	def get_or_add_block(self, block):
+		"Get or add a real block to the file"
+		
+		if block < 12:
+			return self.get_or_add_block_direct(block)
+		return self.get_or_add_block_indirect(block)
+	
+	def get_or_add_block_direct(self, block):
+		"Get or add a real block to the file (direct blocks)"
+		
+		if self.direct[block] == None:
+			self.direct[block] = self.new_block()
+		return self.direct[block]
+	
+	def get_or_add_block_indirect(self, block):
+		"Get or add a real block to the file (indirect blocks)"
+		
+		# Determine the indirection level for the desired block
+		level = None
+		for i in range(4):
+			if block < self.fs.indirect_limits[i]:
+				level = i
+				break
+
+		assert level != None
+	
+		# Compute offsets for the topmost level
+		block_offset_in_level = block - self.fs.indirect_limits[level-1];
+		if self.indirect[level-1] == None:
+			self.indirect[level-1] = self.new_block(data = False)
+		current_block = xstruct.create(STRUCT_BLOCK_REFERENCE)
+		current_block.block_id = self.indirect[level-1]
+		offset_in_block = block_offset_in_level // self.fs.indirect_blocks_per_level[level-1]
+	
+		# Navigate through other levels
+		while level > 0:		
+			assert offset_in_block < self.fs.block_ids_per_block
+			
+			level -= 1
+			
+			self.fs.seek_to_block(current_block.block_id, offset_in_block*4)
+			current_block.unpack(self.fs.outf.read(4))
+			
+			if current_block.block_id == 0:
+				# The block does not exist, so alloc one and write it there
+				self.fs.outf.seek(-4, os.SEEK_CUR)
+				current_block.block_id = self.new_block(data=(level==0))
+				self.fs.outf.write(current_block.pack())
+		
+			# If we are on the last level, break here as
+			# there is no next level to visit
+			if level == 0:
+				break
+		
+			# Visit the next level
+			block_offset_in_level %= self.fs.indirect_blocks_per_level[level];
+			offset_in_block = block_offset_in_level // self.fs.indirect_blocks_per_level[level-1]
+
+		return current_block.block_id
+	
+	def do_seek(self):
+		"Perform a seek to the position indicated by self.pos"
+		
+		block = self.pos // self.fs.block_size
+		real_block = self.get_or_add_block(block)
+		offset = self.pos % self.fs.block_size
+		self.fs.seek_to_block(real_block, offset)
+		
+	def write(self, data):
+		"Write a piece of data (arbitrarily long) as the contents of the inode"
+		
+		data_pos = 0		
+		while data_pos < len(data):
+			bytes_remaining_in_block = self.fs.block_size - (self.pos % self.fs.block_size)
+			bytes_to_write = min(bytes_remaining_in_block, len(data)-data_pos)
+			self.do_seek()
+			self.fs.outf.write(data[data_pos:data_pos + bytes_to_write])
+			self.pos += bytes_to_write
+			data_pos += bytes_to_write
+			self.size = max(self.pos, self.size)
+	
+	def align_size_to_block(self):
+		"Align the size of the inode up to block size"
+		
+		self.size = align_up(self.size, self.fs.block_size)
+	
+	def align_pos(self, bytes):
+		"Align the current position up to bytes boundary"
+		
+		self.pos = align_up(self.pos, bytes)
+	
+	def pack(self):
+		"Pack the inode structure and return the result"
+		
+		data = xstruct.create(STRUCT_INODE)
+		data.mode = (Inode.TYPE2MODE[self.type] << 12)
+		data.mode |= 0x1ff # ugo+rwx
+		data.user_id = 0
+		data.size = self.size & 0xFFFFFFFF
+		data.group_id = 0
+		curtime = int(time.time())
+		data.access_time = curtime
+		data.modification_time = curtime
+		data.creation_time = curtime
+		data.deletion_time = 0
+		data.usage_count = self.refcount
+		data.reserved_512_blocks = self.blocks * (self.fs.block_size // 512)
+		data.flags = 0
+		blockconv = lambda x: 0 if x == None else x
+		data.direct_blocks = map(blockconv, self.direct)
+		data.indirect_blocks = map(blockconv, self.indirect)
+		data.version = 0
+		data.file_acl = 0
+		if self.type == Inode.TYPE_FILE:
+			data.size_high = (self.size >> 32)
+		else:
+			# size_high represents dir_acl in this case
+			data.size_high = 0
+		data.mode_high = 0
+		data.user_id_high = 0
+		data.group_id_high = 0
+		return data.pack()
+		
+class DirEntry:
+	"Represents a linked list directory entry"
+	
+	def __init__(self, name, inode, typ):
+		self.name = name.encode('UTF-8')
+		self.inode = inode
+		self.skip = None
+		self.type = typ
+	
+	def size(self):
+		"Return size of the entry in bytes"
+		
+		return align_up(8 + len(self.name)+1, 4)
+	
+	def write(self, inode):
+		"Write the directory entry into the inode"
+		
+		head = xstruct.create(STRUCT_DIR_ENTRY_HEAD)
+		head.inode = self.inode
+		head.skip = self.skip
+		head.name_length = len(self.name)
+		head.inode_type = self.type
+		inode.write(head.pack())
+		inode.write(self.name+'\0')
+		inode.align_pos(4)
+
+class DirWriter:
+	"Manages writing directory entries into an inode (alignment, etc.)"
+	
+	def __init__(self, inode):
+		self.pos = 0
+		self.inode = inode
+		self.prev_entry = None
+		self.prev_pos = None
+	
+	def prev_write(self):
+		"Write a previously remembered entry"
+		
+		if self.prev_entry:
+			self.prev_entry.skip = self.pos - self.prev_pos
+			if self.inode:
+				self.prev_entry.write(self.inode)
+	
+	def add(self, entry):
+		"Add a directory entry to the directory"
+		
+		size = entry.size()
+		block_size = self.inode.fs.block_size
+		if align_up(self.pos, block_size) < align_up(self.pos + size, block_size):
+			self.pos = align_up(self.pos, block_size)
+		self.prev_write()
+		self.prev_entry = entry
+		self.prev_pos = self.pos
+		self.pos += size
+	
+	def finish(self):
+		"Write the last entry and finish writing the directory contents"
+		
+		if not self.inode:
+			return
+		self.pos = align_up(self.pos, self.inode.fs.block_size)
+		self.prev_write()
+		self.inode.align_size_to_block()
+
+def subtree_stats(root, block_size):
+	"Recursively calculate statistics"
+	
+	blocks = 0
+	inodes = 1
+	dir_writer = DirWriter(None)
+	
+	for item in listdir_items(root):
+		inodes += 1
+		if item.is_file:
+			blocks += count_up(item.size, block_size)
+		elif item.is_dir:
+			subtree_blocks, subtree_inodes = subtree_stats(item.path, block_size)
+			blocks += subtree_blocks
+			inodes += subtree_inodes
+	
+	dir_writer.finish()
+	blocks += count_up(dir_writer.pos, block_size)
+	return (blocks, inodes)
+
+def usage(prname):
+	"Print usage syntax"
+	print(prname + " <EXTRA_BYTES> <PATH> <IMAGE>")
+
+def main():
+	if (len(sys.argv) < 4):
+		usage(sys.argv[0])
+		return
+	
+	if (not sys.argv[1].isdigit()):
+		print("<EXTRA_BYTES> must be a number")
+		return
+	
+	extra_bytes = int(sys.argv[1])
+	
+	path = os.path.abspath(sys.argv[2])
+	if (not os.path.isdir(path)):
+		print("<PATH> must be a directory")
+		return
+	
+	block_size = 4096
+	inode_size = 128
+	reserved_inode_count = 10
+	blocks_per_group = 1024
+	inodes_per_group = 512
+	
+	blocks, inodes = subtree_stats(path, block_size)
+	blocks += count_up(extra_bytes, block_size)
+	inodes += reserved_inode_count
+	
+	inodes_per_group = align_up(inodes_per_group, 8)
+	blocks_per_group = align_up(blocks_per_group, 8)
+	
+	inode_table_blocks_per_group = (inodes_per_group * inode_size) // block_size
+	inode_bitmap_blocks_per_group = count_up(inodes_per_group // 8, block_size)
+	block_bitmap_blocks_per_group = count_up(blocks_per_group // 8, block_size)
+	free_blocks_per_group = blocks_per_group
+	free_blocks_per_group -= inode_table_blocks_per_group
+	free_blocks_per_group -= inode_bitmap_blocks_per_group
+	free_blocks_per_group -= block_bitmap_blocks_per_group
+	free_blocks_per_group -= 10 # one for SB and some reserve for GDT
+	
+	block_groups = max(count_up(inodes, inodes_per_group), count_up(blocks, free_blocks_per_group))
+	
+	fs = Filesystem(sys.argv[3], block_groups, blocks_per_group, inodes_per_group,
+	                        block_size, inode_size, reserved_inode_count)
+	
+	fs.subtree_add(fs.root_inode, fs.root_inode, path, is_root=True)
+	fs.close()
+	
+if __name__ == '__main__':
+	main()
Index: tools/mkfat.py
===================================================================
--- tools/mkfat.py	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ tools/mkfat.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -37,14 +37,5 @@
 import xstruct
 import array
-
-exclude_names = set(['.svn', '.bzr'])
-
-def align_up(size, alignment):
-	"Return size aligned up to alignment"
-	
-	if (size % alignment == 0):
-		return size
-	
-	return ((size // alignment) + 1) * alignment
+from imgutil import *
 
 def subtree_size(root, cluster_size, dirent_size):
@@ -54,13 +45,10 @@
 	files = 2
 	
-	for name in os.listdir(root):
-		canon = os.path.join(root, name)
-		
-		if (os.path.isfile(canon) and (not name in exclude_names)):
-			size += align_up(os.path.getsize(canon), cluster_size)
+	for item in listdir_items(root):
+		if item.is_file:
+			size += align_up(item.size, cluster_size)
 			files += 1
-		
-		if (os.path.isdir(canon) and (not name in exclude_names)):
-			size += subtree_size(canon, cluster_size, dirent_size)
+		elif item.is_dir:
+			size += subtree_size(item.path, cluster_size, dirent_size)
 			files += 1
 	
@@ -72,14 +60,11 @@
 	return len(os.listdir(root))
 
-def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
+def write_file(item, outf, cluster_size, data_start, fat, reserved_clusters):
 	"Store the contents of a file"
 	
-	size = os.path.getsize(path)
 	prev = -1
 	first = 0
 	
-	inf = open(path, "rb")
-	rd = 0;
-	while (rd < size):
+	for data in chunks(item, cluster_size):
 		empty_cluster = fat.index(0)
 		fat[empty_cluster] = 0xffff
@@ -92,11 +77,8 @@
 		prev = empty_cluster
 		
-		data = bytes(inf.read(cluster_size));
 		outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
 		outf.write(data)
-		rd += len(data)
-	inf.close()
-	
-	return first, size
+	
+	return first, item.size
 
 def write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster):
@@ -303,14 +285,11 @@
 		empty_cluster = 0
 	
-	for name in os.listdir(root):
-		canon = os.path.join(root, name)
-		
-		if (os.path.isfile(canon) and (not name in exclude_names)):
-			rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
-			directory.append(create_dirent(name, False, rv[0], rv[1]))
-		
-		if (os.path.isdir(canon) and (not name in exclude_names)):
-			rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
-			directory.append(create_dirent(name, True, rv[0], rv[1]))
+	for item in listdir_items(root):		
+		if item.is_file:
+			rv = write_file(item, outf, cluster_size, data_start, fat, reserved_clusters)
+			directory.append(create_dirent(item.name, False, rv[0], rv[1]))
+		elif item.is_dir:
+			rv = recursion(False, item.path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
+			directory.append(create_dirent(item.name, True, rv[0], rv[1]))
 	
 	if (head):
Index: tools/mktmpfs.py
===================================================================
--- tools/mktmpfs.py	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ tools/mktmpfs.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -35,6 +35,5 @@
 import os
 import xstruct
-
-exclude_names = set(['.svn', '.bzr'])
+from imgutil import listdir_items, chunks
 
 HEADER = """little:
@@ -71,35 +70,26 @@
 	"Recursive directory walk"
 	
-	for name in os.listdir(root):
-		canon = os.path.join(root, name)
-		
-		if (os.path.isfile(canon) and (not name in exclude_names)):
-			size = os.path.getsize(canon)
-			
-			dentry = xstruct.create(DENTRY_FILE % len(name))
+	for item in listdir_items(root):		
+		if item.is_file:			
+			dentry = xstruct.create(DENTRY_FILE % len(item.name))
 			dentry.kind = TMPFS_FILE
-			dentry.fname_len = len(name)
-			dentry.fname = name.encode('ascii')
-			dentry.flen = size
+			dentry.fname_len = len(item.name)
+			dentry.fname = item.name.encode('ascii')
+			dentry.flen = item.size
 			
 			outf.write(dentry.pack())
 			
-			inf = open(canon, "rb")
-			rd = 0;
-			while (rd < size):
-				data = inf.read(4096);
+			for data in chunks(item, 4096):
 				outf.write(data)
-				rd += len(data)
-			inf.close()
 		
-		if (os.path.isdir(canon) and (not name in exclude_names)):
-			dentry = xstruct.create(DENTRY_DIRECTORY % len(name))
+		elif item.is_dir:
+			dentry = xstruct.create(DENTRY_DIRECTORY % len(item.name))
 			dentry.kind = TMPFS_DIRECTORY
-			dentry.fname_len = len(name)
-			dentry.fname = name.encode('ascii')
+			dentry.fname_len = len(item.name)
+			dentry.fname = item.name.encode('ascii')
 			
 			outf.write(dentry.pack())
 			
-			recursion(canon, outf)
+			recursion(item.path, outf)
 			
 			dentry = xstruct.create(DENTRY_NONE)
Index: tools/xstruct.py
===================================================================
--- tools/xstruct.py	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ tools/xstruct.py	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -1,4 +1,5 @@
 #
 # Copyright (c) 2008 Martin Decky
+# Copyright (c) 2011 Martin Sucha
 # All rights reserved.
 #
@@ -31,4 +32,29 @@
 
 import struct
+import types
+
+ranges = {
+	'B': ((int, long), 0x00, 0xff),
+	'H': ((int, long), 0x0000, 0xffff),
+	'L': ((int, long), 0x00000000, 0xffffffff),
+	'Q': ((int, long), 0x0000000000000000, 0xffffffffffffffff),
+	'b': ((int, long), -0x80, 0x7f),
+	'h': ((int, long), -0x8000, 0x7fff),
+	'l': ((int, long), -0x80000000, 0x7fffffff) ,
+	'q': ((int, long), -0x8000000000000000, 0x7fffffffffffffff),
+}
+
+def check_range(varname, fmt, value):
+	if value == None:
+		raise ValueError('Variable "%s" not set' % varname)
+	if not fmt in ranges:
+		return
+	vartype, varmin, varmax = ranges[fmt]
+	if not isinstance(value, vartype):
+		raise ValueError('Variable "%s" is %s but should be %s' %
+		                 (varname, str(type(value)), str(vartype)))
+	if value < varmin or value > varmax:
+		raise ValueError('Variable "%s" value %s out of range %s..%s' % 
+		                 (varname, repr(value), repr(varmin), repr(varmax)))
 
 class Struct:
@@ -38,12 +64,24 @@
 	def pack(self):
 		args = []
-		for variable in self._args_:
-			if (isinstance(self.__dict__[variable], list)):
-				for item in self.__dict__[variable]:
+		for variable, fmt, length in self._args_:
+			value = self.__dict__[variable]
+			if isinstance(value, list):
+				if length != None and length != len(value):
+					raise ValueError('Variable "%s" length %u does not match %u' %
+				                      (variable, len(value), length))
+				for index, item in enumerate(value):
+					check_range(variable + '[' + repr(index) + ']', fmt, item)
 					args.append(item)
 			else:
-				args.append(self.__dict__[variable])
-		
+				check_range(variable, fmt, value)
+				args.append(value)		
 		return struct.pack(self._format_, *args)
+	
+	def unpack(self, data):
+		values = struct.unpack(self._format_, data)
+		i = 0
+		for variable, fmt, length in self._args_:
+			self.__dict__[variable] = values[i]
+			i += 1
 
 def create(definition):
@@ -77,11 +115,13 @@
 			subtokens = token.split("[")
 			
+			length = None
 			if (len(subtokens) > 1):
-				format += "%d" % int(subtokens[1].split("]")[0])
+				length = int(subtokens[1].split("]")[0])
+				format += "%d" % length
 			
 			format += variable
 			
 			inst.__dict__[subtokens[0]] = None
-			args.append(subtokens[0])
+			args.append((subtokens[0], variable, length))
 			
 			variable = None
Index: uspace/app/bdsh/cmds/modules/cp/cp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cp/cp.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -71,6 +71,6 @@
 	size_t blen, int vb)
 {
-	int fd1, fd2, bytes = 0;
-	off64_t total = 0;
+	int fd1, fd2, bytes;
+	off64_t total;
 	int64_t copied = 0;
 	char *buff = NULL;
@@ -104,37 +104,11 @@
 	}
 
-	for (;;) {
-		ssize_t res;
-		size_t written = 0;
-
-		bytes = read(fd1, buff, blen);
-		if (bytes <= 0)
+	while ((bytes = read_all(fd1, buff, blen)) > 0) {
+		if ((bytes = write_all(fd2, buff, bytes)) < 0)
 			break;
 		copied += bytes;
-		res = bytes;
-		do {
-			/*
-			 * Theoretically, it may not be enough to call write()
-			 * only once. Also the previous read() may have
-			 * returned less data than requested.
-			 */
-			bytes = write(fd2, buff + written, res);
-			if (bytes < 0)
-				goto err;
-			written += bytes;
-			res -= bytes;
-		} while (res > 0);
-
-		/* TODO: re-insert assert() once this is stand alone,
-		 * removed as abort() exits the entire shell
-		 */
-		if (res != 0) {
-			printf("\n%zd more bytes than actually exist were copied\n", res);
-			goto err;
-		}
 	}
 
 	if (bytes < 0) {
-err:
 		printf("\nError copying %s, (%d)\n", src, bytes);
 		copied = bytes;
Index: uspace/app/bdsh/compl.c
===================================================================
--- uspace/app/bdsh/compl.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/app/bdsh/compl.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -280,4 +280,8 @@
 
 				cs->dir = opendir(*cs->path);
+
+				/* Skip directories that we fail to open. */
+				if (cs->dir == NULL)
+					cs->path++;
 			}
 
Index: uspace/app/taskdump/elf_core.c
===================================================================
--- uspace/app/taskdump/elf_core.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/app/taskdump/elf_core.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -67,5 +67,4 @@
 
 static off64_t align_foff_up(off64_t, uintptr_t, size_t);
-static int write_all(int, const void *, size_t);
 static int align_pos(int, size_t);
 static int write_mem_area(int, as_area_info_t *, async_sess_t *);
@@ -100,5 +99,5 @@
 
 	int fd;
-	int rc;
+	ssize_t rc;
 	unsigned int i;
 
@@ -204,5 +203,5 @@
 
 	rc = write_all(fd, &elf_hdr, sizeof(elf_hdr));
-	if (rc != EOK) {
+	if (rc != sizeof(elf_hdr)) {
 		printf("Failed writing ELF header.\n");
 		free(p_hdr);
@@ -212,5 +211,5 @@
 	for (i = 0; i < n_ph; ++i) {
 		rc = write_all(fd, &p_hdr[i], sizeof(p_hdr[i]));
-		if (rc != EOK) {
+		if (rc != sizeof(p_hdr[i])) {
 			printf("Failed writing program header.\n");
 			free(p_hdr);
@@ -233,5 +232,5 @@
 
 	rc = write_all(fd, &note, sizeof(elf_note_t));
-	if (rc != EOK) {
+	if (rc != sizeof(elf_note_t)) {
 		printf("Failed writing note header.\n");
 		free(p_hdr);
@@ -240,5 +239,5 @@
 
 	rc = write_all(fd, "CORE", note.namesz);
-	if (rc != EOK) {
+	if (rc != (ssize_t) note.namesz) {
 		printf("Failed writing note header.\n");
 		free(p_hdr);
@@ -254,5 +253,5 @@
 
 	rc = write_all(fd, &pr_status, sizeof(elf_prstatus_t));
-	if (rc != EOK) {
+	if (rc != sizeof(elf_prstatus_t)) {
 		printf("Failed writing register data.\n");
 		free(p_hdr);
@@ -304,5 +303,5 @@
 	size_t total;
 	uintptr_t addr;
-	int rc;
+	ssize_t rc;
 
 	addr = area->start_addr;
@@ -318,5 +317,5 @@
 
 		rc = write_all(fd, buffer, to_copy);
-		if (rc != EOK) {
+		if (rc != (ssize_t) to_copy) {
 			printf("Failed writing memory contents.\n");
 			return EIO;
@@ -326,34 +325,4 @@
 		total += to_copy;
 	}
-
-	return EOK;
-}
-
-/** Write until the buffer is written in its entirety.
- *
- * This function fails if it cannot write exactly @a len bytes to the file.
- *
- * @param fd		The file to write to.
- * @param buf		Data, @a len bytes long.
- * @param len		Number of bytes to write.
- *
- * @return		EOK on error, return value from write() if writing
- *			failed.
- */
-static int write_all(int fd, const void *data, size_t len)
-{
-	int cnt = 0;
-
-	do {
-		data += cnt;
-		len -= cnt;
-		cnt = write(fd, data, len);
-	} while (cnt > 0 && (len - cnt) > 0);
-
-	if (cnt < 0)
-		return cnt;
-
-	if (len - cnt > 0)
-		return EIO;
 
 	return EOK;
Index: uspace/app/taskdump/symtab.c
===================================================================
--- uspace/app/taskdump/symtab.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/app/taskdump/symtab.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -50,5 +50,4 @@
     elf_section_header_t *shdr);
 static int chunk_load(int fd, off64_t start, size_t size, void **ptr);
-static int read_all(int fd, void *buf, size_t len);
 
 /** Load symbol table from an ELF file.
@@ -90,5 +89,5 @@
 
 	rc = read_all(fd, &elf_hdr, sizeof(elf_header_t));
-	if (rc != EOK) {
+	if (rc != sizeof(elf_header_t)) {
 		printf("failed reading elf header\n");
 		free(stab);
@@ -312,5 +311,5 @@
 
 	rc = read_all(fd, sec_hdr, sizeof(elf_section_header_t));
-	if (rc != EOK)
+	if (rc != sizeof(elf_section_header_t))
 		return EIO;
 
@@ -331,8 +330,9 @@
 static int chunk_load(int fd, off64_t start, size_t size, void **ptr)
 {
-	int rc;
-
-	rc = lseek(fd, start, SEEK_SET);
-	if (rc == (off64_t) -1) {
+	ssize_t rc;
+	off64_t offs;
+
+	offs = lseek(fd, start, SEEK_SET);
+	if (offs == (off64_t) -1) {
 		printf("failed seeking chunk\n");
 		*ptr = NULL;
@@ -347,5 +347,5 @@
 
 	rc = read_all(fd, *ptr, size);
-	if (rc != EOK) {
+	if (rc != (ssize_t) size) {
 		printf("failed reading chunk\n");
 		free(*ptr);
@@ -357,34 +357,4 @@
 }
 
-/** Read until the buffer is read in its entirety.
- *
- * This function fails if it cannot read exactly @a len bytes from the file.
- *
- * @param fd		The file to read from.
- * @param buf		Buffer for storing data, @a len bytes long.
- * @param len		Number of bytes to read.
- *
- * @return		EOK on error, EIO if file is short or return value
- *			from read() if reading failed.
- */
-static int read_all(int fd, void *buf, size_t len)
-{
-	int cnt = 0;
-
-	do {
-		buf += cnt;
-		len -= cnt;
-		cnt = read(fd, buf, len);
-	} while (cnt > 0 && (len - cnt) > 0);
-
-	if (cnt < 0)
-		return cnt;
-
-	if (len - cnt > 0)
-		return EIO;
-
-	return EOK;
-}
-
 /** @}
  */
Index: uspace/drv/bus/usb/usbmast/bo_trans.c
===================================================================
--- uspace/drv/bus/usb/usbmast/bo_trans.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/drv/bus/usb/usbmast/bo_trans.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -58,36 +58,46 @@
  * @param tag		Command block wrapper tag (automatically compared
  *			with answer)
- * @param cmd		Command block
- * @param cmd_size	Command block size in bytes
- * @param ddir		Direction in which data will be transferred
- * @param dbuf		Data send/receive buffer
- * @param dbuf_size	Size of the data buffer
- * @param xferred_size	Number of bytes actually transferred
+ * @param cmd		SCSI command
  *
  * @return		Error code
  */
-static int usb_massstor_cmd(usbmast_fun_t *mfun, uint32_t tag, const void *cmd,
-    size_t cmd_size, usb_direction_t ddir, void *dbuf, size_t dbuf_size,
-    size_t *xferred_size)
+int usb_massstor_cmd(usbmast_fun_t *mfun, uint32_t tag, scsi_cmd_t *cmd)
 {
 	int rc;
+	int retval = EOK;
 	size_t act_size;
 	usb_pipe_t *bulk_in_pipe = mfun->mdev->usb_dev->pipes[BULK_IN_EP].pipe;
 	usb_pipe_t *bulk_out_pipe = mfun->mdev->usb_dev->pipes[BULK_OUT_EP].pipe;
+	usb_direction_t ddir;
+	void *dbuf;
+	size_t dbuf_size;
+
+	if (cmd->data_out != NULL && cmd->data_in == NULL) {
+		ddir = USB_DIRECTION_OUT;
+		dbuf = (void *)cmd->data_out;
+		dbuf_size = cmd->data_out_size;
+	} else if (cmd->data_out == NULL && cmd->data_in != NULL) {
+		ddir = USB_DIRECTION_IN;
+		dbuf = cmd->data_in;
+		dbuf_size = cmd->data_in_size;
+	} else {
+		assert(false);
+	}
 
 	/* Prepare CBW - command block wrapper */
 	usb_massstor_cbw_t cbw;
 	usb_massstor_cbw_prepare(&cbw, tag, dbuf_size, ddir, mfun->lun,
-	    cmd_size, cmd);
+	    cmd->cdb_size, cmd->cdb);
 
 	/* Send the CBW. */
+	MASTLOG("Sending CBW.\n");
 	rc = usb_pipe_write(bulk_out_pipe, &cbw, sizeof(cbw));
 	MASTLOG("CBW '%s' sent: %s.\n",
 	    usb_debug_str_buffer((uint8_t *) &cbw, sizeof(cbw), 0),
 	    str_error(rc));
-	if (rc != EOK) {
-		return rc;
-	}
-
+	if (rc != EOK)
+		return EIO;
+
+	MASTLOG("Transferring data.\n");
 	if (ddir == USB_DIRECTION_IN) {
 		/* Recieve data from the device. */
@@ -104,10 +114,15 @@
 	}
 
-	if (rc != EOK) {
-		/*
-		 * XXX If the pipe is stalled, we should clear it
-		 * and read CSW.
-		 */
-		return rc;
+	if (rc == ESTALL) {
+		/* Clear stall condition and continue below to read CSW. */
+		if (ddir == USB_DIRECTION_IN) {
+			usb_pipe_clear_halt(&mfun->mdev->usb_dev->ctrl_pipe,
+			    mfun->mdev->usb_dev->pipes[BULK_IN_EP].pipe);
+		} else {
+			usb_pipe_clear_halt(&mfun->mdev->usb_dev->ctrl_pipe,
+			    mfun->mdev->usb_dev->pipes[BULK_OUT_EP].pipe);
+		}
+        } else if (rc != EOK) {
+		return EIO;
 	}
 
@@ -115,4 +130,5 @@
 	usb_massstor_csw_t csw;
 	size_t csw_size;
+	MASTLOG("Reading CSW.\n");
 	rc = usb_pipe_read(bulk_in_pipe, &csw, sizeof(csw), &csw_size);
 	MASTLOG("CSW '%s' received (%zu bytes): %s.\n",
@@ -121,15 +137,15 @@
 	if (rc != EOK) {
 		MASTLOG("rc != EOK\n");
-		return rc;
+		return EIO;
 	}
 
 	if (csw_size != sizeof(csw)) {
 		MASTLOG("csw_size != sizeof(csw)\n");
-		return ERANGE;
+		return EIO;
 	}
 
 	if (csw.dCSWTag != tag) {
 		MASTLOG("csw.dCSWTag != tag\n");
-		return EBADCHECKSUM;
+		return EIO;
 	}
 
@@ -137,9 +153,19 @@
 	 * Determine the actual return value from the CSW.
 	 */
-	if (csw.dCSWStatus != 0) {
-		MASTLOG("csw.dCSWStatus != 0\n");
-		// FIXME: better error code
-		// FIXME: distinguish 0x01 and 0x02
-		return EXDEV;
+	switch (csw.dCSWStatus) {
+	case cbs_passed:
+		cmd->status = CMDS_GOOD;
+		break;
+	case cbs_failed:
+		MASTLOG("Command failed\n");
+		cmd->status = CMDS_FAILED;
+		break;
+	case cbs_phase_error:
+		MASTLOG("Phase error\n");
+		retval = EIO;
+		break;
+	default:
+		retval = EIO;
+		break;
 	}
 
@@ -147,5 +173,5 @@
 	if (residue > dbuf_size) {
 		MASTLOG("residue > dbuf_size\n");
-		return ERANGE;
+		return EIO;
 	}
 
@@ -158,48 +184,8 @@
 	 */
 
-	if (xferred_size != NULL)
-		*xferred_size = dbuf_size - residue;
-
-	return EOK;
-}
-
-/** Perform data-in command.
- *
- * @param mfun		Mass storage function
- * @param tag		Command block wrapper tag (automatically compared with
- *			answer)
- * @param cmd		CDB (Command Descriptor)
- * @param cmd_size	CDB length in bytes
- * @param dbuf		Data receive buffer
- * @param dbuf_size	Data receive buffer size in bytes
- * @param proc_size	Number of bytes actually processed by device
- *
- * @return Error code
- */
-int usb_massstor_data_in(usbmast_fun_t *mfun, uint32_t tag, const void *cmd,
-    size_t cmd_size, void *dbuf, size_t dbuf_size, size_t *proc_size)
-{
-	return usb_massstor_cmd(mfun, tag, cmd, cmd_size, USB_DIRECTION_IN,
-	    dbuf, dbuf_size, proc_size);
-}
-
-/** Perform data-out command.
- *
- * @param mfun		Mass storage function
- * @param tag		Command block wrapper tag (automatically compared with
- *			answer)
- * @param cmd		CDB (Command Descriptor)
- * @param cmd_size	CDB length in bytes
- * @param data		Command data
- * @param data_size	Size of @a data in bytes
- * @param proc_size	Number of bytes actually processed by device
- *
- * @return Error code
- */
-int usb_massstor_data_out(usbmast_fun_t *mfun, uint32_t tag, const void *cmd,
-    size_t cmd_size, const void *data, size_t data_size, size_t *proc_size)
-{
-	return usb_massstor_cmd(mfun, tag, cmd, cmd_size, USB_DIRECTION_OUT,
-	    (void *) data, data_size, proc_size);
+	if (ddir == USB_DIRECTION_IN)
+		cmd->rcvd_size = dbuf_size - residue;
+
+	return retval;
 }
 
Index: uspace/drv/bus/usb/usbmast/bo_trans.h
===================================================================
--- uspace/drv/bus/usb/usbmast/bo_trans.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/drv/bus/usb/usbmast/bo_trans.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -47,8 +47,46 @@
 #define BULK_OUT_EP 1
 
-extern int usb_massstor_data_in(usbmast_fun_t *, uint32_t, const void *,
-    size_t, void *, size_t, size_t *);
-extern int usb_massstor_data_out(usbmast_fun_t *, uint32_t, const void *,
-    size_t, const void *, size_t, size_t *);
+typedef enum cmd_status {
+	CMDS_GOOD,
+	CMDS_FAILED
+} cmd_status_t;
+
+/** SCSI command.
+ *
+ * Contains (a subset of) the input and output arguments of SCSI
+ * Execute Command procedure call (see SAM-4 chapter 5.1)
+ */
+typedef struct {
+	/*
+	 * Related to IN fields
+	 */
+
+	/** Command Descriptor Block */
+	void *cdb;
+	/** CDB size in bytes */
+	size_t cdb_size;
+
+	/** Outgoing data */
+	const void *data_out;
+	/** Size of outgoing data in bytes */
+	size_t data_out_size;
+
+	/*
+	 * Related to OUT fields
+	 */
+
+	/** Buffer for incoming data */
+	void *data_in;
+	/** Size of input buffer in bytes */
+	size_t data_in_size;
+
+	/** Number of bytes actually received */
+	size_t rcvd_size;
+
+	/** Status */
+	cmd_status_t status;
+} scsi_cmd_t;
+
+extern int usb_massstor_cmd(usbmast_fun_t *, uint32_t, scsi_cmd_t *);
 extern int usb_massstor_reset(usbmast_dev_t *);
 extern void usb_massstor_reset_recovery(usbmast_dev_t *);
Index: uspace/drv/bus/usb/usbmast/cmdw.h
===================================================================
--- uspace/drv/bus/usb/usbmast/cmdw.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/drv/bus/usb/usbmast/cmdw.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -57,4 +57,10 @@
 } __attribute__((packed)) usb_massstor_csw_t;
 
+enum cmd_block_status {
+	cbs_passed	= 0x00,
+	cbs_failed	= 0x01,
+	cbs_phase_error = 0x02
+};
+
 extern void usb_massstor_cbw_prepare(usb_massstor_cbw_t *, uint32_t, uint32_t,
     usb_direction_t, uint8_t, uint8_t, const uint8_t *);
Index: uspace/drv/bus/usb/usbmast/scsi_ms.c
===================================================================
--- uspace/drv/bus/usb/usbmast/scsi_ms.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/drv/bus/usb/usbmast/scsi_ms.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -61,4 +61,63 @@
 }
 
+static void usbmast_dump_sense(scsi_sense_data_t *sense_buf)
+{
+	unsigned sense_key;
+
+	sense_key = sense_buf->flags_key & 0x0f;
+	printf("Got sense data. Sense key: 0x%x (%s), ASC 0x%02x, "
+	    "ASCQ 0x%02x.\n", sense_key,
+	    scsi_get_sense_key_str(sense_key),
+	    sense_buf->additional_code,
+	    sense_buf->additional_cqual);
+}
+
+/** Run SCSI command.
+ *
+ * Run command and repeat in case of unit attention.
+ * XXX This is too simplified.
+ */
+static int usbmast_run_cmd(usbmast_fun_t *mfun, scsi_cmd_t *cmd)
+{
+	uint8_t sense_key;
+	scsi_sense_data_t sense_buf;
+	int rc;
+
+	do {
+		rc = usb_massstor_cmd(mfun, 0xDEADBEEF, cmd);
+		if (rc != EOK) {
+			usb_log_error("Inquiry transport failed, device %s: %s.\n",
+			   mfun->mdev->ddf_dev->name, str_error(rc));
+			return rc;
+		}
+
+		if (cmd->status == CMDS_GOOD)
+			return EOK;
+
+		usb_log_error("SCSI command failed, device %s.\n",
+		    mfun->mdev->ddf_dev->name);
+
+		rc = usbmast_request_sense(mfun, &sense_buf, sizeof(sense_buf));
+		if (rc != EOK) {
+			usb_log_error("Failed to read sense data.\n");
+			return EIO;
+		}
+
+		/* Dump sense data to log */
+		usbmast_dump_sense(&sense_buf);
+
+		/* Get sense key */
+		sense_key = sense_buf.flags_key & 0x0f;
+
+		if (sense_key == SCSI_SK_UNIT_ATTENTION) {
+			printf("Got unit attention. Re-trying command.\n");
+		}
+
+	} while (sense_key == SCSI_SK_UNIT_ATTENTION);
+
+	/* Command status is not good, nevertheless transport succeeded. */
+	return EOK;
+}
+
 /** Perform SCSI Inquiry command on USB mass storage device.
  *
@@ -70,5 +129,5 @@
 {
 	scsi_std_inquiry_data_t inq_data;
-	size_t response_len;
+	scsi_cmd_t cmd;
 	scsi_cdb_inquiry_t cdb;
 	int rc;
@@ -78,16 +137,27 @@
 	cdb.alloc_len = host2uint16_t_be(sizeof(inq_data));
 
-	rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
-	    sizeof(cdb), &inq_data, sizeof(inq_data), &response_len);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = &inq_data;
+	cmd.data_in_size = sizeof(inq_data);
+
+	rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
 
 	if (rc != EOK) {
-		usb_log_error("Inquiry failed, device %s: %s.\n",
-		   mfun->mdev->ddf_dev->name, str_error(rc));
-		return rc;
-	}
-
-	if (response_len < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
+		usb_log_error("Inquiry transport failed, device %s: %s.\n",
+		   mfun->mdev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Inquiry command failed, device %s.\n",
+		   mfun->mdev->ddf_dev->name);
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < SCSI_STD_INQUIRY_DATA_MIN_SIZE) {
 		usb_log_error("SCSI Inquiry response too short (%zu).\n",
-		    response_len);
+		    cmd.rcvd_size);
 		return EIO;
 	}
@@ -127,6 +197,6 @@
 int usbmast_request_sense(usbmast_fun_t *mfun, void *buf, size_t size)
 {
+	scsi_cmd_t cmd;
 	scsi_cdb_request_sense_t cdb;
-	size_t data_len;
 	int rc;
 
@@ -135,8 +205,13 @@
 	cdb.alloc_len = min(size, SCSI_SENSE_DATA_MAX_SIZE);
 
-	rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
-	    sizeof(cdb), buf, size, &data_len);
-
-        if (rc != EOK) {
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = buf;
+	cmd.data_in_size = size;
+
+	rc = usb_massstor_cmd(mfun, 0xDEADBEEF, &cmd);
+
+        if (rc != EOK || cmd.status != CMDS_GOOD) {
 		usb_log_error("Request Sense failed, device %s: %s.\n",
 		   mfun->mdev->ddf_dev->name, str_error(rc));
@@ -144,8 +219,8 @@
 	}
 
-	if (data_len < SCSI_SENSE_DATA_MIN_SIZE) {
+	if (cmd.rcvd_size < SCSI_SENSE_DATA_MIN_SIZE) {
 		/* The missing bytes should be considered to be zeroes. */
-		memset((uint8_t *)buf + data_len, 0,
-		    SCSI_SENSE_DATA_MIN_SIZE - data_len);
+		memset((uint8_t *)buf + cmd.rcvd_size, 0,
+		    SCSI_SENSE_DATA_MIN_SIZE - cmd.rcvd_size);
 	}
 
@@ -164,7 +239,7 @@
     uint32_t *block_size)
 {
+	scsi_cmd_t cmd;
 	scsi_cdb_read_capacity_10_t cdb;
 	scsi_read_capacity_10_data_t data;
-	size_t data_len;
 	int rc;
 
@@ -172,16 +247,27 @@
 	cdb.op_code = SCSI_CMD_READ_CAPACITY_10;
 
-	rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
-	    sizeof(cdb), &data, sizeof(data), &data_len);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = &data;
+	cmd.data_in_size = sizeof(data);
+
+	rc = usbmast_run_cmd(mfun, &cmd);
 
         if (rc != EOK) {
-		usb_log_error("Read Capacity (10) failed, device %s: %s.\n",
-		   mfun->mdev->ddf_dev->name, str_error(rc));
-		return rc;
-	}
-
-	if (data_len < sizeof(data)) {
+		usb_log_error("Read Capacity (10) transport failed, device %s: %s.\n",
+		   mfun->mdev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Read Capacity (10) command failed, device %s.\n",
+		   mfun->mdev->ddf_dev->name);
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < sizeof(data)) {
 		usb_log_error("SCSI Read Capacity response too short (%zu).\n",
-		    data_len);
+		    cmd.rcvd_size);
 		return EIO;
 	}
@@ -203,33 +289,42 @@
 int usbmast_read(usbmast_fun_t *mfun, uint64_t ba, size_t nblocks, void *buf)
 {
-	scsi_cdb_read_12_t cdb;
-	size_t data_len;
-	int rc;
-
-	/* XXX Need softstate to store block size. */
+	scsi_cmd_t cmd;
+	scsi_cdb_read_10_t cdb;
+	int rc;
 
 	if (ba > UINT32_MAX)
 		return ELIMIT;
 
-	if ((uint64_t)nblocks * mfun->block_size > UINT32_MAX)
+	if (nblocks > UINT16_MAX)
 		return ELIMIT;
 
 	memset(&cdb, 0, sizeof(cdb));
-	cdb.op_code = SCSI_CMD_READ_12;
+	cdb.op_code = SCSI_CMD_READ_10;
 	cdb.lba = host2uint32_t_be(ba);
-	cdb.xfer_len = host2uint32_t_be(nblocks);
-
-	rc = usb_massstor_data_in(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
-	    sizeof(cdb), buf, nblocks * mfun->block_size, &data_len);
+	cdb.xfer_len = host2uint16_t_be(nblocks);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_in = buf;
+	cmd.data_in_size = nblocks * mfun->block_size;
+
+	rc = usbmast_run_cmd(mfun, &cmd);
 
         if (rc != EOK) {
-		usb_log_error("Read (12) failed, device %s: %s.\n",
-		   mfun->mdev->ddf_dev->name, str_error(rc));
-		return rc;
-	}
-
-	if (data_len < nblocks * mfun->block_size) {
+		usb_log_error("Read (10) transport failed, device %s: %s.\n",
+		   mfun->mdev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Read (10) command failed, device %s.\n",
+		   mfun->mdev->ddf_dev->name);
+		return EIO;
+	}
+
+	if (cmd.rcvd_size < nblocks * mfun->block_size) {
 		usb_log_error("SCSI Read response too short (%zu).\n",
-		    data_len);
+		    cmd.rcvd_size);
 		return EIO;
 	}
@@ -250,6 +345,6 @@
     const void *data)
 {
-	scsi_cdb_write_12_t cdb;
-	size_t sent_len;
+	scsi_cmd_t cmd;
+	scsi_cdb_write_10_t cdb;
 	int rc;
 
@@ -257,24 +352,29 @@
 		return ELIMIT;
 
-	if ((uint64_t)nblocks * mfun->block_size > UINT32_MAX)
+	if (nblocks > UINT16_MAX)
 		return ELIMIT;
 
 	memset(&cdb, 0, sizeof(cdb));
-	cdb.op_code = SCSI_CMD_WRITE_12;
+	cdb.op_code = SCSI_CMD_WRITE_10;
 	cdb.lba = host2uint32_t_be(ba);
-	cdb.xfer_len = host2uint32_t_be(nblocks);
-
-	rc = usb_massstor_data_out(mfun, 0xDEADBEEF, (uint8_t *) &cdb,
-	    sizeof(cdb), data, nblocks * mfun->block_size, &sent_len);
+	cdb.xfer_len = host2uint16_t_be(nblocks);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb = &cdb;
+	cmd.cdb_size = sizeof(cdb);
+	cmd.data_out = data;
+	cmd.data_out_size = nblocks * mfun->block_size;
+
+	rc = usbmast_run_cmd(mfun, &cmd);
 
         if (rc != EOK) {
-		usb_log_error("Write (12) failed, device %s: %s.\n",
-		   mfun->mdev->ddf_dev->name, str_error(rc));
-		return rc;
-	}
-
-	if (sent_len < nblocks * mfun->block_size) {
-		usb_log_error("SCSI Write not all bytes transferred (%zu).\n",
-		    sent_len);
+		usb_log_error("Write (10) transport failed, device %s: %s.\n",
+		   mfun->mdev->ddf_dev->name, str_error(rc));
+		return rc;
+	}
+
+	if (cmd.status != CMDS_GOOD) {
+		usb_log_error("Write (10) command failed, device %s.\n",
+		   mfun->mdev->ddf_dev->name);
 		return EIO;
 	}
Index: uspace/lib/block/libblock.c
===================================================================
--- uspace/lib/block/libblock.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/block/libblock.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -258,5 +258,5 @@
 static hash_index_t cache_hash(unsigned long *key)
 {
-	return *key & (CACHE_BUCKETS - 1);
+	return MERGE_LOUP32(key[0], key[1]) & (CACHE_BUCKETS - 1);
 }
 
@@ -264,5 +264,5 @@
 {
 	block_t *b = hash_table_get_instance(item, block_t, hash_link);
-	return b->lba == *key;
+	return b->lba == MERGE_LOUP32(key[0], key[1]);
 }
 
@@ -305,5 +305,5 @@
 	cache->blocks_cluster = cache->lblock_size / devcon->pblock_size;
 
-	if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
+	if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 2,
 	    &cache_ops)) {
 		free(cache);
@@ -344,6 +344,9 @@
 		}
 
-		unsigned long key = b->lba;
-		hash_table_remove(&cache->block_hash, &key, 1);
+		unsigned long key[2] = {
+			LOWER32(b->lba),
+			UPPER32(b->lba)
+		};
+		hash_table_remove(&cache->block_hash, key, 2);
 		
 		free(b->data);
@@ -398,5 +401,9 @@
 	block_t *b;
 	link_t *l;
-	unsigned long key = ba;
+	unsigned long key[2] = {
+		LOWER32(ba),
+		UPPER32(ba)
+	};
+
 	int rc;
 	
@@ -413,5 +420,5 @@
 
 	fibril_mutex_lock(&cache->lock);
-	l = hash_table_find(&cache->block_hash, &key);
+	l = hash_table_find(&cache->block_hash, key);
 	if (l) {
 found:
@@ -451,5 +458,4 @@
 			 * Try to recycle a block from the free list.
 			 */
-			unsigned long temp_key;
 recycle:
 			if (list_empty(&cache->free_list)) {
@@ -499,5 +505,5 @@
 					goto retry;
 				}
-				l = hash_table_find(&cache->block_hash, &key);
+				l = hash_table_find(&cache->block_hash, key);
 				if (l) {
 					/*
@@ -522,6 +528,9 @@
 			 */
 			list_remove(&b->free_link);
-			temp_key = b->lba;
-			hash_table_remove(&cache->block_hash, &temp_key, 1);
+			unsigned long temp_key[2] = {
+				LOWER32(b->lba),
+				UPPER32(b->lba)
+			};
+			hash_table_remove(&cache->block_hash, temp_key, 2);
 		}
 
@@ -531,5 +540,5 @@
 		b->lba = ba;
 		b->pba = ba_ltop(devcon, b->lba);
-		hash_table_insert(&cache->block_hash, &key, &b->hash_link);
+		hash_table_insert(&cache->block_hash, key, &b->hash_link);
 
 		/*
@@ -643,6 +652,9 @@
 			 * Take the block out of the cache and free it.
 			 */
-			unsigned long key = block->lba;
-			hash_table_remove(&cache->block_hash, &key, 1);
+			unsigned long key[2] = {
+				LOWER32(block->lba),
+				UPPER32(block->lba)
+			};
+			hash_table_remove(&cache->block_hash, key, 2);
 			fibril_mutex_unlock(&block->lock);
 			free(block->data);
Index: uspace/lib/c/generic/elf/elf_load.c
===================================================================
--- uspace/lib/c/generic/elf/elf_load.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/c/generic/elf/elf_load.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -74,17 +74,4 @@
 static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
 
-/** Read until the buffer is read in its entirety. */
-static int my_read(int fd, void *buf, size_t len)
-{
-	int cnt = 0;
-	do {
-		buf += cnt;
-		len -= cnt;
-		cnt = read(fd, buf, len);
-	} while ((cnt > 0) && ((len - cnt) > 0));
-
-	return cnt;
-}
-
 /** Load ELF binary from a file.
  *
@@ -160,6 +147,6 @@
 	int i, rc;
 
-	rc = my_read(elf->fd, header, sizeof(elf_header_t));
-	if (rc < 0) {
+	rc = read_all(elf->fd, header, sizeof(elf_header_t));
+	if (rc != sizeof(elf_header_t)) {
 		DPRINTF("Read error.\n"); 
 		return EE_INVALID;
@@ -222,7 +209,7 @@
 		        + i * sizeof(elf_segment_header_t), SEEK_SET);
 
-		rc = my_read(elf->fd, &segment_hdr,
+		rc = read_all(elf->fd, &segment_hdr,
 		    sizeof(elf_segment_header_t));
-		if (rc < 0) {
+		if (rc != sizeof(elf_segment_header_t)) {
 			DPRINTF("Read error.\n");
 			return EE_INVALID;
@@ -244,7 +231,7 @@
 		    + i * sizeof(elf_section_header_t), SEEK_SET);
 
-		rc = my_read(elf->fd, &section_hdr,
+		rc = read_all(elf->fd, &section_hdr,
 		    sizeof(elf_section_header_t));
-		if (rc < 0) {
+		if (rc != sizeof(elf_section_header_t)) {
 			DPRINTF("Read error.\n");
 			return EE_INVALID;
@@ -334,5 +321,5 @@
 	uintptr_t seg_addr;
 	size_t mem_sz;
-	int rc;
+	ssize_t rc;
 
 	bias = elf->bias;
@@ -412,7 +399,7 @@
 		if (now > left) now = left;
 
-		rc = my_read(elf->fd, dp, now);
-
-		if (rc < 0) { 
+		rc = read_all(elf->fd, dp, now);
+
+		if (rc != (ssize_t) now) { 
 			DPRINTF("Read error.\n");
 			return EE_INVALID;
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -417,4 +417,66 @@
 }
 
+/** Read entire buffer.
+ *
+ * In face of short reads this function continues reading until either
+ * the entire buffer is read or no more data is available (at end of file).
+ *
+ * @param fildes	File descriptor
+ * @param buf		Buffer, @a nbytes bytes long
+ * @param nbytes	Number of bytes to read
+ *
+ * @return		On success, positive number of bytes read.
+ *			On failure, negative error code from read().
+ */
+ssize_t read_all(int fildes, void *buf, size_t nbyte)
+{
+	ssize_t cnt = 0;
+	size_t nread = 0;
+	uint8_t *bp = (uint8_t *) buf;
+
+	do {
+		bp += cnt;
+		nread += cnt;
+		cnt = read(fildes, bp, nbyte - nread);
+	} while (cnt > 0 && (nbyte - nread - cnt) > 0);
+
+	if (cnt < 0)
+		return cnt;
+
+	return nread + cnt;
+}
+
+/** Write entire buffer.
+ *
+ * This function fails if it cannot write exactly @a len bytes to the file.
+ *
+ * @param fildes	File descriptor
+ * @param buf		Data, @a nbytes bytes long
+ * @param nbytes	Number of bytes to write
+ *
+ * @return		EOK on error, return value from write() if writing
+ *			failed.
+ */
+ssize_t write_all(int fildes, const void *buf, size_t nbyte)
+{
+	ssize_t cnt = 0;
+	ssize_t nwritten = 0;
+	const uint8_t *bp = (uint8_t *) buf;
+
+	do {
+		bp += cnt;
+		nwritten += cnt;
+		cnt = write(fildes, bp, nbyte - nwritten);
+	} while (cnt > 0 && ((ssize_t )nbyte - nwritten - cnt) > 0);
+
+	if (cnt < 0)
+		return cnt;
+
+	if ((ssize_t)nbyte - nwritten - cnt > 0)
+		return EIO;
+
+	return nbyte;
+}
+
 int fsync(int fildes)
 {
Index: uspace/lib/c/include/unistd.h
===================================================================
--- uspace/lib/c/include/unistd.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/c/include/unistd.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -63,4 +63,7 @@
 extern ssize_t read(int, void *, size_t);
 
+extern ssize_t read_all(int, void *, size_t);
+extern ssize_t write_all(int, const void *, size_t);
+
 extern off64_t lseek(int, off64_t, int);
 extern int ftruncate(int, aoff64_t);
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/fs/libfs.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -45,4 +45,5 @@
 #include <mem.h>
 #include <sys/stat.h>
+#include <stdlib.h>
 
 #define on_error(rc, action) \
@@ -61,4 +62,226 @@
 	} while (0)
 
+static fs_reg_t reg;
+
+static vfs_out_ops_t *vfs_out_ops = NULL;
+static libfs_ops_t *libfs_ops = NULL;
+
+static void libfs_mount(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
+static void libfs_unmount(libfs_ops_t *, ipc_callid_t, ipc_call_t *);
+static void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_callid_t,
+    ipc_call_t *);
+static void libfs_stat(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
+static void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_callid_t,
+    ipc_call_t *);
+
+static void vfs_out_mounted(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	char *opts;
+	int rc;
+	
+	/* Accept the mount options. */
+	rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	fs_index_t index;
+	aoff64_t size;
+	unsigned lnkcnt;
+	rc = vfs_out_ops->mounted(devmap_handle, opts, &index, &size, &lnkcnt);
+
+	if (rc == EOK)
+		async_answer_4(rid, EOK, index, LOWER32(size), UPPER32(size),
+		    lnkcnt);
+	else
+		async_answer_0(rid, rc);
+
+	free(opts);
+}
+
+static void vfs_out_mount(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_mount(libfs_ops, reg.fs_handle, rid, req);
+}
+
+static void vfs_out_unmounted(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	int rc; 
+
+	rc = vfs_out_ops->unmounted(devmap_handle);
+
+	async_answer_0(rid, rc);
+}
+
+static void vfs_out_unmount(ipc_callid_t rid, ipc_call_t *req)
+{
+		
+	libfs_unmount(libfs_ops, rid, req);
+}
+
+static void vfs_out_lookup(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_lookup(libfs_ops, reg.fs_handle, rid, req);
+}
+
+static void vfs_out_read(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
+	    IPC_GET_ARG4(*req));
+	size_t rbytes;
+	int rc;
+
+	rc = vfs_out_ops->read(devmap_handle, index, pos, &rbytes);
+
+	if (rc == EOK)
+		async_answer_1(rid, EOK, rbytes);
+	else
+		async_answer_0(rid, rc);
+}
+
+static void vfs_out_write(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	aoff64_t pos = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
+	    IPC_GET_ARG4(*req));
+	size_t wbytes;
+	aoff64_t nsize;
+	int rc;
+
+	rc = vfs_out_ops->write(devmap_handle, index, pos, &wbytes, &nsize);
+
+	if (rc == EOK)
+		async_answer_3(rid, EOK, wbytes, LOWER32(nsize), UPPER32(nsize));
+	else
+		async_answer_0(rid, rc);
+}
+
+static void vfs_out_truncate(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	aoff64_t size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*req),
+	    IPC_GET_ARG4(*req));
+	int rc;
+
+	rc = vfs_out_ops->truncate(devmap_handle, index, size);
+
+	async_answer_0(rid, rc);
+}
+
+static void vfs_out_close(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	int rc;
+
+	rc = vfs_out_ops->close(devmap_handle, index);
+
+	async_answer_0(rid, rc);
+}
+
+static void vfs_out_destroy(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	int rc;
+
+	rc = vfs_out_ops->destroy(devmap_handle, index);
+
+	async_answer_0(rid, rc);
+}
+
+static void vfs_out_open_node(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_open_node(libfs_ops, reg.fs_handle, rid, req);
+}
+
+static void vfs_out_stat(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_stat(libfs_ops, reg.fs_handle, rid, req);
+}
+
+static void vfs_out_sync(ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	int rc;
+
+	rc = vfs_out_ops->sync(devmap_handle, index);
+
+	async_answer_0(rid, rc);
+}
+
+static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	if (iid) {
+		/*
+		 * This only happens for connections opened by
+		 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
+		 * created by IPC_M_CONNECT_TO_ME.
+		 */
+		async_answer_0(iid, EOK);
+	}
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			return;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case VFS_OUT_MOUNTED:
+			vfs_out_mounted(callid, &call);
+			break;
+		case VFS_OUT_MOUNT:
+			vfs_out_mount(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNTED:
+			vfs_out_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			vfs_out_unmount(callid, &call);
+			break;
+		case VFS_OUT_LOOKUP:
+			vfs_out_lookup(callid, &call);
+			break;
+		case VFS_OUT_READ:
+			vfs_out_read(callid, &call);
+			break;
+		case VFS_OUT_WRITE:
+			vfs_out_write(callid, &call);
+			break;
+		case VFS_OUT_TRUNCATE:
+			vfs_out_truncate(callid, &call);
+			break;
+		case VFS_OUT_CLOSE:
+			vfs_out_close(callid, &call);
+			break;
+		case VFS_OUT_DESTROY:
+			vfs_out_destroy(callid, &call);
+			break;
+		case VFS_OUT_OPEN_NODE:
+			vfs_out_open_node(callid, &call);
+			break;
+		case VFS_OUT_STAT:
+			vfs_out_stat(callid, &call);
+			break;
+		case VFS_OUT_SYNC:
+			vfs_out_sync(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+}
+
 /** Register file system server.
  *
@@ -68,16 +291,14 @@
  *
  * @param sess Session for communication with VFS.
- * @param reg  File system registration structure. It will be
- *             initialized by this function.
  * @param info VFS info structure supplied by the file system
  *             implementation.
- * @param conn Connection fibril for handling all calls originating in
- *             VFS.
+ * @param vops Address of the vfs_out_ops_t structure.
+ * @param lops Address of the libfs_ops_t structure.
  *
  * @return EOK on success or a non-zero error code on errror.
  *
  */
-int fs_register(async_sess_t *sess, fs_reg_t *reg, vfs_info_t *info,
-    async_client_conn_t conn)
+int fs_register(async_sess_t *sess, vfs_info_t *info, vfs_out_ops_t *vops,
+    libfs_ops_t *lops)
 {
 	/*
@@ -104,13 +325,19 @@
 	
 	/*
+	 * Set VFS_OUT and libfs operations.
+	 */
+	vfs_out_ops = vops;
+	libfs_ops = lops;
+
+	/*
 	 * Ask VFS for callback connection.
 	 */
-	async_connect_to_me(exch, 0, 0, 0, conn, NULL);
+	async_connect_to_me(exch, 0, 0, 0, vfs_connection, NULL);
 	
 	/*
 	 * Allocate piece of address space for PLB.
 	 */
-	reg->plb_ro = as_get_mappable_page(PLB_SIZE);
-	if (!reg->plb_ro) {
+	reg.plb_ro = as_get_mappable_page(PLB_SIZE);
+	if (!reg.plb_ro) {
 		async_exchange_end(exch);
 		async_wait_for(req, NULL);
@@ -121,5 +348,5 @@
 	 * Request sharing the Path Lookup Buffer with VFS.
 	 */
-	rc = async_share_in_start_0_0(exch, reg->plb_ro, PLB_SIZE);
+	rc = async_share_in_start_0_0(exch, reg.plb_ro, PLB_SIZE);
 	
 	async_exchange_end(exch);
@@ -134,5 +361,5 @@
 	 */
 	async_wait_for(req, NULL);
-	reg->fs_handle = (int) IPC_GET_ARG1(answer);
+	reg.fs_handle = (int) IPC_GET_ARG1(answer);
 	
 	/*
@@ -140,5 +367,5 @@
 	 * the same connection fibril as well.
 	 */
-	async_set_client_connection(conn);
+	async_set_client_connection(vfs_connection);
 	
 	return IPC_GET_RETVAL(answer);
@@ -151,10 +378,10 @@
 
 void libfs_mount(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
-    ipc_call_t *request)
-{
-	devmap_handle_t mp_devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*request);
-	fs_handle_t mr_fs_handle = (fs_handle_t) IPC_GET_ARG3(*request);
-	devmap_handle_t mr_devmap_handle = (devmap_handle_t) IPC_GET_ARG4(*request);
+    ipc_call_t *req)
+{
+	devmap_handle_t mp_devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*req);
+	fs_handle_t mr_fs_handle = (fs_handle_t) IPC_GET_ARG3(*req);
+	devmap_handle_t mr_devmap_handle = (devmap_handle_t) IPC_GET_ARG4(*req);
 	
 	async_sess_t *mountee_sess = async_clone_receive(EXCHANGE_PARALLEL);
@@ -208,12 +435,12 @@
 	 * Do not release the FS node so that it stays in memory.
 	 */
-	async_answer_3(rid, rc, IPC_GET_ARG1(answer), IPC_GET_ARG2(answer),
-	    IPC_GET_ARG3(answer));
-}
-
-void libfs_unmount(libfs_ops_t *ops, ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t mp_devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*request);
+	async_answer_4(rid, rc, IPC_GET_ARG1(answer), IPC_GET_ARG2(answer),
+	    IPC_GET_ARG3(answer), IPC_GET_ARG4(answer));
+}
+
+void libfs_unmount(libfs_ops_t *ops, ipc_callid_t rid, ipc_call_t *req)
+{
+	devmap_handle_t mp_devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*req);
+	fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*req);
 	fs_node_t *fn;
 	int res;
@@ -259,4 +486,9 @@
 }
 
+static char plb_get_char(unsigned pos)
+{
+	return reg.plb_ro[pos % PLB_SIZE];
+}
+
 /** Lookup VFS triplet by name in the file system name space.
  *
@@ -273,12 +505,12 @@
  */
 void libfs_lookup(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
-    ipc_call_t *request)
-{
-	unsigned int first = IPC_GET_ARG1(*request);
-	unsigned int last = IPC_GET_ARG2(*request);
+    ipc_call_t *req)
+{
+	unsigned int first = IPC_GET_ARG1(*req);
+	unsigned int last = IPC_GET_ARG2(*req);
 	unsigned int next = first;
-	devmap_handle_t devmap_handle = IPC_GET_ARG3(*request);
-	int lflag = IPC_GET_ARG4(*request);
-	fs_index_t index = IPC_GET_ARG5(*request);
+	devmap_handle_t devmap_handle = IPC_GET_ARG3(*req);
+	int lflag = IPC_GET_ARG4(*req);
+	fs_index_t index = IPC_GET_ARG5(*req);
 	char component[NAME_MAX + 1];
 	int len;
@@ -298,5 +530,6 @@
 		async_exch_t *exch = async_exchange_begin(cur->mp_data.sess);
 		async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next, last,
-		    cur->mp_data.devmap_handle, lflag, index, IPC_FF_ROUTE_FROM_ME);
+		    cur->mp_data.devmap_handle, lflag, index,
+		    IPC_FF_ROUTE_FROM_ME);
 		async_exchange_end(exch);
 		
@@ -306,5 +539,5 @@
 	
 	/* Eat slash */
-	if (ops->plb_get_char(next) == '/')
+	if (plb_get_char(next) == '/')
 		next++;
 	
@@ -319,5 +552,5 @@
 		/* Collect the component */
 		len = 0;
-		while ((next <= last) && (ops->plb_get_char(next) != '/')) {
+		while ((next <= last) && (plb_get_char(next) != '/')) {
 			if (len + 1 == NAME_MAX) {
 				/* Component length overflow */
@@ -325,5 +558,5 @@
 				goto out;
 			}
-			component[len++] = ops->plb_get_char(next);
+			component[len++] = plb_get_char(next);
 			/* Process next character */
 			next++;
@@ -357,6 +590,6 @@
 			
 			async_exch_t *exch = async_exchange_begin(tmp->mp_data.sess);
-			async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next, last,
-			    tmp->mp_data.devmap_handle, lflag, index,
+			async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next,
+			    last, tmp->mp_data.devmap_handle, lflag, index,
 			    IPC_FF_ROUTE_FROM_ME);
 			async_exchange_end(exch);
@@ -451,5 +684,5 @@
 			len = 0;
 			while (next <= last) {
-				if (ops->plb_get_char(next) == '/') {
+				if (plb_get_char(next) == '/') {
 					/* More than one component */
 					async_answer_0(rid, ENOENT);
@@ -463,5 +696,5 @@
 				}
 				
-				component[len++] = ops->plb_get_char(next);
+				component[len++] = plb_get_char(next);
 				/* Process next character */
 				next++;
@@ -637,5 +870,6 @@
 	rc = ops->node_open(fn);
 	aoff64_t size = ops->size_get(fn);
-	async_answer_4(rid, rc, LOWER32(size), UPPER32(size), ops->lnkcnt_get(fn),
+	async_answer_4(rid, rc, LOWER32(size), UPPER32(size),
+	    ops->lnkcnt_get(fn),
 	    (ops->is_file(fn) ? L_FILE : 0) | (ops->is_directory(fn) ? L_DIRECTORY : 0));
 	
Index: uspace/lib/fs/libfs.h
===================================================================
--- uspace/lib/fs/libfs.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/fs/libfs.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -43,4 +43,17 @@
 
 typedef struct {
+	int (* mounted)(devmap_handle_t, const char *, fs_index_t *, aoff64_t *,
+	    unsigned *);
+	int (* unmounted)(devmap_handle_t);
+	int (* read)(devmap_handle_t, fs_index_t, aoff64_t, size_t *);
+	int (* write)(devmap_handle_t, fs_index_t, aoff64_t, size_t *,
+	    aoff64_t *);
+	int (* truncate)(devmap_handle_t, fs_index_t, aoff64_t);
+	int (* close)(devmap_handle_t, fs_index_t);
+	int (* destroy)(devmap_handle_t, fs_index_t);
+	int (* sync)(devmap_handle_t, fs_index_t);
+} vfs_out_ops_t;
+
+typedef struct {
 	bool mp_active;
 	async_sess_t *sess;
@@ -71,11 +84,10 @@
 	int (* has_children)(bool *, fs_node_t *);
 	/*
-	 * The second set of methods are usually mere getters that do not return
-	 * an integer error code.
+	 * The second set of methods are usually mere getters that do not
+	 * return an integer error code.
 	 */
 	fs_index_t (* index_get)(fs_node_t *);
 	aoff64_t (* size_get)(fs_node_t *);
 	unsigned int (* lnkcnt_get)(fs_node_t *);
-	char (* plb_get_char)(unsigned pos);
 	bool (* is_directory)(fs_node_t *);
 	bool (* is_file)(fs_node_t *);
@@ -88,15 +100,8 @@
 } fs_reg_t;
 
-extern int fs_register(async_sess_t *, fs_reg_t *, vfs_info_t *,
-    async_client_conn_t);
+extern int fs_register(async_sess_t *, vfs_info_t *, vfs_out_ops_t *,
+    libfs_ops_t *);
 
 extern void fs_node_initialize(fs_node_t *);
-
-extern void libfs_mount(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
-extern void libfs_unmount(libfs_ops_t *, ipc_callid_t, ipc_call_t *);
-extern void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
-extern void libfs_stat(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
-extern void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_callid_t,
-    ipc_call_t *);
 
 #endif
Index: uspace/lib/scsi/include/scsi/sbc.h
===================================================================
--- uspace/lib/scsi/include/scsi/sbc.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/scsi/include/scsi/sbc.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -56,9 +56,25 @@
 };
 
+/** SCSI Read (10) command */
+typedef struct {
+	/** Operation code (SCSI_CMD_READ_10) */
+	uint8_t op_code;
+	/** RdProtect, DPO, FUA, Reserved, FUA_NV, Obsolete */
+	uint8_t flags;
+	/** Logical block address */
+	uint32_t lba;
+	/** Reserved, Group Number */
+	uint8_t group_no;
+	/** Transfer length */
+	uint16_t xfer_len;
+	/** Control */
+	uint8_t control;
+} __attribute__((packed)) scsi_cdb_read_10_t;
+
 /** SCSI Read (12) command */
 typedef struct {
 	/** Operation code (SCSI_CMD_READ_12) */
 	uint8_t op_code;
-	/** RdProtect, DPO, FUA, Reserved, FUA_NV, Reserved */
+	/** RdProtect, DPO, FUA, Reserved, FUA_NV, Obsolete */
 	uint8_t flags;
 	/** Logical block address */
@@ -115,9 +131,25 @@
 } scsi_read_capacity_10_data_t;
 
+/** SCSI Write (10) command */
+typedef struct {
+	/** Operation code (SCSI_CMD_WRITE_10) */
+	uint8_t op_code;
+	/** WrProtect, DPO, FUA, Reserved, FUA_NV, Obsolete */
+	uint8_t flags;
+	/** Logical block address */
+	uint32_t lba;
+	/** Reserved, Group Number */
+	uint8_t group_no;
+	/** Transfer length */
+	uint16_t xfer_len;
+	/** Control */
+	uint8_t control;
+} __attribute__((packed)) scsi_cdb_write_10_t;
+
 /** SCSI Write (12) command */
 typedef struct {
 	/** Operation code (SCSI_CMD_WRITE_12) */
 	uint8_t op_code;
-	/** WrProtect, DPO, FUA, Reserved, FUA_NV, Reserved */
+	/** WrProtect, DPO, FUA, Reserved, FUA_NV, Obsolete */
 	uint8_t flags;
 	/** Logical block address */
Index: uspace/lib/scsi/include/scsi/spc.h
===================================================================
--- uspace/lib/scsi/include/scsi/spc.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/scsi/include/scsi/spc.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -179,5 +179,5 @@
 	uint8_t additional_len;
 	/** Command-specific Information */
-	uint8_t cmd_spec;
+	uint32_t cmd_spec;
 	/** Additional Sense Code */
 	uint8_t additional_code;
@@ -205,9 +205,14 @@
 	SCSI_SK_ABORTED_COMMAND	= 0xb,
 	SCSI_SK_VOLUME_OVERFLOW	= 0xd,
-	SCSI_SK_MISCOMPARE	= 0xe
+	SCSI_SK_MISCOMPARE	= 0xe,
+
+	SCSI_SK_LIMIT		= 0x10
 };
 
 extern const char *scsi_dev_type_str[SCSI_DEV_LIMIT];
+extern const char *scsi_sense_key_str[SCSI_SK_LIMIT];
+
 extern const char *scsi_get_dev_type_str(unsigned);
+extern const char *scsi_get_sense_key_str(unsigned);
 
 #endif
Index: uspace/lib/scsi/src/spc.c
===================================================================
--- uspace/lib/scsi/src/spc.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/lib/scsi/src/spc.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -44,4 +44,21 @@
 };
 
+const char *scsi_sense_key_str[SCSI_SK_LIMIT] = {
+	[SCSI_SK_NO_SENSE]		= "No Sense",
+	[SCSI_SK_RECOVERED_ERROR]	= "Recovered Error",
+	[SCSI_SK_NOT_READY]		= "Not Ready",
+	[SCSI_SK_MEDIUM_ERROR]		= "Medium Error",
+	[SCSI_SK_HARDWARE_ERROR]	= "Hardware Error",
+	[SCSI_SK_ILLEGAL_REQUEST]	= "Illegal Request",
+	[SCSI_SK_UNIT_ATTENTION]	= "Unit Attention",
+	[SCSI_SK_DATA_PROTECT]		= "Data Protect",
+	[SCSI_SK_BLANK_CHECK]		= "Blank Check",
+	[SCSI_SK_VENDOR_SPECIFIC]	= "Vendor-specific",
+	[SCSI_SK_COPY_ABORTED]		= "Copy Aborted",
+	[SCSI_SK_ABORTED_COMMAND]	= "Aborted Command",
+	[SCSI_SK_VOLUME_OVERFLOW]	= "Volume Overflow",
+	[SCSI_SK_MISCOMPARE]		= "Miscompare"
+};
+
 /** Get peripheral device type string.
  *
@@ -53,6 +70,21 @@
 {
 	if (dev_type >= SCSI_DEV_LIMIT || scsi_dev_type_str[dev_type] == NULL)
-		return "<unknown>";
+		return "Unknown";
 
 	return scsi_dev_type_str[dev_type];
 }
+
+/** Get sense key string.
+ *
+ * Return string description of SCSI sense key.
+ * The returned string is valid indefinitely, the caller should
+ * not attempt to free it.
+ */
+const char *scsi_get_sense_key_str(unsigned sense_key)
+{
+	if (sense_key >= SCSI_SK_LIMIT || scsi_sense_key_str[sense_key] == NULL)
+		return "Unknown";
+
+	return scsi_sense_key_str[sense_key];
+}
+
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/devman/devman.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -270,7 +270,8 @@
 	}
 	
-	ssize_t read_bytes = safe_read(fd, buf, len);
+	ssize_t read_bytes = read_all(fd, buf, len);
 	if (read_bytes <= 0) {
-		log_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
+		log_msg(LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
+		    read_bytes);
 		goto cleanup;
 	}
@@ -421,7 +422,7 @@
 	}
 	
-	insert_fun_node(tree, fun, clone_string(""), NULL);
+	insert_fun_node(tree, fun, str_dup(""), NULL);
 	match_id_t *id = create_match_id();
-	id->id = clone_string("root");
+	id->id = str_dup("root");
 	id->score = 100;
 	add_match_id(&fun->match_ids, id);
Index: uspace/srv/devman/util.c
===================================================================
--- uspace/srv/devman/util.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/devman/util.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -91,15 +91,4 @@
 }
 
-char *clone_string(const char *s)
-{
-	size_t size = str_size(s) + 1;
-	char *str;
-	
-	str = (char *) malloc(size);
-	if (str != NULL)
-		str_cpy(str, size, s);
-	return str;
-}
-
 void replace_char(char *str, char orig, char repl)
 {
@@ -111,31 +100,4 @@
 }
 
-ssize_t safe_read(int fd, void *buffer, size_t size)
-{
-	if (size == 0) {
-		return 0;
-	}
-
-	uint8_t *buf_ptr = (uint8_t *) buffer;
-
-	size_t total_read = 0;
-	while (total_read < size) {
-		ssize_t bytes_read = read(fd, buf_ptr, size - total_read);
-		if (bytes_read < 0) {
-			/* Error. */
-			return bytes_read;
-		} else if (bytes_read == 0) {
-			/* Possibly end of file. */
-			break;
-		} else {
-			/* Read at least something. */
-			buf_ptr += bytes_read;
-			total_read += bytes_read;
-		}
-	}
-
-	return (ssize_t) total_read;
-}
-
 /** @}
  */
Index: uspace/srv/devman/util.h
===================================================================
--- uspace/srv/devman/util.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/devman/util.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -44,8 +44,5 @@
 extern size_t get_nonspace_len(const char *);
 extern void free_not_null(const void *);
-extern char *clone_string(const char *);
 extern void replace_char(char *, char, char);
-
-extern ssize_t safe_read(int, void *, size_t);
 
 #endif
Index: uspace/srv/fs/devfs/devfs.c
===================================================================
--- uspace/srv/fs/devfs/devfs.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/devfs/devfs.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -57,65 +57,4 @@
 };
 
-fs_reg_t devfs_reg;
-
-static void devfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	if (iid)
-		async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_MOUNTED:
-			devfs_mounted(callid, &call);
-			break;
-		case VFS_OUT_MOUNT:
-			devfs_mount(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNTED:
-			devfs_unmounted(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNT:
-			devfs_unmount(callid, &call);
-			break;
-		case VFS_OUT_LOOKUP:
-			devfs_lookup(callid, &call);
-			break;
-		case VFS_OUT_OPEN_NODE:
-			devfs_open_node(callid, &call);
-			break;
-		case VFS_OUT_STAT:
-			devfs_stat(callid, &call);
-			break;
-		case VFS_OUT_READ:
-			devfs_read(callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			devfs_write(callid, &call);
-			break;
-		case VFS_OUT_TRUNCATE:
-			devfs_truncate(callid, &call);
-			break;
-		case VFS_OUT_CLOSE:
-			devfs_close(callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			devfs_sync(callid, &call);
-			break;
-		case VFS_OUT_DESTROY:
-			devfs_destroy(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		}
-	}
-}
-
 int main(int argc, char *argv[])
 {
@@ -134,6 +73,6 @@
 	}
 	
-	int rc = fs_register(vfs_sess, &devfs_reg, &devfs_vfs_info,
-	    devfs_connection);
+	int rc = fs_register(vfs_sess, &devfs_vfs_info, &devfs_ops,
+	    &devfs_libfs_ops);
 	if (rc != EOK) {
 		printf("%s: Failed to register file system (%d)\n", NAME, rc);
@@ -152,2 +91,3 @@
  * @}
  */
+
Index: uspace/srv/fs/devfs/devfs.h
===================================================================
--- uspace/srv/fs/devfs/devfs.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/devfs/devfs.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -36,5 +36,6 @@
 #include <libfs.h>
 
-extern fs_reg_t devfs_reg;
+extern vfs_out_ops_t devfs_ops;
+extern libfs_ops_t devfs_libfs_ops;
 
 #endif
Index: uspace/srv/fs/devfs/devfs_ops.c
===================================================================
--- uspace/srv/fs/devfs/devfs_ops.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/devfs/devfs_ops.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -403,9 +403,4 @@
 }
 
-static char devfs_plb_get_char(unsigned pos)
-{
-	return devfs_reg.plb_ro[pos % PLB_SIZE];
-}
-
 static bool devfs_is_directory(fs_node_t *fn)
 {
@@ -447,5 +442,4 @@
 	.size_get = devfs_size_get,
 	.lnkcnt_get = devfs_lnkcnt_get,
-	.plb_get_char = devfs_plb_get_char,
 	.is_directory = devfs_is_directory,
 	.is_file = devfs_is_file,
@@ -462,56 +456,22 @@
 }
 
-void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	char *opts;
-	
-	/* Accept the mount options */
-	sysarg_t retval = async_data_write_accept((void **) &opts, true, 0, 0,
-	    0, NULL);
-	if (retval != EOK) {
-		async_answer_0(rid, retval);
-		return;
-	}
-	
-	free(opts);
-	async_answer_3(rid, EOK, 0, 0, 0);
-}
-
-void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_mount(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
-}
-
-void devfs_unmounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	async_answer_0(rid, ENOTSUP);
-}
-
-void devfs_unmount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_unmount(&devfs_libfs_ops, rid, request);
-}
-
-void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_lookup(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
-}
-
-void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_open_node(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
-}
-
-void devfs_stat(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_stat(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
-}
-
-void devfs_read(ipc_callid_t rid, ipc_call_t *request)
-{
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
+static int devfs_mounted(devmap_handle_t devmap_handle, const char *opts,
+    fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
+{
+	*index = 0;
+	*size = 0;
+	*lnkcnt = 0;
+	return EOK;
+}
+
+static int devfs_unmounted(devmap_handle_t devmap_handle)
+{
+	return ENOTSUP;
+}
+
+static int
+devfs_read(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
 	if (index == 0) {
 		ipc_callid_t callid;
@@ -519,6 +479,5 @@
 		if (!async_data_read_receive(&callid, &size)) {
 			async_answer_0(callid, EINVAL);
-			async_answer_0(rid, EINVAL);
-			return;
+			return EINVAL;
 		}
 		
@@ -540,6 +499,6 @@
 			async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
 			free(desc);
-			async_answer_1(rid, EOK, 1);
-			return;
+			*rbytes = 1;
+			return EOK;
 		}
 		
@@ -555,6 +514,6 @@
 				async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
 				free(desc);
-				async_answer_1(rid, EOK, 1);
-				return;
+				*rbytes = 1;
+				return EOK;
 			}
 			
@@ -563,6 +522,5 @@
 		
 		async_answer_0(callid, ENOENT);
-		async_answer_1(rid, ENOENT, 0);
-		return;
+		return ENOENT;
 	}
 	
@@ -575,6 +533,5 @@
 		if (!async_data_read_receive(&callid, &size)) {
 			async_answer_0(callid, EINVAL);
-			async_answer_0(rid, EINVAL);
-			return;
+			return EINVAL;
 		}
 		
@@ -585,12 +542,11 @@
 			async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
 			free(desc);
-			async_answer_1(rid, EOK, 1);
-			return;
+			*rbytes = 1;
+			return EOK;
 		}
 		
 		free(desc);
 		async_answer_0(callid, ENOENT);
-		async_answer_1(rid, ENOENT, 0);
-		return;
+		return ENOENT;
 	}
 	
@@ -606,6 +562,5 @@
 		if (lnk == NULL) {
 			fibril_mutex_unlock(&devices_mutex);
-			async_answer_0(rid, ENOENT);
-			return;
+			return ENOENT;
 		}
 		
@@ -617,6 +572,5 @@
 			fibril_mutex_unlock(&devices_mutex);
 			async_answer_0(callid, EINVAL);
-			async_answer_0(rid, EINVAL);
-			return;
+			return EINVAL;
 		}
 		
@@ -625,7 +579,6 @@
 		
 		ipc_call_t answer;
-		aid_t msg = async_send_3(exch, IPC_GET_IMETHOD(*request),
-		    IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
-		    IPC_GET_ARG3(*request), &answer);
+		aid_t msg = async_send_4(exch, VFS_OUT_READ, devmap_handle,
+		    index, LOWER32(pos), UPPER32(pos), &answer);
 		
 		/* Forward the IPC_M_DATA_READ request to the driver */
@@ -639,21 +592,18 @@
 		sysarg_t rc;
 		async_wait_for(msg, &rc);
-		size_t bytes = IPC_GET_ARG1(answer);
-		
-		/* Driver reply is the final result of the whole operation */
-		async_answer_1(rid, rc, bytes);
-		return;
-	}
-	
-	async_answer_0(rid, ENOENT);
-}
-
-void devfs_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	if (index == 0) {
-		async_answer_0(rid, ENOTSUP);
-		return;
-	}
+		
+		*rbytes = IPC_GET_ARG1(answer);
+		return rc;
+	}
+	
+	return ENOENT;
+}
+
+static int
+devfs_write(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
+	if (index == 0)
+		return ENOTSUP;
 	
 	devmap_handle_type_t type = devmap_handle_probe(index);
@@ -661,6 +611,5 @@
 	if (type == DEV_HANDLE_NAMESPACE) {
 		/* Namespace directory */
-		async_answer_0(rid, ENOTSUP);
-		return;
+		return ENOTSUP;
 	}
 	
@@ -675,6 +624,5 @@
 		if (lnk == NULL) {
 			fibril_mutex_unlock(&devices_mutex);
-			async_answer_0(rid, ENOENT);
-			return;
+			return ENOENT;
 		}
 		
@@ -686,6 +634,5 @@
 			fibril_mutex_unlock(&devices_mutex);
 			async_answer_0(callid, EINVAL);
-			async_answer_0(rid, EINVAL);
-			return;
+			return EINVAL;
 		}
 		
@@ -694,7 +641,6 @@
 		
 		ipc_call_t answer;
-		aid_t msg = async_send_3(exch, IPC_GET_IMETHOD(*request),
-		    IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
-		    IPC_GET_ARG3(*request), &answer);
+		aid_t msg = async_send_4(exch, VFS_OUT_WRITE, devmap_handle,
+		    index, LOWER32(pos), UPPER32(pos), &answer);
 		
 		/* Forward the IPC_M_DATA_WRITE request to the driver */
@@ -708,27 +654,23 @@
 		sysarg_t rc;
 		async_wait_for(msg, &rc);
-		size_t bytes = IPC_GET_ARG1(answer);
-		
-		/* Driver reply is the final result of the whole operation */
-		async_answer_1(rid, rc, bytes);
-		return;
-	}
-	
-	async_answer_0(rid, ENOENT);
-}
-
-void devfs_truncate(ipc_callid_t rid, ipc_call_t *request)
-{
-	async_answer_0(rid, ENOTSUP);
-}
-
-void devfs_close(ipc_callid_t rid, ipc_call_t *request)
-{
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	
-	if (index == 0) {
-		async_answer_0(rid, EOK);
-		return;
-	}
+		
+		*wbytes = IPC_GET_ARG1(answer);
+		*nsize = 0;
+		return rc;
+	}
+	
+	return ENOENT;
+}
+
+static int
+devfs_truncate(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t size)
+{
+	return ENOTSUP;
+}
+
+static int devfs_close(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	if (index == 0)
+		return EOK;
 	
 	devmap_handle_type_t type = devmap_handle_probe(index);
@@ -736,6 +678,5 @@
 	if (type == DEV_HANDLE_NAMESPACE) {
 		/* Namespace directory */
-		async_answer_0(rid, EOK);
-		return;
+		return EOK;
 	}
 	
@@ -749,6 +690,5 @@
 		if (lnk == NULL) {
 			fibril_mutex_unlock(&devices_mutex);
-			async_answer_0(rid, ENOENT);
-			return;
+			return ENOENT;
 		}
 		
@@ -764,19 +704,14 @@
 		fibril_mutex_unlock(&devices_mutex);
 		
-		async_answer_0(rid, EOK);
-		return;
-	}
-	
-	async_answer_0(rid, ENOENT);
-}
-
-void devfs_sync(ipc_callid_t rid, ipc_call_t *request)
-{
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	
-	if (index == 0) {
-		async_answer_0(rid, EOK);
-		return;
-	}
+		return EOK;
+	}
+	
+	return ENOENT;
+}
+
+static int devfs_sync(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	if (index == 0)
+		return EOK;
 	
 	devmap_handle_type_t type = devmap_handle_probe(index);
@@ -784,6 +719,5 @@
 	if (type == DEV_HANDLE_NAMESPACE) {
 		/* Namespace directory */
-		async_answer_0(rid, EOK);
-		return;
+		return EOK;
 	}
 	
@@ -797,6 +731,5 @@
 		if (lnk == NULL) {
 			fibril_mutex_unlock(&devices_mutex);
-			async_answer_0(rid, ENOENT);
-			return;
+			return ENOENT;
 		}
 		
@@ -808,6 +741,6 @@
 		
 		ipc_call_t answer;
-		aid_t msg = async_send_2(exch, IPC_GET_IMETHOD(*request),
-		    IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer);
+		aid_t msg = async_send_2(exch, VFS_OUT_SYNC, devmap_handle,
+		    index, &answer);
 		
 		async_exchange_end(exch);
@@ -819,16 +752,25 @@
 		async_wait_for(msg, &rc);
 		
-		/* Driver reply is the final result of the whole operation */
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	async_answer_0(rid, ENOENT);
-}
-
-void devfs_destroy(ipc_callid_t rid, ipc_call_t *request)
-{
-	async_answer_0(rid, ENOTSUP);
-}
+		return rc;
+	}
+	
+	return  ENOENT;
+}
+
+static int devfs_destroy(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	return ENOTSUP;
+}
+
+vfs_out_ops_t devfs_ops = {
+	.mounted = devfs_mounted,
+	.unmounted = devfs_unmounted,
+	.read = devfs_read,
+	.write = devfs_write,
+	.truncate = devfs_truncate,
+	.close = devfs_close,
+	.destroy = devfs_destroy,
+	.sync = devfs_sync,
+};
 
 /**
Index: uspace/srv/fs/devfs/devfs_ops.h
===================================================================
--- uspace/srv/fs/devfs/devfs_ops.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/devfs/devfs_ops.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -34,22 +34,7 @@
 #define DEVFS_DEVFS_OPS_H_
 
-#include <ipc/common.h>
 #include <bool.h>
 
 extern bool devfs_init(void);
-
-extern void devfs_mounted(ipc_callid_t, ipc_call_t *);
-extern void devfs_mount(ipc_callid_t, ipc_call_t *);
-extern void devfs_unmounted(ipc_callid_t, ipc_call_t *);
-extern void devfs_unmount(ipc_callid_t, ipc_call_t *);
-extern void devfs_lookup(ipc_callid_t, ipc_call_t *);
-extern void devfs_open_node(ipc_callid_t, ipc_call_t *);
-extern void devfs_stat(ipc_callid_t, ipc_call_t *);
-extern void devfs_sync(ipc_callid_t, ipc_call_t *);
-extern void devfs_read(ipc_callid_t, ipc_call_t *);
-extern void devfs_write(ipc_callid_t, ipc_call_t *);
-extern void devfs_truncate(ipc_callid_t, ipc_call_t *);
-extern void devfs_close(ipc_callid_t, ipc_call_t *);
-extern void devfs_destroy(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/fs/ext2fs/ext2fs.c
===================================================================
--- uspace/srv/fs/ext2fs/ext2fs.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/ext2fs/ext2fs.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -1,5 +1,4 @@
 /*
  * Copyright (c) 2006 Martin Decky
- * Copyright (c) 2008 Jakub Jermar
  * Copyright (c) 2011 Martin Sucha
  * All rights reserved.
@@ -55,90 +54,4 @@
 };
 
-fs_reg_t ext2fs_reg;
-
-/**
- * This connection fibril processes VFS requests from VFS.
- *
- * In order to support simultaneous VFS requests, our design is as follows.
- * The connection fibril accepts VFS requests from VFS. If there is only one
- * instance of the fibril, VFS will need to serialize all VFS requests it sends
- * to EXT2FS. To overcome this bottleneck, VFS can send EXT2FS the IPC_M_CONNECT_ME_TO
- * call. In that case, a new connection fibril will be created, which in turn
- * will accept the call. Thus, a new phone will be opened for VFS.
- *
- * There are few issues with this arrangement. First, VFS can run out of
- * available phones. In that case, VFS can close some other phones or use one
- * phone for more serialized requests. Similarily, EXT2FS can refuse to duplicate
- * the connection. VFS should then just make use of already existing phones and
- * route its requests through them. To avoid paying the fibril creation price 
- * upon each request, EXT2FS might want to keep the connections open after the
- * request has been completed.
- */
-static void ext2fs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	if (iid) {
-		/*
-		 * This only happens for connections opened by
-		 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
-		 * created by IPC_M_CONNECT_TO_ME.
-		 */
-		async_answer_0(iid, EOK);
-	}
-	
-	dprintf(NAME ": connection opened\n");
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_MOUNTED:
-			ext2fs_mounted(callid, &call);
-			break;
-		case VFS_OUT_MOUNT:
-			ext2fs_mount(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNTED:
-			ext2fs_unmounted(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNT:
-			ext2fs_unmount(callid, &call);
-			break;
-		case VFS_OUT_LOOKUP:
-			ext2fs_lookup(callid, &call);
-			break;
-		case VFS_OUT_READ:
-			ext2fs_read(callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			ext2fs_write(callid, &call);
-			break;
-		case VFS_OUT_TRUNCATE:
-			ext2fs_truncate(callid, &call);
-			break;
-		case VFS_OUT_STAT:
-			ext2fs_stat(callid, &call);
-			break;
-		case VFS_OUT_CLOSE:
-			ext2fs_close(callid, &call);
-			break;
-		case VFS_OUT_DESTROY:
-			ext2fs_destroy(callid, &call);
-			break;
-		case VFS_OUT_OPEN_NODE:
-			ext2fs_open_node(callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			ext2fs_sync(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		}
-	}
-}
-
 int main(int argc, char **argv)
 {
@@ -158,5 +71,6 @@
 	}	
 		
-	rc = fs_register(vfs_sess, &ext2fs_reg, &ext2fs_vfs_info, ext2fs_connection);
+	rc = fs_register(vfs_sess, &ext2fs_vfs_info, &ext2fs_ops,
+	    &ext2fs_libfs_ops);
 	if (rc != EOK) {
 		fprintf(stdout, NAME ": Failed to register fs (%d)\n", rc);
Index: uspace/srv/fs/ext2fs/ext2fs.h
===================================================================
--- uspace/srv/fs/ext2fs/ext2fs.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/ext2fs/ext2fs.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -1,4 +1,3 @@
 /*
- * Copyright (c) 2008 Jakub Jermar
  * Copyright (c) 2011 Martin Sucha
  * All rights reserved.
@@ -36,35 +35,14 @@
 
 #include <libext2.h>
-#include <fibril_synch.h>
 #include <libfs.h>
-#include <atomic.h>
 #include <sys/types.h>
-#include <bool.h>
-#include "../../vfs/vfs.h"
-
-#ifndef dprintf
-#define dprintf(...)	printf(__VA_ARGS__)
-#endif
 
 #define min(a, b)		((a) < (b) ? (a) : (b))
 
-extern fs_reg_t ext2fs_reg;
+extern vfs_out_ops_t ext2fs_ops;
+extern libfs_ops_t ext2fs_libfs_ops;
 
 extern int ext2fs_global_init(void);
 extern int ext2fs_global_fini(void);
-extern void ext2fs_mounted(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_mount(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_unmounted(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_unmount(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_lookup(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_read(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_write(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_truncate(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_stat(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_close(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_destroy(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_open_node(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_stat(ipc_callid_t, ipc_call_t *);
-extern void ext2fs_sync(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/fs/ext2fs/ext2fs_ops.c
===================================================================
--- uspace/srv/fs/ext2fs/ext2fs_ops.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/ext2fs/ext2fs_ops.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -1,4 +1,3 @@
 /*
- * Copyright (c) 2008 Jakub Jermar
  * Copyright (c) 2011 Martin Sucha
  * All rights reserved.
@@ -87,8 +86,8 @@
  */
 static int ext2fs_instance_get(devmap_handle_t, ext2fs_instance_t **);
-static void ext2fs_read_directory(ipc_callid_t, ipc_callid_t, aoff64_t,
-	size_t, ext2fs_instance_t *, ext2_inode_ref_t *);
-static void ext2fs_read_file(ipc_callid_t, ipc_callid_t, aoff64_t,
-	size_t, ext2fs_instance_t *, ext2_inode_ref_t *);
+static int ext2fs_read_directory(ipc_callid_t, aoff64_t, size_t,
+    ext2fs_instance_t *, ext2_inode_ref_t *, size_t *);
+static int ext2fs_read_file(ipc_callid_t, aoff64_t, size_t, ext2fs_instance_t *,
+    ext2_inode_ref_t *, size_t *);
 static bool ext2fs_is_dots(const uint8_t *, size_t);
 static int ext2fs_node_get_core(fs_node_t **, ext2fs_instance_t *, fs_index_t);
@@ -111,5 +110,4 @@
 static aoff64_t ext2fs_size_get(fs_node_t *);
 static unsigned ext2fs_lnkcnt_get(fs_node_t *);
-static char ext2fs_plb_get_char(unsigned);
 static bool ext2fs_is_directory(fs_node_t *);
 static bool ext2fs_is_file(fs_node_t *node);
@@ -538,9 +536,4 @@
 	EXT2FS_DBG("%u", count);
 	return count;
-}
-
-char ext2fs_plb_get_char(unsigned pos)
-{
-	return ext2fs_reg.plb_ro[pos % PLB_SIZE];
 }
 
@@ -586,5 +579,4 @@
 	.size_get = ext2fs_size_get,
 	.lnkcnt_get = ext2fs_lnkcnt_get,
-	.plb_get_char = ext2fs_plb_get_char,
 	.is_directory = ext2fs_is_directory,
 	.is_file = ext2fs_is_file,
@@ -596,30 +588,17 @@
  */
 
-void ext2fs_mounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	int rc;
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+static int ext2fs_mounted(devmap_handle_t devmap_handle, const char *opts,
+   fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
+{
+	EXT2FS_DBG("");
+	int rc;
 	ext2_filesystem_t *fs;
 	ext2fs_instance_t *inst;
 	bool read_only;
 	
-	/* Accept the mount options */
-	char *opts;
-	rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
-	
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-
-	free(opts);
-	
 	/* Allocate libext2 filesystem structure */
 	fs = (ext2_filesystem_t *) malloc(sizeof(ext2_filesystem_t));
-	if (fs == NULL) {
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
+	if (fs == NULL)
+		return ENOMEM;
 	
 	/* Allocate instance structure */
@@ -627,6 +606,5 @@
 	if (inst == NULL) {
 		free(fs);
-		async_answer_0(rid, ENOMEM);
-		return;
+		return ENOMEM;
 	}
 	
@@ -636,6 +614,5 @@
 		free(fs);
 		free(inst);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -646,6 +623,5 @@
 		free(fs);
 		free(inst);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -656,6 +632,5 @@
 		free(fs);
 		free(inst);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -673,6 +648,5 @@
 		free(fs);
 		free(inst);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	ext2fs_node_t *enode = EXT2FS_NODE(root_node);
@@ -683,22 +657,16 @@
 	fibril_mutex_unlock(&instance_list_mutex);
 	
-	async_answer_3(rid, EOK,
-	    EXT2_INODE_ROOT_INDEX,
-	    0,
-	    ext2_inode_get_usage_count(enode->inode_ref->inode));
+	*index = EXT2_INODE_ROOT_INDEX;
+	*size = 0;
+	*lnkcnt = ext2_inode_get_usage_count(enode->inode_ref->inode);
 	
 	ext2fs_node_put(root_node);
-}
-
-void ext2fs_mount(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	libfs_mount(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
-}
-
-void ext2fs_unmounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+
+	return EOK;
+}
+
+static int ext2fs_unmounted(devmap_handle_t devmap_handle)
+{
+	EXT2FS_DBG("");
 	ext2fs_instance_t *inst;
 	int rc;
@@ -706,8 +674,6 @@
 	rc = ext2fs_instance_get(devmap_handle, &inst);
 	
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
 	
 	fibril_mutex_lock(&open_nodes_lock);
@@ -716,6 +682,5 @@
 	if (inst->open_nodes_count != 0) {
 		fibril_mutex_unlock(&open_nodes_lock);
-		async_answer_0(rid, EBUSY);
-		return;
+		return EBUSY;
 	}
 	
@@ -729,26 +694,12 @@
 	ext2_filesystem_fini(inst->filesystem);
 	
-	async_answer_0(rid, EOK);
-}
-
-void ext2fs_unmount(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	libfs_unmount(&ext2fs_libfs_ops, rid, request);
-}
-
-void ext2fs_lookup(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	libfs_lookup(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
-}
-
-void ext2fs_read(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
+	return EOK;
+}
+
+static int
+ext2fs_read(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
+	EXT2FS_DBG("");
 	
 	ext2fs_instance_t *inst;
@@ -763,6 +714,5 @@
 	if (!async_data_read_receive(&callid, &size)) {
 		async_answer_0(callid, EINVAL);
-		async_answer_0(rid, EINVAL);
-		return;
+		return EINVAL;
 	}
 	
@@ -770,6 +720,5 @@
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -777,24 +726,24 @@
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
 	if (ext2_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
-		    EXT2_INODE_MODE_FILE)) {
-		ext2fs_read_file(rid, callid, pos, size, inst, inode_ref);
-	}
-	else if (ext2_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
-		    EXT2_INODE_MODE_DIRECTORY)) {
-		ext2fs_read_directory(rid, callid, pos, size, inst, inode_ref);
-	}
-	else {
+	    EXT2_INODE_MODE_FILE)) {
+		rc = ext2fs_read_file(callid, pos, size, inst, inode_ref,
+		    rbytes);
+	} else if (ext2_inode_is_type(inst->filesystem->superblock,
+	    inode_ref->inode, EXT2_INODE_MODE_DIRECTORY)) {
+		rc = ext2fs_read_directory(callid, pos, size, inst, inode_ref,
+		    rbytes);
+	} else {
 		/* Other inode types not supported */
 		async_answer_0(callid, ENOTSUP);
-		async_answer_0(rid, ENOTSUP);
+		rc = ENOTSUP;
 	}
 	
 	ext2_filesystem_put_inode_ref(inode_ref);
 	
+	return rc;
 }
 
@@ -814,6 +763,6 @@
 }
 
-void ext2fs_read_directory(ipc_callid_t rid, ipc_callid_t callid, aoff64_t pos,
-	size_t size, ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref)
+int ext2fs_read_directory(ipc_callid_t callid, aoff64_t pos, size_t size,
+    ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref, size_t *rbytes)
 {
 	ext2_directory_iterator_t it;
@@ -827,6 +776,5 @@
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -841,5 +789,5 @@
 		
 		name_size = ext2_directory_entry_ll_get_name_length(
-			inst->filesystem->superblock, it.current);
+		    inst->filesystem->superblock, it.current);
 		
 		/* skip . and .. */
@@ -849,18 +797,17 @@
 		
 		/* 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
-			*/
+		 * end of entry name, so we copy it to new buffer
+		 * and add the \0 at the end
+		 */
 		buf = malloc(name_size+1);
 		if (buf == NULL) {
 			ext2_directory_iterator_fini(&it);
 			async_answer_0(callid, ENOMEM);
-			async_answer_0(rid, ENOMEM);
-			return;
+			return ENOMEM;
 		}
 		memcpy(buf, &it.current->name, name_size);
-		*(buf+name_size) = 0;
+		*(buf + name_size) = 0;
 		found = true;
-		(void) async_data_read_finalize(callid, buf, name_size+1);
+		(void) async_data_read_finalize(callid, buf, name_size + 1);
 		free(buf);
 		break;
@@ -871,6 +818,5 @@
 			ext2_directory_iterator_fini(&it);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 	}
@@ -878,28 +824,24 @@
 	if (found) {
 		rc = ext2_directory_iterator_next(&it);
-		if (rc != EOK) {
-			async_answer_0(rid, rc);
-			return;
-		}
+		if (rc != EOK)
+			return rc;
 		next = it.current_offset;
 	}
 	
 	rc = ext2_directory_iterator_fini(&it);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
 	
 	if (found) {
-		async_answer_1(rid, EOK, next-pos);
-	}
-	else {
+		*rbytes = next - pos;
+		return EOK;
+	} else {
 		async_answer_0(callid, ENOENT);
-		async_answer_0(rid, ENOENT);
-	}
-}
-
-void ext2fs_read_file(ipc_callid_t rid, ipc_callid_t callid, aoff64_t pos,
-	size_t size, ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref)
+		return ENOENT;
+	}
+}
+
+int ext2fs_read_file(ipc_callid_t callid, aoff64_t pos, size_t size,
+    ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref, size_t *rbytes)
 {
 	int rc;
@@ -919,6 +861,6 @@
 		/* Read 0 bytes successfully */
 		async_data_read_finalize(callid, NULL, 0);
-		async_answer_1(rid, EOK, 0);
-		return;
+		*rbytes = 0;
+		return EOK;
 	}
 	
@@ -939,6 +881,5 @@
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -952,6 +893,5 @@
 		if (buffer == NULL) {
 			async_answer_0(callid, ENOMEM);
-			async_answer_0(rid, ENOMEM);
-			return;
+			return ENOMEM;
 		}
 		
@@ -959,9 +899,9 @@
 		
 		async_data_read_finalize(callid, buffer, bytes);
-		async_answer_1(rid, EOK, bytes);
+		*rbytes = bytes;
 		
 		free(buffer);
 		
-		return;
+		return EOK;
 	}
 	
@@ -970,6 +910,5 @@
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 	
@@ -978,75 +917,57 @@
 	
 	rc = block_put(block);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-		
-	async_answer_1(rid, EOK, bytes);
-}
-
-void ext2fs_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-//	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-//	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-//	aoff64_t pos =
-//	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
-	// TODO
-	async_answer_0(rid, ENOTSUP);
-}
-
-void ext2fs_truncate(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-//	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-//	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-//	aoff64_t size =
-//	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
-	// TODO
-	async_answer_0(rid, ENOTSUP);
-}
-
-void ext2fs_close(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	async_answer_0(rid, EOK);
-}
-
-void ext2fs_destroy(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-//	devmap_handle_t devmap_handle = (devmap_handle_t)IPC_GET_ARG1(*request);
-//	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
-	
-	// TODO
-	async_answer_0(rid, ENOTSUP);
-}
-
-void ext2fs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	libfs_open_node(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
-}
-
-void ext2fs_stat(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-	libfs_stat(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
-}
-
-void ext2fs_sync(ipc_callid_t rid, ipc_call_t *request)
-{
-	EXT2FS_DBG("");
-//	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-//	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	
-	// TODO
-	async_answer_0(rid, ENOTSUP);
-}
+	if (rc != EOK)
+		return rc;
+	
+	*rbytes = bytes;
+	return EOK;
+}
+
+static int
+ext2fs_write(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
+	EXT2FS_DBG("");
+	return ENOTSUP;
+}
+
+static int
+ext2fs_truncate(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t size)
+{
+	EXT2FS_DBG("");
+	return ENOTSUP;
+}
+
+static int ext2fs_close(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	EXT2FS_DBG("");
+	return EOK;
+}
+
+static int ext2fs_destroy(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	EXT2FS_DBG("");
+	return ENOTSUP;
+}
+
+static int ext2fs_sync(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	EXT2FS_DBG("");
+	return ENOTSUP;
+}
+
+vfs_out_ops_t ext2fs_ops = {
+	.mounted = ext2fs_mounted,
+	.unmounted = ext2fs_unmounted,
+	.read = ext2fs_read,
+	.write = ext2fs_write,
+	.truncate = ext2fs_truncate,
+	.close = ext2fs_close,
+	.destroy = ext2fs_destroy,
+	.sync = ext2fs_sync,
+};
 
 /**
  * @}
  */
+
Index: uspace/srv/fs/fat/fat.c
===================================================================
--- uspace/srv/fs/fat/fat.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/fat/fat.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -56,91 +56,4 @@
 };
 
-fs_reg_t fat_reg;
-
-/**
- * This connection fibril processes VFS requests from VFS.
- *
- * In order to support simultaneous VFS requests, our design is as follows.
- * The connection fibril accepts VFS requests from VFS. If there is only one
- * instance of the fibril, VFS will need to serialize all VFS requests it sends
- * to FAT. To overcome this bottleneck, VFS can send FAT the IPC_M_CONNECT_ME_TO
- * call. In that case, a new connection fibril will be created, which in turn
- * will accept the call. Thus, a new phone will be opened for VFS.
- *
- * There are few issues with this arrangement. First, VFS can run out of
- * available phones. In that case, VFS can close some other phones or use one
- * phone for more serialized requests. Similarily, FAT can refuse to duplicate
- * the connection. VFS should then just make use of already existing phones and
- * route its requests through them. To avoid paying the fibril creation price 
- * upon each request, FAT might want to keep the connections open after the
- * request has been completed.
- */
-static void fat_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	if (iid) {
-		/*
-		 * This only happens for connections opened by
-		 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
-		 * created by IPC_M_CONNECT_TO_ME.
-		 */
-		async_answer_0(iid, EOK);
-	}
-	
-	dprintf(NAME ": connection opened\n");
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_MOUNTED:
-			fat_mounted(callid, &call);
-			break;
-		case VFS_OUT_MOUNT:
-			fat_mount(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNTED:
-			fat_unmounted(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNT:
-			fat_unmount(callid, &call);
-			break;
-		case VFS_OUT_LOOKUP:
-			fat_lookup(callid, &call);
-			break;
-		case VFS_OUT_READ:
-			fat_read(callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			fat_write(callid, &call);
-			break;
-		case VFS_OUT_TRUNCATE:
-			fat_truncate(callid, &call);
-			break;
-		case VFS_OUT_STAT:
-			fat_stat(callid, &call);
-			break;
-		case VFS_OUT_CLOSE:
-			fat_close(callid, &call);
-			break;
-		case VFS_OUT_DESTROY:
-			fat_destroy(callid, &call);
-			break;
-		case VFS_OUT_OPEN_NODE:
-			fat_open_node(callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			fat_sync(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		}
-	}
-}
-
 int main(int argc, char **argv)
 {
@@ -158,5 +71,5 @@
 	}
 	
-	rc = fs_register(vfs_sess, &fat_reg, &fat_vfs_info, fat_connection);
+	rc = fs_register(vfs_sess, &fat_vfs_info, &fat_ops, &fat_libfs_ops);
 	if (rc != EOK) {
 		fat_idx_fini();
Index: uspace/srv/fs/fat/fat.h
===================================================================
--- uspace/srv/fs/fat/fat.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/fat/fat.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -224,20 +224,6 @@
 } fat_node_t;
 
-extern fs_reg_t fat_reg;
-
-extern void fat_mounted(ipc_callid_t, ipc_call_t *);
-extern void fat_mount(ipc_callid_t, ipc_call_t *);
-extern void fat_unmounted(ipc_callid_t, ipc_call_t *);
-extern void fat_unmount(ipc_callid_t, ipc_call_t *);
-extern void fat_lookup(ipc_callid_t, ipc_call_t *);
-extern void fat_read(ipc_callid_t, ipc_call_t *);
-extern void fat_write(ipc_callid_t, ipc_call_t *);
-extern void fat_truncate(ipc_callid_t, ipc_call_t *);
-extern void fat_stat(ipc_callid_t, ipc_call_t *);
-extern void fat_close(ipc_callid_t, ipc_call_t *);
-extern void fat_destroy(ipc_callid_t, ipc_call_t *);
-extern void fat_open_node(ipc_callid_t, ipc_call_t *);
-extern void fat_stat(ipc_callid_t, ipc_call_t *);
-extern void fat_sync(ipc_callid_t, ipc_call_t *);
+extern vfs_out_ops_t fat_ops;
+extern libfs_ops_t fat_libfs_ops;
 
 extern int fat_idx_get_new(fat_idx_t **, devmap_handle_t);
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -85,5 +85,4 @@
 static aoff64_t fat_size_get(fs_node_t *);
 static unsigned fat_lnkcnt_get(fs_node_t *);
-static char fat_plb_get_char(unsigned);
 static bool fat_is_directory(fs_node_t *);
 static bool fat_is_file(fs_node_t *node);
@@ -901,9 +900,4 @@
 }
 
-char fat_plb_get_char(unsigned pos)
-{
-	return fat_reg.plb_ro[pos % PLB_SIZE];
-}
-
 bool fat_is_directory(fs_node_t *fn)
 {
@@ -936,5 +930,4 @@
 	.size_get = fat_size_get,
 	.lnkcnt_get = fat_lnkcnt_get,
-	.plb_get_char = fat_plb_get_char,
 	.is_directory = fat_is_directory,
 	.is_file = fat_is_file,
@@ -943,22 +936,15 @@
 
 /*
- * VFS operations.
+ * FAT VFS_OUT operations.
  */
 
-void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+static int
+fat_mounted(devmap_handle_t devmap_handle, const char *opts, fs_index_t *index,
+    aoff64_t *size, unsigned *linkcnt)
+{
 	enum cache_mode cmode;
 	fat_bs_t *bs;
-	
-	/* Accept the mount options */
-	char *opts;
-	int rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
-	
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-
+	int rc;
+	
 	/* Check for option enabling write through. */
 	if (str_cmp(opts, "wtcache") == 0)
@@ -967,12 +953,8 @@
 		cmode = CACHE_MODE_WB;
 
-	free(opts);
-
 	/* initialize libblock */
 	rc = block_init(EXCHANGE_SERIALIZE, devmap_handle, BS_SIZE);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
 
 	/* prepare the boot block */
@@ -980,6 +962,5 @@
 	if (rc != EOK) {
 		block_fini(devmap_handle);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 
@@ -989,6 +970,5 @@
 	if (BPS(bs) != BS_SIZE) {
 		block_fini(devmap_handle);
-		async_answer_0(rid, ENOTSUP);
-		return;
+		return ENOTSUP;
 	}
 
@@ -997,6 +977,5 @@
 	if (rc != EOK) {
 		block_fini(devmap_handle);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 
@@ -1006,6 +985,5 @@
 		(void) block_cache_fini(devmap_handle);
 		block_fini(devmap_handle);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 
@@ -1014,6 +992,5 @@
 		(void) block_cache_fini(devmap_handle);
 		block_fini(devmap_handle);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 	}
 
@@ -1024,6 +1001,5 @@
 		block_fini(devmap_handle);
 		fat_idx_fini_by_devmap_handle(devmap_handle);
-		async_answer_0(rid, ENOMEM);
-		return;
+		return ENOMEM;
 	}
 	fs_node_initialize(rfn);
@@ -1034,6 +1010,5 @@
 		block_fini(devmap_handle);
 		fat_idx_fini_by_devmap_handle(devmap_handle);
-		async_answer_0(rid, ENOMEM);
-		return;
+		return ENOMEM;
 	}
 	fat_node_initialize(rootp);
@@ -1046,6 +1021,5 @@
 		block_fini(devmap_handle);
 		fat_idx_fini_by_devmap_handle(devmap_handle);
-		async_answer_0(rid, ENOMEM);
-		return;
+		return ENOMEM;
 	}
 	assert(ridxp->index == 0);
@@ -1064,15 +1038,13 @@
 	fibril_mutex_unlock(&ridxp->lock);
 
-	async_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
-}
-
-void fat_mount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_mount(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
-}
-
-void fat_unmounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	*index = ridxp->index;
+	*size = rootp->size;
+	*linkcnt = rootp->lnkcnt;
+
+	return EOK;
+}
+
+static int fat_unmounted(devmap_handle_t devmap_handle)
+{
 	fs_node_t *fn;
 	fat_node_t *nodep;
@@ -1080,8 +1052,6 @@
 
 	rc = fat_root_get(&fn, devmap_handle);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
 	nodep = FAT_NODE(fn);
 
@@ -1092,6 +1062,5 @@
 	if (nodep->refcnt != 2) {
 		(void) fat_node_put(fn);
-		async_answer_0(rid, EBUSY);
-		return;
+		return EBUSY;
 	}
 	
@@ -1112,23 +1081,11 @@
 	block_fini(devmap_handle);
 
-	async_answer_0(rid, EOK);
-}
-
-void fat_unmount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_unmount(&fat_libfs_ops, rid, request);
-}
-
-void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_lookup(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
-}
-
-void fat_read(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
+	return EOK;
+}
+
+static int
+fat_read(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
 	fs_node_t *fn;
 	fat_node_t *nodep;
@@ -1139,12 +1096,8 @@
 
 	rc = fat_node_get(&fn, devmap_handle, index);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	if (!fn) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
 	nodep = FAT_NODE(fn);
 
@@ -1154,6 +1107,5 @@
 		fat_node_put(fn);
 		async_answer_0(callid, EINVAL);
-		async_answer_0(rid, EINVAL);
-		return;
+		return EINVAL;
 	}
 
@@ -1178,6 +1130,5 @@
 				fat_node_put(fn);
 				async_answer_0(callid, rc);
-				async_answer_0(rid, rc);
-				return;
+				return rc;
 			}
 			(void) async_data_read_finalize(callid,
@@ -1186,6 +1137,5 @@
 			if (rc != EOK) {
 				fat_node_put(fn);
-				async_answer_0(rid, rc);
-				return;
+				return rc;
 			}
 		}
@@ -1244,12 +1194,11 @@
 		rc = fat_node_put(fn);
 		async_answer_0(callid, rc != EOK ? rc : ENOENT);
-		async_answer_1(rid, rc != EOK ? rc : ENOENT, 0);
-		return;
+		*rbytes = 0;
+		return rc != EOK ? rc : ENOENT;
 
 err:
 		(void) fat_node_put(fn);
 		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
+		return rc;
 
 hit:
@@ -1259,17 +1208,16 @@
 
 	rc = fat_node_put(fn);
-	async_answer_1(rid, rc, (sysarg_t)bytes);
-}
-
-void fat_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
+	*rbytes = bytes;
+	return rc;
+}
+
+static int
+fat_write(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
 	fs_node_t *fn;
 	fat_node_t *nodep;
 	fat_bs_t *bs;
-	size_t bytes, size;
+	size_t bytes;
 	block_t *b;
 	aoff64_t boundary;
@@ -1278,12 +1226,8 @@
 	
 	rc = fat_node_get(&fn, devmap_handle, index);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	if (!fn) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
 	nodep = FAT_NODE(fn);
 	
@@ -1293,6 +1237,5 @@
 		(void) fat_node_put(fn);
 		async_answer_0(callid, EINVAL);
-		async_answer_0(rid, EINVAL);
-		return;
+		return EINVAL;
 	}
 
@@ -1322,6 +1265,5 @@
 			(void) fat_node_put(fn);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		rc = fat_block_get(&b, bs, nodep, pos / BPS(bs), flags);
@@ -1329,6 +1271,5 @@
 			(void) fat_node_put(fn);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		(void) async_data_write_finalize(callid,
@@ -1338,6 +1279,5 @@
 		if (rc != EOK) {
 			(void) fat_node_put(fn);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		if (pos + bytes > nodep->size) {
@@ -1345,8 +1285,8 @@
 			nodep->dirty = true;	/* need to sync node */
 		}
-		size = nodep->size;
+		*wbytes = bytes;
+		*nsize = nodep->size;
 		rc = fat_node_put(fn);
-		async_answer_2(rid, rc, bytes, nodep->size);
-		return;
+		return rc;
 	} else {
 		/*
@@ -1364,6 +1304,5 @@
 			(void) fat_node_put(fn);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		/* zero fill any gaps */
@@ -1373,6 +1312,5 @@
 			(void) fat_node_put(fn);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		rc = _fat_block_get(&b, bs, devmap_handle, lcl, NULL,
@@ -1382,6 +1320,5 @@
 			(void) fat_node_put(fn);
 			async_answer_0(callid, rc);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		(void) async_data_write_finalize(callid,
@@ -1392,6 +1329,5 @@
 			(void) fat_free_clusters(bs, devmap_handle, mcl);
 			(void) fat_node_put(fn);
-			async_answer_0(rid, rc);
-			return;
+			return rc;
 		}
 		/*
@@ -1403,21 +1339,17 @@
 			(void) fat_free_clusters(bs, devmap_handle, mcl);
 			(void) fat_node_put(fn);
-			async_answer_0(rid, rc);
-			return;
-		}
-		nodep->size = size = pos + bytes;
+			return rc;
+		}
+		*nsize = nodep->size = pos + bytes;
+		rc = fat_node_put(fn);
 		nodep->dirty = true;		/* need to sync node */
-		rc = fat_node_put(fn);
-		async_answer_2(rid, rc, bytes, size);
-		return;
-	}
-}
-
-void fat_truncate(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t size =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
+		*wbytes = bytes;
+		return rc;
+	}
+}
+
+static int
+fat_truncate(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t size)
+{
 	fs_node_t *fn;
 	fat_node_t *nodep;
@@ -1426,12 +1358,8 @@
 
 	rc = fat_node_get(&fn, devmap_handle, index);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	if (!fn) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
 	nodep = FAT_NODE(fn);
 
@@ -1477,17 +1405,14 @@
 out:
 	fat_node_put(fn);
-	async_answer_0(rid, rc);
-	return;
-}
-
-void fat_close(ipc_callid_t rid, ipc_call_t *request)
-{
-	async_answer_0(rid, EOK);
-}
-
-void fat_destroy(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t)IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
+	return rc;
+}
+
+static int fat_close(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	return EOK;
+}
+
+static int fat_destroy(devmap_handle_t devmap_handle, fs_index_t index)
+{
 	fs_node_t *fn;
 	fat_node_t *nodep;
@@ -1495,12 +1420,8 @@
 
 	rc = fat_node_get(&fn, devmap_handle, index);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	if (!fn) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
 
 	nodep = FAT_NODE(fn);
@@ -1512,32 +1433,15 @@
 
 	rc = fat_destroy_node(fn);
-	async_answer_0(rid, rc);
-}
-
-void fat_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_open_node(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
-}
-
-void fat_stat(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_stat(&fat_libfs_ops, fat_reg.fs_handle, rid, request);
-}
-
-void fat_sync(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	
+	return rc;
+}
+
+static int fat_sync(devmap_handle_t devmap_handle, fs_index_t index)
+{
 	fs_node_t *fn;
 	int rc = fat_node_get(&fn, devmap_handle, index);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	if (!fn) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
 	
 	fat_node_t *nodep = FAT_NODE(fn);
@@ -1547,6 +1451,17 @@
 	
 	fat_node_put(fn);
-	async_answer_0(rid, rc);
-}
+	return rc;
+}
+
+vfs_out_ops_t fat_ops = {
+	.mounted = fat_mounted,
+	.unmounted = fat_unmounted,
+	.read = fat_read,
+	.write = fat_write,
+	.truncate = fat_truncate,
+	.close = fat_close,
+	.destroy = fat_destroy,
+	.sync = fat_sync,
+};
 
 /**
Index: uspace/srv/fs/tmpfs/tmpfs.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/tmpfs/tmpfs.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -61,92 +61,4 @@
 };
 
-fs_reg_t tmpfs_reg;
-
-/**
- * This connection fibril processes VFS requests from VFS.
- *
- * In order to support simultaneous VFS requests, our design is as follows.
- * The connection fibril accepts VFS requests from VFS. If there is only one
- * instance of the fibril, VFS will need to serialize all VFS requests it sends
- * to FAT. To overcome this bottleneck, VFS can send TMPFS the
- * IPC_M_CONNECT_ME_TO call. In that case, a new connection fibril will be
- * created, which in turn will accept the call. Thus, a new phone will be
- * opened for VFS.
- *
- * There are few issues with this arrangement. First, VFS can run out of
- * available phones. In that case, VFS can close some other phones or use one
- * phone for more serialized requests. Similarily, TMPFS can refuse to duplicate
- * the connection. VFS should then just make use of already existing phones and
- * route its requests through them. To avoid paying the fibril creation price 
- * upon each request, TMPFS might want to keep the connections open after the
- * request has been completed.
- */
-static void tmpfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	if (iid) {
-		/*
-		 * This only happens for connections opened by
-		 * IPC_M_CONNECT_ME_TO calls as opposed to callback connections
-		 * created by IPC_M_CONNECT_TO_ME.
-		 */
-		async_answer_0(iid, EOK);
-	}
-	
-	dprintf(NAME ": connection opened\n");
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_MOUNTED:
-			tmpfs_mounted(callid, &call);
-			break;
-		case VFS_OUT_MOUNT:
-			tmpfs_mount(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNTED:
-			tmpfs_unmounted(callid, &call);
-			break;
-		case VFS_OUT_UNMOUNT:
-			tmpfs_unmount(callid, &call);
-			break;
-		case VFS_OUT_LOOKUP:
-			tmpfs_lookup(callid, &call);
-			break;
-		case VFS_OUT_READ:
-			tmpfs_read(callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			tmpfs_write(callid, &call);
-			break;
-		case VFS_OUT_TRUNCATE:
-			tmpfs_truncate(callid, &call);
-			break;
-		case VFS_OUT_CLOSE:
-			tmpfs_close(callid, &call);
-			break;
-		case VFS_OUT_DESTROY:
-			tmpfs_destroy(callid, &call);
-			break;
-		case VFS_OUT_OPEN_NODE:
-			tmpfs_open_node(callid, &call);
-			break;
-		case VFS_OUT_STAT:
-			tmpfs_stat(callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			tmpfs_sync(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		}
-	}
-}
-
 int main(int argc, char **argv)
 {
@@ -165,6 +77,6 @@
 	}
 	
-	int rc = fs_register(vfs_sess, &tmpfs_reg, &tmpfs_vfs_info,
-	    tmpfs_connection);
+	int rc = fs_register(vfs_sess, &tmpfs_vfs_info, &tmpfs_ops,
+	    &tmpfs_libfs_ops);
 	if (rc != EOK) {
 		printf(NAME ": Failed to register file system (%d)\n", rc);
Index: uspace/srv/fs/tmpfs/tmpfs.h
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs.h	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/tmpfs/tmpfs.h	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -70,24 +70,8 @@
 } tmpfs_node_t;
 
-extern fs_reg_t tmpfs_reg;
-
+extern vfs_out_ops_t tmpfs_ops;
 extern libfs_ops_t tmpfs_libfs_ops;
 
 extern bool tmpfs_init(void);
-
-extern void tmpfs_mounted(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_mount(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_unmounted(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_unmount(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_lookup(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_read(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_write(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_truncate(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_stat(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_close(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_destroy(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_open_node(ipc_callid_t, ipc_call_t *);
-extern void tmpfs_sync(ipc_callid_t, ipc_call_t *);
-
 extern bool tmpfs_restore(devmap_handle_t);
 
Index: uspace/srv/fs/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -104,9 +104,4 @@
 }
 
-static char tmpfs_plb_get_char(unsigned pos)
-{
-	return tmpfs_reg.plb_ro[pos % PLB_SIZE];
-}
-
 static bool tmpfs_is_directory(fs_node_t *fn)
 {
@@ -139,5 +134,4 @@
 	.size_get = tmpfs_size_get,
 	.lnkcnt_get = tmpfs_lnkcnt_get,
-	.plb_get_char = tmpfs_plb_get_char,
 	.is_directory = tmpfs_is_directory,
 	.is_file = tmpfs_is_file,
@@ -433,33 +427,25 @@
 }
 
-void tmpfs_mounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+/*
+ * Implementation of the VFS_OUT interface.
+ */
+
+static int
+tmpfs_mounted(devmap_handle_t devmap_handle, const char *opts,
+    fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
+{
 	fs_node_t *rootfn;
 	int rc;
 	
-	/* Accept the mount options. */
-	char *opts;
-	rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-
 	/* Check if this device is not already mounted. */
 	rc = tmpfs_root_get(&rootfn, devmap_handle);
 	if ((rc == EOK) && (rootfn)) {
 		(void) tmpfs_node_put(rootfn);
-		free(opts);
-		async_answer_0(rid, EEXIST);
-		return;
+		return EEXIST;
 	}
 
 	/* Initialize TMPFS instance. */
-	if (!tmpfs_instance_init(devmap_handle)) {
-		free(opts);
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
+	if (!tmpfs_instance_init(devmap_handle))
+		return ENOMEM;
 
 	rc = tmpfs_root_get(&rootfn, devmap_handle);
@@ -467,46 +453,24 @@
 	tmpfs_node_t *rootp = TMPFS_NODE(rootfn);
 	if (str_cmp(opts, "restore") == 0) {
-		if (tmpfs_restore(devmap_handle))
-			async_answer_3(rid, EOK, rootp->index, rootp->size,
-			    rootp->lnkcnt);
-		else
-			async_answer_0(rid, ELIMIT);
-	} else {
-		async_answer_3(rid, EOK, rootp->index, rootp->size,
-		    rootp->lnkcnt);
-	}
-	free(opts);
-}
-
-void tmpfs_mount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_mount(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
-}
-
-void tmpfs_unmounted(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-
+		if (!tmpfs_restore(devmap_handle))
+			return ELIMIT;
+	}
+
+	*index = rootp->index;
+	*size = rootp->size;
+	*lnkcnt = rootp->lnkcnt;
+
+	return EOK;
+}
+
+static int tmpfs_unmounted(devmap_handle_t devmap_handle)
+{
 	tmpfs_instance_done(devmap_handle);
-	async_answer_0(rid, EOK);
-}
-
-void tmpfs_unmount(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_unmount(&tmpfs_libfs_ops, rid, request);
-}
-
-void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_lookup(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
-}
-
-void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
+	return EOK;
+}
+
+static int tmpfs_read(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
 	/*
 	 * Lookup the respective TMPFS node.
@@ -518,8 +482,6 @@
 	};
 	hlp = hash_table_find(&nodes, key);
-	if (!hlp) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (!hlp)
+		return ENOENT;
 	tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
 	    nh_link);
@@ -532,6 +494,5 @@
 	if (!async_data_read_receive(&callid, &size)) {
 		async_answer_0(callid, EINVAL);
-		async_answer_0(rid, EINVAL);
-		return;
+		return EINVAL;
 	}
 
@@ -556,6 +517,5 @@
 		if (lnk == NULL) {
 			async_answer_0(callid, ENOENT);
-			async_answer_1(rid, ENOENT, 0);
-			return;
+			return ENOENT;
 		}
 
@@ -567,17 +527,12 @@
 	}
 
-	/*
-	 * Answer the VFS_READ call.
-	 */
-	async_answer_1(rid, EOK, bytes);
-}
-
-void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t pos =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
+	*rbytes = bytes;
+	return EOK;
+}
+
+static int
+tmpfs_write(devmap_handle_t devmap_handle, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
 	/*
 	 * Lookup the respective TMPFS node.
@@ -589,8 +544,6 @@
 	};
 	hlp = hash_table_find(&nodes, key);
-	if (!hlp) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (!hlp)
+		return ENOENT;
 	tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
 	    nh_link);
@@ -603,6 +556,5 @@
 	if (!async_data_write_receive(&callid, &size)) {
 		async_answer_0(callid, EINVAL);	
-		async_answer_0(rid, EINVAL);
-		return;
+		return EINVAL;
 	}
 
@@ -612,7 +564,7 @@
 	if (pos + size <= nodep->size) {
 		/* The file size is not changing. */
-		(void) async_data_write_finalize(callid, nodep->data + pos, size);
-		async_answer_2(rid, EOK, size, nodep->size);
-		return;
+		(void) async_data_write_finalize(callid, nodep->data + pos,
+		    size);
+		goto out;
 	}
 	size_t delta = (pos + size) - nodep->size;
@@ -627,6 +579,6 @@
 	if (!newdata) {
 		async_answer_0(callid, ENOMEM);
-		async_answer_2(rid, EOK, 0, nodep->size);
-		return;
+		size = 0;
+		goto out;
 	}
 	/* Clear any newly allocated memory in order to emulate gaps. */
@@ -635,14 +587,14 @@
 	nodep->data = newdata;
 	(void) async_data_write_finalize(callid, nodep->data + pos, size);
-	async_answer_2(rid, EOK, size, nodep->size);
-}
-
-void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	aoff64_t size =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
-	
+
+out:
+	*wbytes = size;
+	*nsize = nodep->size;
+	return EOK;
+}
+
+static int tmpfs_truncate(devmap_handle_t devmap_handle, fs_index_t index,
+    aoff64_t size)
+{
 	/*
 	 * Lookup the respective TMPFS node.
@@ -653,26 +605,17 @@
 	};
 	link_t *hlp = hash_table_find(&nodes, key);
-	if (!hlp) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
-	tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
-	    nh_link);
-	
-	if (size == nodep->size) {
-		async_answer_0(rid, EOK);
-		return;
-	}
-	
-	if (size > SIZE_MAX) {
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
+	if (!hlp)
+		return ENOENT;
+	tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t, nh_link);
+	
+	if (size == nodep->size)
+		return EOK;
+	
+	if (size > SIZE_MAX)
+		return ENOMEM;
 	
 	void *newdata = realloc(nodep->data, size);
-	if (!newdata) {
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
+	if (!newdata)
+		return ENOMEM;
 	
 	if (size > nodep->size) {
@@ -683,18 +626,14 @@
 	nodep->size = size;
 	nodep->data = newdata;
-	async_answer_0(rid, EOK);
-}
-
-void tmpfs_close(ipc_callid_t rid, ipc_call_t *request)
-{
-	async_answer_0(rid, EOK);
-}
-
-void tmpfs_destroy(ipc_callid_t rid, ipc_call_t *request)
-{
-	devmap_handle_t devmap_handle = (devmap_handle_t)IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
-	int rc;
-
+	return EOK;
+}
+
+static int tmpfs_close(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	return EOK;
+}
+
+static int tmpfs_destroy(devmap_handle_t devmap_handle, fs_index_t index)
+{
 	link_t *hlp;
 	unsigned long key[] = {
@@ -703,25 +642,12 @@
 	};
 	hlp = hash_table_find(&nodes, key);
-	if (!hlp) {
-		async_answer_0(rid, ENOENT);
-		return;
-	}
+	if (!hlp)
+		return ENOENT;
 	tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
 	    nh_link);
-	rc = tmpfs_destroy_node(FS_NODE(nodep));
-	async_answer_0(rid, rc);
-}
-
-void tmpfs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_open_node(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
-}
-
-void tmpfs_stat(ipc_callid_t rid, ipc_call_t *request)
-{
-	libfs_stat(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
-}
-
-void tmpfs_sync(ipc_callid_t rid, ipc_call_t *request)
+	return tmpfs_destroy_node(FS_NODE(nodep));
+}
+
+static int tmpfs_sync(devmap_handle_t devmap_handle, fs_index_t index)
 {
 	/*
@@ -729,8 +655,20 @@
 	 * thus the sync operation is a no-op.
 	 */
-	async_answer_0(rid, EOK);
-}
+	return EOK;
+}
+
+vfs_out_ops_t tmpfs_ops = {
+	.mounted = tmpfs_mounted,
+	.unmounted = tmpfs_unmounted,
+	.read = tmpfs_read,
+	.write = tmpfs_write,
+	.truncate = tmpfs_truncate,
+	.close = tmpfs_close,
+	.destroy = tmpfs_destroy,
+	.sync = tmpfs_sync,
+};
 
 /**
  * @}
  */
+
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision c936c7f9ded988d13c3729f1394182797ffa8d7e)
+++ uspace/srv/vfs/vfs_ops.c	(revision 80e9e5edd009650fe1b6323206b0bb22db7900cc)
@@ -76,5 +76,5 @@
 	vfs_node_t *mr_node;
 	fs_index_t rindex;
-	size_t rsize;
+	aoff64_t rsize;
 	unsigned rlnkcnt;
 	async_exch_t *exch;
@@ -146,6 +146,6 @@
 
 			rindex = (fs_index_t) IPC_GET_ARG1(answer);
-			rsize = (size_t) IPC_GET_ARG2(answer);
-			rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
+			rsize = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG2(answer), IPC_GET_ARG3(answer));
+			rlnkcnt = (unsigned) IPC_GET_ARG4(answer);
 			
 			mr_res.triplet.fs_handle = fs_handle;
@@ -229,6 +229,7 @@
 	if (rc == EOK) {
 		rindex = (fs_index_t) IPC_GET_ARG1(answer);
-		rsize = (size_t) IPC_GET_ARG2(answer);
-		rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
+		rsize = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG2(answer),
+		    IPC_GET_ARG3(answer));
+		rlnkcnt = (unsigned) IPC_GET_ARG4(answer);
 		
 		mr_res.triplet.fs_handle = fs_handle;
@@ -795,14 +796,14 @@
 	ipc_call_t answer;
 	if (read) {
-		rc = async_data_read_forward_3_1(fs_exch, VFS_OUT_READ,
-		    file->node->devmap_handle, file->node->index, file->pos,
-		    &answer);
+		rc = async_data_read_forward_4_1(fs_exch, VFS_OUT_READ,
+		    file->node->devmap_handle, file->node->index,
+		    LOWER32(file->pos), UPPER32(file->pos), &answer);
 	} else {
 		if (file->append)
 			file->pos = file->node->size;
 		
-		rc = async_data_write_forward_3_1(fs_exch, VFS_OUT_WRITE,
-		    file->node->devmap_handle, file->node->index, file->pos,
-		    &answer);
+		rc = async_data_write_forward_4_1(fs_exch, VFS_OUT_WRITE,
+		    file->node->devmap_handle, file->node->index,
+		    LOWER32(file->pos), UPPER32(file->pos), &answer);
 	}
 	
@@ -821,5 +822,6 @@
 		/* Update the cached version of node's size. */
 		if (rc == EOK)
-			file->node->size = IPC_GET_ARG2(answer); 
+			file->node->size = MERGE_LOUP32(IPC_GET_ARG2(answer),
+			    IPC_GET_ARG3(answer));
 		fibril_rwlock_write_unlock(&file->node->contents_rwlock);
 	}
