Index: .bzrignore
===================================================================
--- .bzrignore	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
+++ .bzrignore	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -0,0 +1,148 @@
+Makefile.depend
+Makefile.depend.prev
+*.map
+*.disasm
+Makefile.common
+Makefile.config
+autotool/
+common.h
+common.h.prev
+config.h
+image.iso
+cscope.out
+boot/distroot/
+boot/initrd.fs
+boot/initrd.img
+kernel/kernel.bin
+kernel/kernel.dump
+kernel/kernel.map.prev
+kernel/kernel.raw
+kernel/arch/amd64/_link.ld
+kernel/arch/amd64/include/common.h
+kernel/arch/ia32/_link.ld
+kernel/generic/include/arch
+kernel/generic/include/genarch
+kernel/generic/src/debug/real_map.bin
+uspace/app/bdsh/bdsh
+uspace/app/edit/edit
+uspace/app/getterm/getterm
+uspace/app/init/init
+uspace/app/klog/klog
+uspace/app/mkfat/mkfat
+uspace/app/netecho/netecho
+uspace/app/nettest1/nettest1
+uspace/app/nettest2/nettest2
+uspace/app/ping/ping
+uspace/app/redir/redir
+uspace/app/sbi/sbi
+uspace/app/stats/stats
+uspace/app/taskdump/taskdump
+uspace/app/tasks/tasks
+uspace/app/test_serial/test_serial
+uspace/app/tester/tester
+uspace/app/tetris/tetris
+uspace/app/top/top
+uspace/app/trace/trace
+uspace/dist/app/bdsh
+uspace/dist/app/edit
+uspace/dist/app/getterm
+uspace/dist/app/klog
+uspace/dist/app/mkfat
+uspace/dist/app/netecho
+uspace/dist/app/nettest1
+uspace/dist/app/nettest2
+uspace/dist/app/ping
+uspace/dist/app/redir
+uspace/dist/app/sbi
+uspace/dist/app/stats
+uspace/dist/app/taskdump
+uspace/dist/app/tasks
+uspace/dist/app/test_serial
+uspace/dist/app/tester
+uspace/dist/app/tetris
+uspace/dist/app/top
+uspace/dist/app/trace
+uspace/dist/cfg/net/general
+uspace/dist/cfg/net/lo
+uspace/dist/cfg/net/ne2k
+uspace/dist/drv/isa/
+uspace/dist/drv/ns8250/
+uspace/dist/drv/pciintel/
+uspace/dist/drv/root/
+uspace/dist/drv/rootia32/
+uspace/dist/srv/arp
+uspace/dist/srv/ata_bd
+uspace/dist/srv/char_ms
+uspace/dist/srv/clip
+uspace/dist/srv/console
+uspace/dist/srv/devfs
+uspace/dist/srv/devman
+uspace/dist/srv/dp8390
+uspace/dist/srv/eth
+uspace/dist/srv/fat
+uspace/dist/srv/fb
+uspace/dist/srv/file_bd
+uspace/dist/srv/g_part
+uspace/dist/srv/i8042
+uspace/dist/srv/icmp
+uspace/dist/srv/ip
+uspace/dist/srv/kbd
+uspace/dist/srv/lo
+uspace/dist/srv/mbr_part
+uspace/dist/srv/net
+uspace/dist/srv/nildummy
+uspace/dist/srv/pipefs
+uspace/dist/srv/taskmon
+uspace/dist/srv/tcp
+uspace/dist/srv/tmpfs
+uspace/dist/srv/udp
+uspace/drv/root/root
+uspace/drv/isa/isa
+uspace/drv/ns8250/ns8250
+uspace/drv/pciintel/pciintel
+uspace/drv/rootia32/rootia32
+uspace/lib/c/arch/amd64/_link.ld
+uspace/lib/c/arch/amd64/include/common.h
+uspace/lib/c/arch/ia32/_link.ld
+uspace/lib/c/include/arch
+uspace/lib/c/include/kernel
+uspace/lib/c/include/libarch
+uspace/srv/bd/ata_bd/ata_bd
+uspace/srv/bd/file_bd/file_bd
+uspace/srv/bd/gxe_bd/gxe_bd
+uspace/srv/bd/part/guid_part/g_part
+uspace/srv/bd/part/mbr_part/mbr_part
+uspace/srv/bd/rd/rd
+uspace/srv/clip/clip
+uspace/srv/devman/devman
+uspace/srv/devmap/devmap
+uspace/srv/fs/devfs/devfs
+uspace/srv/fs/fat/fat
+uspace/srv/fs/tmpfs/tmpfs
+uspace/srv/fs/pipefs/pipefs
+uspace/srv/hid/adb_mouse/adb_ms
+uspace/srv/hid/char_mouse/char_ms
+uspace/srv/hid/console/console
+uspace/srv/hid/fb/fb
+uspace/srv/hid/kbd/kbd
+uspace/srv/hid/s3c24xx_ts/s3c24ts
+uspace/srv/hw/char/i8042/i8042
+uspace/srv/hw/char/s3c24xx_uart/s3c24ser
+uspace/srv/hw/netif/dp8390/dp8390
+uspace/srv/loader/loader
+uspace/srv/loader/arch/amd64/_link.ld
+uspace/srv/loader/arch/ia32/_link.ld
+uspace/srv/net/cfg/lo
+uspace/srv/net/cfg/ne2k
+uspace/srv/net/il/arp/arp
+uspace/srv/net/il/ip/ip
+uspace/srv/net/net/net
+uspace/srv/net/netif/lo/lo
+uspace/srv/net/nil/eth/eth
+uspace/srv/net/nil/nildummy/nildummy
+uspace/srv/net/tl/icmp/icmp
+uspace/srv/net/tl/tcp/tcp
+uspace/srv/net/tl/udp/udp
+uspace/srv/ns/ns
+uspace/srv/taskmon/taskmon
+uspace/srv/vfs/vfs
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision cb819f92c9dcc88804bdf9c7b5816464ce237d44)
+++ boot/Makefile.common	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -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 cb819f92c9dcc88804bdf9c7b5816464ce237d44)
+++ uspace/Makefile	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -70,4 +70,5 @@
 	srv/fs/tmpfs \
 	srv/fs/devfs \
