Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision a4b92fb02227d80cddeca1fc5b42d4cacb2f93ef)
+++ boot/Makefile.common	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -97,4 +97,5 @@
 	$(USPACE_PATH)/srv/fs/tmpfs/tmpfs \
 	$(USPACE_PATH)/srv/fs/fat/fat \
+	$(USPACE_PATH)/srv/fs/pipefs/pipefs \
 	$(USPACE_PATH)/srv/taskmon/taskmon \
 	$(USPACE_PATH)/srv/hw/netif/dp8390/dp8390 \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision a4b92fb02227d80cddeca1fc5b42d4cacb2f93ef)
+++ uspace/Makefile	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -70,4 +70,5 @@
 	srv/fs/tmpfs \
 	srv/fs/devfs \
+	srv/fs/pipefs \
 	srv/hid/adb_mouse \
 	srv/hid/char_mouse \
Index: uspace/srv/fs/pipefs/Makefile
===================================================================
--- uspace/srv/fs/pipefs/Makefile	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
+++ uspace/srv/fs/pipefs/Makefile	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 Jakub Jermar
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBBLOCK_PREFIX)/libblock.a $(LIBFS_PREFIX)/libfs.a
+EXTRA_CFLAGS += -I$(LIBBLOCK_PREFIX) -I$(LIBFS_PREFIX)
+BINARY = pipefs
+
+SOURCES = \
+	pipefs.c \
+	pipefs_ops.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/pipefs/pipefs.c
===================================================================
--- uspace/srv/fs/pipefs/pipefs.c	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
+++ uspace/srv/fs/pipefs/pipefs.c	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2006 Martin Decky
+ * Copyright (c) 2008 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */ 
+
+/**
+ * @file	pipefs.c
+ * @brief	File system driver for in-memory file system.
+ *
+ * Every instance of pipefs exists purely in memory and has neither a disk layout
+ * nor any permanent storage (e.g. disk blocks). With each system reboot, data
+ * stored in a pipefs file system is lost.
+ */
+
+#include "pipefs.h"
+#include <ipc/ipc.h>
+#include <ipc/services.h>
+#include <async.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <task.h>
+#include <libfs.h>
+#include "../../vfs/vfs.h"
+
+#define NAME "pipefs"
+
+
+vfs_info_t pipefs_vfs_info = {
+	.name = NAME,
+};
+
+fs_reg_t pipefs_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 PIPEFS. To overcome this bottleneck, VFS can send PIPEFS 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, PIPEFS 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, PIPEFS might want to keep the connections open after the
+ * request has been completed.
+ */
+static void pipefs_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	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.
+		 */
+		ipc_answer_0(iid, EOK);
+	}
+	
+	dprintf(NAME ": connection opened\n");
+	while (1) {
+		ipc_callid_t callid;
+		ipc_call_t call;
+	
+		callid = async_get_call(&call);
+		switch  (IPC_GET_METHOD(call)) {
+		case IPC_M_PHONE_HUNGUP:
+			return;
+		case VFS_OUT_MOUNTED:
+			pipefs_mounted(callid, &call);
+			break;
+		case VFS_OUT_MOUNT:
+			pipefs_mount(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNTED:
+			pipefs_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			pipefs_unmount(callid, &call);
+			break;
+		case VFS_OUT_LOOKUP:
+			pipefs_lookup(callid, &call);
+			break;
+		case VFS_OUT_READ:
+			pipefs_read(callid, &call);
+			break;
+		case VFS_OUT_WRITE:
+			pipefs_write(callid, &call);
+			break;
+		case VFS_OUT_TRUNCATE:
+			pipefs_truncate(callid, &call);
+			break;
+		case VFS_OUT_CLOSE:
+			pipefs_close(callid, &call);
+			break;
+		case VFS_OUT_DESTROY:
+			pipefs_destroy(callid, &call);
+			break;
+		case VFS_OUT_OPEN_NODE:
+			pipefs_open_node(callid, &call);
+			break;
+		case VFS_OUT_STAT:
+			pipefs_stat(callid, &call);
+			break;
+		case VFS_OUT_SYNC:
+			pipefs_sync(callid, &call);
+			break;
+		default:
+			ipc_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	printf(NAME ": HelenOS PIPEFS file system server\n");
+
+	if (!pipefs_init()) {
+		printf(NAME ": failed to initialize PIPEFS\n");
+		return -1;
+	}
+
+	int vfs_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VFS, 0, 0);
+	if (vfs_phone < EOK) {
+		printf(NAME ": Unable to connect to VFS\n");
+		return -1;
+	}
+
+	int rc = fs_register(vfs_phone, &pipefs_reg, &pipefs_vfs_info,
+	    pipefs_connection);
+	if (rc != EOK) {
+		printf(NAME ": Failed to register file system (%d)\n", rc);
+		return rc;
+	}
+
+	printf(NAME ": Accepting connections\n");
+	task_retval(0);
+	async_manager();
+	/* not reached */
+	return 0;
+}
+
+/**
+ * @}
+ */ 
Index: uspace/srv/fs/pipefs/pipefs.h
===================================================================
--- uspace/srv/fs/pipefs/pipefs.h	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
+++ uspace/srv/fs/pipefs/pipefs.h	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#ifndef PIPEFS_PIPEFS_H_
+#define PIPEFS_PIPEFS_H_
+
+#include <ipc/ipc.h>
+#include <libfs.h>
+#include <atomic.h>
+#include <sys/types.h>
+#include <bool.h>
+#include <adt/hash_table.h>
+
+#define PIPEFS_NODE(node)	((node) ? (pipefs_node_t *)(node)->data : NULL)
+#define FS_NODE(node)		((node) ? (node)->bp : NULL)
+
+typedef enum {
+	PIPEFS_NONE,
+	PIPEFS_FILE,
+	PIPEFS_DIRECTORY
+} pipefs_dentry_type_t;
+
+/* forward declaration */
+struct pipefs_node;
+
+typedef struct pipefs_dentry {
+	link_t link;		/**< Linkage for the list of siblings. */
+	struct pipefs_node *node;/**< Back pointer to PIPEFS node. */
+	char *name;		/**< Name of dentry. */
+} pipefs_dentry_t;
+
+typedef struct pipefs_node {
+	fs_node_t *bp;		/**< Back pointer to the FS node. */
+	fs_index_t index;	/**< PIPEFS node index. */
+	devmap_handle_t devmap_handle;/**< Device handle. */
+	link_t nh_link;		/**< Nodes hash table link. */
+	pipefs_dentry_type_t type;
+	unsigned lnkcnt;	/**< Link count. */
+	size_t size;		/**< File size if type is PIPEFS_FILE. */
+	void *data;		/**< File content's if type is PIPEFS_FILE. */
+	link_t cs_head;		/**< Head of child's siblings list. */
+} pipefs_node_t;
+
+extern fs_reg_t pipefs_reg;
+
+extern libfs_ops_t pipefs_libfs_ops;
+
+extern bool pipefs_init(void);
+
+extern void pipefs_mounted(ipc_callid_t, ipc_call_t *);
+extern void pipefs_mount(ipc_callid_t, ipc_call_t *);
+extern void pipefs_unmounted(ipc_callid_t, ipc_call_t *);
+extern void pipefs_unmount(ipc_callid_t, ipc_call_t *);
+extern void pipefs_lookup(ipc_callid_t, ipc_call_t *);
+extern void pipefs_read(ipc_callid_t, ipc_call_t *);
+extern void pipefs_write(ipc_callid_t, ipc_call_t *);
+extern void pipefs_truncate(ipc_callid_t, ipc_call_t *);
+extern void pipefs_stat(ipc_callid_t, ipc_call_t *);
+extern void pipefs_close(ipc_callid_t, ipc_call_t *);
+extern void pipefs_destroy(ipc_callid_t, ipc_call_t *);
+extern void pipefs_open_node(ipc_callid_t, ipc_call_t *);
+extern void pipefs_sync(ipc_callid_t, ipc_call_t *);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/pipefs/pipefs_ops.c
===================================================================
--- uspace/srv/fs/pipefs/pipefs_ops.c	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
+++ uspace/srv/fs/pipefs/pipefs_ops.c	(revision 5bd4f2cdcf5b1214b80769737edcc5abdd052fc9)
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+/**
+ * @file	pipefs_ops.c
+ * @brief	Implementation of VFS operations for the PIPEFS file system
+ *		server.
+ */
+
+#include "pipefs.h"
+#include "../../vfs/vfs.h"
+#include <ipc/ipc.h>
+#include <macros.h>
+#include <stdint.h>
+#include <async.h>
+#include <errno.h>
+#include <atomic.h>
+#include <stdlib.h>
+#include <str.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <adt/hash_table.h>
+#include <as.h>
+#include <libfs.h>
+
+#define min(a, b)		((a) < (b) ? (a) : (b))
+#define max(a, b)		((a) > (b) ? (a) : (b))
+
+#define NODES_BUCKETS	256
+
+/** All root nodes have index 0. */
+#define PIPEFS_SOME_ROOT		0
+/** Global counter for assigning node indices. Shared by all instances. */
+fs_index_t pipefs_next_index = 1;
+
+/*
+ * Implementation of the libfs interface.
+ */
+
+/* Forward declarations of static functions. */
+static int pipefs_match(fs_node_t **, fs_node_t *, const char *);
+static int pipefs_node_get(fs_node_t **, devmap_handle_t, fs_index_t);
+static int pipefs_node_open(fs_node_t *);
+static int pipefs_node_put(fs_node_t *);
+static int pipefs_create_node(fs_node_t **, devmap_handle_t, int);
+static int pipefs_destroy_node(fs_node_t *);
+static int pipefs_link_node(fs_node_t *, fs_node_t *, const char *);
+static int pipefs_unlink_node(fs_node_t *, fs_node_t *, const char *);
+
+/* Implementation of helper functions. */
+static int pipefs_root_get(fs_node_t **rfn, devmap_handle_t devmap_handle)
+{
+	return pipefs_node_get(rfn, devmap_handle, PIPEFS_SOME_ROOT); 
+}
+
+static int pipefs_has_children(bool *has_children, fs_node_t *fn)
+{
+	*has_children = !list_empty(&PIPEFS_NODE(fn)->cs_head);
+	return EOK;
+}
+
+static fs_index_t pipefs_index_get(fs_node_t *fn)
+{
+	return PIPEFS_NODE(fn)->index;
+}
+
+static aoff64_t pipefs_size_get(fs_node_t *fn)
+{
+	return PIPEFS_NODE(fn)->size;
+}
+
+static unsigned pipefs_lnkcnt_get(fs_node_t *fn)
+{
+	return PIPEFS_NODE(fn)->lnkcnt;
+}
+
+static char pipefs_plb_get_char(unsigned pos)
+{
+	return pipefs_reg.plb_ro[pos % PLB_SIZE];
+}
+
+static bool pipefs_is_directory(fs_node_t *fn)
+{
+	return PIPEFS_NODE(fn)->type == PIPEFS_DIRECTORY;
+}
+
+static bool pipefs_is_file(fs_node_t *fn)
+{
+	return PIPEFS_NODE(fn)->type == PIPEFS_FILE;
+}
+
+static devmap_handle_t pipefs_device_get(fs_node_t *fn)
+{
+	return 0;
+}
+
+/** libfs operations */
+libfs_ops_t pipefs_libfs_ops = {
+	.root_get = pipefs_root_get,
+	.match = pipefs_match,
+	.node_get = pipefs_node_get,
+	.node_open = pipefs_node_open,
+	.node_put = pipefs_node_put,
+	.create = pipefs_create_node,
+	.destroy = pipefs_destroy_node,
+	.link = pipefs_link_node,
+	.unlink = pipefs_unlink_node,
+	.has_children = pipefs_has_children,
+	.index_get = pipefs_index_get,
+	.size_get = pipefs_size_get,
+	.lnkcnt_get = pipefs_lnkcnt_get,
+	.plb_get_char = pipefs_plb_get_char,
+	.is_directory = pipefs_is_directory,
+	.is_file = pipefs_is_file,
+	.device_get = pipefs_device_get
+};
+
+/** Hash table of all PIPEFS nodes. */
+hash_table_t nodes;
+
+#define NODES_KEY_DEV	0	
+#define NODES_KEY_INDEX	1
+
+/* Implementation of hash table interface for the nodes hash table. */
+static hash_index_t nodes_hash(unsigned long key[])
+{
+	return key[NODES_KEY_INDEX] % NODES_BUCKETS;
+}
+
+static int nodes_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	pipefs_node_t *nodep = hash_table_get_instance(item, pipefs_node_t,
+	    nh_link);
+	
+	switch (keys) {
+	case 1:
+		return (nodep->devmap_handle == key[NODES_KEY_DEV]);
+	case 2:	
+		return ((nodep->devmap_handle == key[NODES_KEY_DEV]) &&
+		    (nodep->index == key[NODES_KEY_INDEX]));
+	default:
+		assert((keys == 1) || (keys == 2));
+	}
+
+	return 0;
+}
+
+static void nodes_remove_callback(link_t *item)
+{
+	pipefs_node_t *nodep = hash_table_get_instance(item, pipefs_node_t,
+	    nh_link);
+
+	while (!list_empty(&nodep->cs_head)) {
+		pipefs_dentry_t *dentryp = list_get_instance(nodep->cs_head.next,
+		    pipefs_dentry_t, link);
+
+		assert(nodep->type == PIPEFS_DIRECTORY);
+		list_remove(&dentryp->link);
+		free(dentryp);
+	}
+
+	if (nodep->data) {
+		assert(nodep->type == PIPEFS_FILE);
+		free(nodep->data);
+	}
+	free(nodep->bp);
+	free(nodep);
+}
+
+/** PIPEFS nodes hash table operations. */
+hash_table_operations_t nodes_ops = {
+	.hash = nodes_hash,
+	.compare = nodes_compare,
+	.remove_callback = nodes_remove_callback
+};
+
+static void pipefs_node_initialize(pipefs_node_t *nodep)
+{
+	nodep->bp = NULL;
+	nodep->index = 0;
+	nodep->devmap_handle = 0;
+	nodep->type = PIPEFS_NONE;
+	nodep->lnkcnt = 0;
+	nodep->size = 0;
+	nodep->data = NULL;
+	link_initialize(&nodep->nh_link);
+	list_initialize(&nodep->cs_head);
+}
+
+static void pipefs_dentry_initialize(pipefs_dentry_t *dentryp)
+{
+	link_initialize(&dentryp->link);
+	dentryp->name = NULL;
+	dentryp->node = NULL;
+}
+
+bool pipefs_init(void)
+{
+	if (!hash_table_create(&nodes, NODES_BUCKETS, 2, &nodes_ops))
+		return false;
+	
+	return true;
+}
+
+static bool pipefs_instance_init(devmap_handle_t devmap_handle)
+{
+	fs_node_t *rfn;
+	int rc;
+	
+	rc = pipefs_create_node(&rfn, devmap_handle, L_DIRECTORY);
+	if (rc != EOK || !rfn)
+		return false;
+	PIPEFS_NODE(rfn)->lnkcnt = 0;	/* FS root is not linked */
+	return true;
+}
+
+static void pipefs_instance_done(devmap_handle_t devmap_handle)
+{
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle
+	};
+	/*
+	 * Here we are making use of one special feature of our hash table
+	 * implementation, which allows to remove more items based on a partial
+	 * key match. In the following, we are going to remove all nodes
+	 * matching our device handle. The nodes_remove_callback() function will
+	 * take care of resource deallocation.
+	 */
+	hash_table_remove(&nodes, key, 1);
+}
+
+int pipefs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
+{
+	pipefs_node_t *parentp = PIPEFS_NODE(pfn);
+	link_t *lnk;
+
+	for (lnk = parentp->cs_head.next; lnk != &parentp->cs_head;
+	    lnk = lnk->next) {
+		pipefs_dentry_t *dentryp;
+		dentryp = list_get_instance(lnk, pipefs_dentry_t, link);
+		if (!str_cmp(dentryp->name, component)) {
+			*rfn = FS_NODE(dentryp->node);
+			return EOK;
+		}
+	}
+
+	*rfn = NULL;
+	return EOK;
+}
+
+int pipefs_node_get(fs_node_t **rfn, devmap_handle_t devmap_handle, fs_index_t index)
+{
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle,
+		[NODES_KEY_INDEX] = index
+	};
+	link_t *lnk = hash_table_find(&nodes, key);
+	if (lnk) {
+		pipefs_node_t *nodep;
+		nodep = hash_table_get_instance(lnk, pipefs_node_t, nh_link);
+		*rfn = FS_NODE(nodep);
+	} else {
+		*rfn = NULL;
+	}
+	return EOK;	
+}
+
+int pipefs_node_open(fs_node_t *fn)
+{
+	/* nothing to do */
+	return EOK;
+}
+
+int pipefs_node_put(fs_node_t *fn)
+{
+	/* nothing to do */
+	return EOK;
+}
+
+int pipefs_create_node(fs_node_t **rfn, devmap_handle_t devmap_handle, int lflag)
+{
+	fs_node_t *rootfn;
+	int rc;
+
+	assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
+
+	pipefs_node_t *nodep = malloc(sizeof(pipefs_node_t));
+	if (!nodep)
+		return ENOMEM;
+	pipefs_node_initialize(nodep);
+	nodep->bp = malloc(sizeof(fs_node_t));
+	if (!nodep->bp) {
+		free(nodep);
+		return ENOMEM;
+	}
+	fs_node_initialize(nodep->bp);
+	nodep->bp->data = nodep;	/* link the FS and PIPEFS nodes */
+
+	rc = pipefs_root_get(&rootfn, devmap_handle);
+	assert(rc == EOK);
+	if (!rootfn)
+		nodep->index = PIPEFS_SOME_ROOT;
+	else
+		nodep->index = pipefs_next_index++;
+	nodep->devmap_handle = devmap_handle;
+	if (lflag & L_DIRECTORY) 
+		nodep->type = PIPEFS_DIRECTORY;
+	else 
+		nodep->type = PIPEFS_FILE;
+
+	/* Insert the new node into the nodes hash table. */
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = nodep->devmap_handle,
+		[NODES_KEY_INDEX] = nodep->index
+	};
+	hash_table_insert(&nodes, key, &nodep->nh_link);
+	*rfn = FS_NODE(nodep);
+	return EOK;
+}
+
+int pipefs_destroy_node(fs_node_t *fn)
+{
+	pipefs_node_t *nodep = PIPEFS_NODE(fn);
+	
+	assert(!nodep->lnkcnt);
+	assert(list_empty(&nodep->cs_head));
+
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = nodep->devmap_handle,
+		[NODES_KEY_INDEX] = nodep->index
+	};
+	hash_table_remove(&nodes, key, 2);
+
+	/*
+	 * The nodes_remove_callback() function takes care of the actual
+	 * resource deallocation.
+	 */
+	return EOK;
+}
+
+int pipefs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	pipefs_node_t *parentp = PIPEFS_NODE(pfn);
+	pipefs_node_t *childp = PIPEFS_NODE(cfn);
+	pipefs_dentry_t *dentryp;
+	link_t *lnk;
+
+	assert(parentp->type == PIPEFS_DIRECTORY);
+
+	/* Check for duplicit entries. */
+	for (lnk = parentp->cs_head.next; lnk != &parentp->cs_head;
+	    lnk = lnk->next) {
+		dentryp = list_get_instance(lnk, pipefs_dentry_t, link);	
+		if (!str_cmp(dentryp->name, nm))
+			return EEXIST;
+	}
+
+	/* Allocate and initialize the dentry. */
+	dentryp = malloc(sizeof(pipefs_dentry_t));
+	if (!dentryp)
+		return ENOMEM;
+	pipefs_dentry_initialize(dentryp);
+
+	/* Populate and link the new dentry. */
+	size_t size = str_size(nm);
+	dentryp->name = malloc(size + 1);
+	if (!dentryp->name) {
+		free(dentryp);
+		return ENOMEM;
+	}
+	str_cpy(dentryp->name, size + 1, nm);
+	dentryp->node = childp;
+	childp->lnkcnt++;
+	list_append(&dentryp->link, &parentp->cs_head);
+
+	return EOK;
+}
+
+int pipefs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	pipefs_node_t *parentp = PIPEFS_NODE(pfn);
+	pipefs_node_t *childp = NULL;
+	pipefs_dentry_t *dentryp;
+	link_t *lnk;
+
+	if (!parentp)
+		return EBUSY;
+	
+	for (lnk = parentp->cs_head.next; lnk != &parentp->cs_head;
+	    lnk = lnk->next) {
+		dentryp = list_get_instance(lnk, pipefs_dentry_t, link);
+		if (!str_cmp(dentryp->name, nm)) {
+			childp = dentryp->node;
+			assert(FS_NODE(childp) == cfn);
+			break;
+		}	
+	}
+
+	if (!childp)
+		return ENOENT;
+		
+	if ((childp->lnkcnt == 1) && !list_empty(&childp->cs_head))
+		return ENOTEMPTY;
+
+	list_remove(&dentryp->link);
+	free(dentryp);
+	childp->lnkcnt--;
+
+	return EOK;
+}
+
+void pipefs_mounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	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) {
+		ipc_answer_0(rid, rc);
+		return;
+	}
+
+	/* Check if this device is not already mounted. */
+	rc = pipefs_root_get(&rootfn, devmap_handle);
+	if ((rc == EOK) && (rootfn)) {
+		(void) pipefs_node_put(rootfn);
+		free(opts);
+		ipc_answer_0(rid, EEXIST);
+		return;
+	}
+
+	/* Initialize PIPEFS instance. */
+	if (!pipefs_instance_init(devmap_handle)) {
+		free(opts);
+		ipc_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	rc = pipefs_root_get(&rootfn, devmap_handle);
+	assert(rc == EOK);
+	pipefs_node_t *rootp = PIPEFS_NODE(rootfn);
+	ipc_answer_3(rid, EOK, rootp->index, rootp->size, rootp->lnkcnt);
+	free(opts);
+}
+
+void pipefs_mount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_mount(&pipefs_libfs_ops, pipefs_reg.fs_handle, rid, request);
+}
+
+void pipefs_unmounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+
+	pipefs_instance_done(devmap_handle);
+	ipc_answer_0(rid, EOK);
+}
+
+void pipefs_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_unmount(&pipefs_libfs_ops, rid, request);
+}
+
+void pipefs_lookup(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_lookup(&pipefs_libfs_ops, pipefs_reg.fs_handle, rid, request);
+}
+
+void pipefs_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));
+	
+	/*
+	 * Lookup the respective PIPEFS node.
+	 */
+	link_t *hlp;
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle,
+		[NODES_KEY_INDEX] = index
+	};
+	hlp = hash_table_find(&nodes, key);
+	if (!hlp) {
+		ipc_answer_0(rid, ENOENT);
+		return;
+	}
+	pipefs_node_t *nodep = hash_table_get_instance(hlp, pipefs_node_t,
+	    nh_link);
+	
+	/*
+	 * Receive the read request.
+	 */
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		ipc_answer_0(callid, EINVAL);
+		ipc_answer_0(rid, EINVAL);
+		return;
+	}
+
+	size_t bytes;
+	if (nodep->type == PIPEFS_FILE) {
+		bytes = min(nodep->size - pos, size);
+		(void) async_data_read_finalize(callid, nodep->data + pos,
+		    bytes);
+	} else {
+		pipefs_dentry_t *dentryp;
+		link_t *lnk;
+		aoff64_t i;
+		
+		assert(nodep->type == PIPEFS_DIRECTORY);
+		
+		/*
+		 * Yes, we really use O(n) algorithm here.
+		 * If it bothers someone, it could be fixed by introducing a
+		 * hash table.
+		 */
+		for (i = 0, lnk = nodep->cs_head.next;
+		    (i < pos) && (lnk != &nodep->cs_head);
+		    i++, lnk = lnk->next)
+			;
+
+		if (lnk == &nodep->cs_head) {
+			ipc_answer_0(callid, ENOENT);
+			ipc_answer_1(rid, ENOENT, 0);
+			return;
+		}
+
+		dentryp = list_get_instance(lnk, pipefs_dentry_t, link);
+
+		(void) async_data_read_finalize(callid, dentryp->name,
+		    str_size(dentryp->name) + 1);
+		bytes = 1;
+	}
+
+	/*
+	 * Answer the VFS_READ call.
+	 */
+	ipc_answer_1(rid, EOK, bytes);
+}
+
+void pipefs_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));
+	
+	/*
+	 * Lookup the respective PIPEFS node.
+	 */
+	link_t *hlp;
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle,
+		[NODES_KEY_INDEX] = index
+	};
+	hlp = hash_table_find(&nodes, key);
+	if (!hlp) {
+		ipc_answer_0(rid, ENOENT);
+		return;
+	}
+	pipefs_node_t *nodep = hash_table_get_instance(hlp, pipefs_node_t,
+	    nh_link);
+
+	/*
+	 * Receive the write request.
+	 */
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_write_receive(&callid, &size)) {
+		ipc_answer_0(callid, EINVAL);	
+		ipc_answer_0(rid, EINVAL);
+		return;
+	}
+
+	/*
+	 * Check whether the file needs to grow.
+	 */
+	if (pos + size <= nodep->size) {
+		/* The file size is not changing. */
+		(void) async_data_write_finalize(callid, nodep->data + pos, size);
+		ipc_answer_2(rid, EOK, size, nodep->size);
+		return;
+	}
+	size_t delta = (pos + size) - nodep->size;
+	/*
+	 * At this point, we are deliberately extremely straightforward and
+	 * simply realloc the contents of the file on every write that grows the
+	 * file. In the end, the situation might not be as bad as it may look:
+	 * our heap allocator can save us and just grow the block whenever
+	 * possible.
+	 */
+	void *newdata = realloc(nodep->data, nodep->size + delta);
+	if (!newdata) {
+		ipc_answer_0(callid, ENOMEM);
+		ipc_answer_2(rid, EOK, 0, nodep->size);
+		return;
+	}
+	/* Clear any newly allocated memory in order to emulate gaps. */
+	memset(newdata + nodep->size, 0, delta);
+	nodep->size += delta;
+	nodep->data = newdata;
+	(void) async_data_write_finalize(callid, nodep->data + pos, size);
+	ipc_answer_2(rid, EOK, size, nodep->size);
+}
+
+void pipefs_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));
+	
+	/*
+	 * Lookup the respective PIPEFS node.
+	 */
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle,
+		[NODES_KEY_INDEX] = index
+	};
+	link_t *hlp = hash_table_find(&nodes, key);
+	if (!hlp) {
+		ipc_answer_0(rid, ENOENT);
+		return;
+	}
+	pipefs_node_t *nodep = hash_table_get_instance(hlp, pipefs_node_t,
+	    nh_link);
+	
+	if (size == nodep->size) {
+		ipc_answer_0(rid, EOK);
+		return;
+	}
+	
+	if (size > SIZE_MAX) {
+		ipc_answer_0(rid, ENOMEM);
+		return;
+	}
+	
+	void *newdata = realloc(nodep->data, size);
+	if (!newdata) {
+		ipc_answer_0(rid, ENOMEM);
+		return;
+	}
+	
+	if (size > nodep->size) {
+		size_t delta = size - nodep->size;
+		memset(newdata + nodep->size, 0, delta);
+	}
+	
+	nodep->size = size;
+	nodep->data = newdata;
+	ipc_answer_0(rid, EOK);
+}
+
+void pipefs_close(ipc_callid_t rid, ipc_call_t *request)
+{
+	ipc_answer_0(rid, EOK);
+}
+
+void pipefs_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;
+
+	link_t *hlp;
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = devmap_handle,
+		[NODES_KEY_INDEX] = index
+	};
+	hlp = hash_table_find(&nodes, key);
+	if (!hlp) {
+		ipc_answer_0(rid, ENOENT);
+		return;
+	}
+	pipefs_node_t *nodep = hash_table_get_instance(hlp, pipefs_node_t,
+	    nh_link);
+	rc = pipefs_destroy_node(FS_NODE(nodep));
+	ipc_answer_0(rid, rc);
+}
+
+void pipefs_open_node(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_open_node(&pipefs_libfs_ops, pipefs_reg.fs_handle, rid, request);
+}
+
+void pipefs_stat(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_stat(&pipefs_libfs_ops, pipefs_reg.fs_handle, rid, request);
+}
+
+void pipefs_sync(ipc_callid_t rid, ipc_call_t *request)
+{
+	/*
+	 * PIPEFS keeps its data structures always consistent,
+	 * thus the sync operation is a no-op.
+	 */
+	ipc_answer_0(rid, EOK);
+}
+
+/**
+ * @}
+ */