+	srv/fs/pipefs \
 	srv/hid/adb_mouse \
 	srv/hid/char_mouse \
Index: uspace/app/bdsh/cmds/modules/mount/mount.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mount/mount.c	(revision cb819f92c9dcc88804bdf9c7b5816464ce237d44)
+++ uspace/app/bdsh/cmds/modules/mount/mount.c	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -44,5 +44,5 @@
 {
 	static char helpfmt[] =
-	    "Usage:  %s <fstype> <mp> <dev> [<moptions>]\n";
+	    "Usage:  %s <fstype> <mp> [dev] [<moptions>]\n";
 	if (level == HELP_SHORT) {
 		printf("'%s' mounts a file system.\n", cmdname);
@@ -59,17 +59,20 @@
 	unsigned int argc;
 	const char *mopts = "";
+	const char *dev = "";
 	int rc;
 
 	argc = cli_count_args(argv);
 
-	if ((argc < 4) || (argc > 5)) {
+	if ((argc < 3) || (argc > 5)) {
 		printf("%s: invalid number of arguments.\n",
 		    cmdname);
 		return CMD_FAILURE;
 	}
+	if (argc > 3)
+		dev = argv[3];
 	if (argc == 5)
 		mopts = argv[4];
 
-	rc = mount(argv[1], argv[2], argv[3], mopts, 0);
+	rc = mount(argv[1], argv[2], dev, mopts, 0);
 	if (rc != EOK) {
 		printf("Unable to mount %s filesystem to %s on %s (rc=%d)\n",
Index: uspace/srv/fs/pipefs/Makefile
===================================================================
--- uspace/srv/fs/pipefs/Makefile	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
+++ uspace/srv/fs/pipefs/Makefile	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -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 c646ea062ed8a7bd20f8309cacad0242d11107e1)
+++ uspace/srv/fs/pipefs/pipefs.c	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -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 c646ea062ed8a7bd20f8309cacad0242d11107e1)
+++ uspace/srv/fs/pipefs/pipefs.h	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -0,0 +1,107 @@
+/*
+ * 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. */
+	/* Following is for nodes of type PIPEFS_FILE */
+	aoff64_t start;		/**< File offset where first data block resides */
+	size_t size;		/**< File size if type is PIPEFS_FILE. */
+	link_t data_head;	/**< Head of data blocks list for PIPEFS_FILE. */
+	/* This is for directory */
+	link_t cs_head;		/**< Head of child's siblings list. */	
+} pipefs_node_t;
+
+typedef struct pipefs_data_block {
+	link_t link;		/**< Linkage for the list of data blocks */
+	size_t size;		/**< Size of this block */
+	void *data;			/**< Data for this block */
+} pipefs_data_block_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 c646ea062ed8a7bd20f8309cacad0242d11107e1)
+++ uspace/srv/fs/pipefs/pipefs_ops.c	(revision c646ea062ed8a7bd20f8309cacad0242d11107e1)
@@ -0,0 +1,788 @@
+/*
+ * 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);
+	}
+
+	while (!list_empty(&nodep->data_head)) {
+		assert(nodep->type == PIPEFS_FILE);
+		
+		pipefs_data_block_t *data_block = list_get_instance(nodep->data_head.next,
+		    pipefs_data_block_t, link);
+		
+		list_remove(&data_block->link);
+		free(data_block->data);
+		free(data_block);
+	}
+	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->start = 0;
+	nodep->size = 0;
+	list_initialize(&nodep->data_head);
+	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) {
+		/*
+		 * Check if we still have the requested data.
+		 * This may happen if the client seeked backwards
+		 */
+		if (pos < nodep->start) {
+			ipc_answer_0(callid, ENOTSUP);
+			ipc_answer_0(rid, ENOTSUP);
+			return;
+		}
+		
+		/*
+		 * Free all data blocks that end before pos.
+		 * This is in case the client seeked forward 
+		 */
+		while (!list_empty(&nodep->data_head)) {
+			pipefs_data_block_t *data_block =
+			    list_get_instance(nodep->data_head.next,
+			        pipefs_data_block_t, link);
+		
+			aoff64_t block_end = nodep->start + data_block->size;
+			
+			if (block_end > pos) {
+				break;
+			}
+			
+			list_remove(&data_block->link);
+			free(data_block->data);
+			free(data_block);
+			nodep->start = block_end;
+		}
+		
+		if (!list_empty(&nodep->data_head)) {
+			/* We have data block, so let's return its portion after pos */
+			pipefs_data_block_t *data_block =
+			    list_get_instance(nodep->data_head.next,
+			        pipefs_data_block_t, link);
+			
+			assert(nodep->start <= pos);
+			
+			aoff64_t block_end = nodep->start + data_block->size;
+			size_t block_offset = pos - nodep->start;
+			
+			assert(block_end > pos);
+			
+			bytes = min(block_end - pos, size);
+			(void) async_data_read_finalize(callid,
+			    data_block->data + block_offset,
+			    bytes);
+			
+			if (nodep->start + block_offset + bytes == block_end) {
+				/* Free the data block - it was fully read */
+				list_remove(&data_block->link);
+				free(data_block->data);
+				free(data_block);
+				nodep->start = block_end;
+			}
+		}
+		else {
+			/*
+			 * there is no data
+			 * TODO implement waiting for the data
+			 * and remove this else clause
+			 */
+			ipc_answer_0(callid, ENOTSUP);
+			ipc_answer_1(rid, ENOTSUP, 0);
+			return;
+		}
+	} 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 we are writing to the end
+	 */
+	if (pos != nodep->size) {
+		ipc_answer_0(callid, ENOTSUP);
+		ipc_answer_2(rid, EOK, 0, nodep->size);
+		return;
+	}
+	
+	/*
+	 * Allocate a buffer for the new data.
+	 * Currently we accept any size, create a data block from this
+	 * and append it to the end of a file
+	 */
+	void *newdata = malloc(size);
+	if (!newdata) {
+		ipc_answer_0(callid, ENOMEM);
+		ipc_answer_2(rid, EOK, 0, nodep->size);
+		return;
+	}
+	
+	pipefs_data_block_t *newblock = malloc(sizeof(pipefs_data_block_t));
+	
+	if (!newblock) {
+		free(newdata);
+		ipc_answer_0(callid, ENOMEM);
+		ipc_answer_2(rid, EOK, 0, nodep->size);
+		return;
+	}
+	
+	int rc = async_data_write_finalize(callid, newdata, size);
+	
+	if (rc != EOK) {
+		free(newblock);
+		free(newdata);
+		ipc_answer_0(callid, rc);
+		ipc_answer_2(rid, EOK, 0, nodep->size);
+		return;
+	}
+	
+	link_initialize(&newblock->link);
+	newblock->size = size;
+	newblock->data = newdata;
+	list_append(&newblock->link, &nodep->data_head);
+	
+	nodep->size += size;
+	
+	ipc_answer_2(rid, EOK, size, nodep->size);
+}
+
+void pipefs_truncate(ipc_callid_t rid, ipc_call_t *request)
+{
+	/*
+	 * PIPEFS does not support resizing of files
+	 */
+	ipc_answer_0(rid, ENOTSUP);
+}
+
+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);
+}
+
+/**
+ * @}
+ */
