Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/app/trace/trace.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -696,6 +696,4 @@
 
 	p = proto_new("vfs");
-	o = oper_new("open", 2, arg_def, V_INT_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_OPEN, o);
 	o = oper_new("read", 1, arg_def, V_ERRNO, 1, resp_def);
 	proto_add_oper(p, VFS_IN_READ, o);
@@ -716,14 +714,14 @@
 	o = oper_new("sync", 1, arg_def, V_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_SYNC, o);
-	o = oper_new("mkdir", 1, arg_def, V_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_MKDIR, o);
-	o = oper_new("unlink", 0, arg_def, V_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_UNLINK, o);
 	o = oper_new("rename", 0, arg_def, V_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_RENAME, o);
-	o = oper_new("stat", 0, arg_def, V_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_STAT, o);
 	o = oper_new("statfs", 0, arg_def, V_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_STATFS, o);
+	o = oper_new("walk", 2, arg_def, V_INT_ERRNO, 0, resp_def);
+	proto_add_oper(p, VFS_IN_WALK, o);
+	o = oper_new("open2", 2, arg_def, V_ERRNO, 0, resp_def);
+	proto_add_oper(p, VFS_IN_OPEN2, o);
+	o = oper_new("unlink2", 3, arg_def, V_ERRNO, 0, resp_def);
+	proto_add_oper(p, VFS_IN_UNLINK2, o);
 
 	proto_register(SERVICE_VFS, p);
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -93,4 +93,36 @@
 }
 
+int _vfs_walk(int parent, const char *path, int flags)
+{
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, VFS_IN_WALK, parent, flags, &answer);
+	sysarg_t rc = async_data_write_start(exch, path, str_size(path));
+	vfs_exchange_end(exch);
+		
+	sysarg_t rc_orig;
+	async_wait_for(req, &rc_orig);
+
+	if (rc_orig != EOK) {
+		return (int) rc_orig;
+	}
+		
+	if (rc != EOK) {
+		return (int) rc;
+	}
+	
+	return (int) IPC_GET_ARG1(answer);
+}
+
+int _vfs_open(int fildes, int mode)
+{
+	async_exch_t *exch = vfs_exchange_begin();
+	sysarg_t rc = async_req_2_0(exch, VFS_IN_OPEN2, fildes, mode);
+	vfs_exchange_end(exch);
+	
+	return (int) rc;
+}
+
 char *vfs_absolutize(const char *path, size_t *retlen)
 {
@@ -229,20 +261,4 @@
 	}
 	
-	/* Ask VFS whether it likes fs_name. */
-	rc = async_req_0_0(exch, VFS_IN_PING);
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-		free(mpa);
-		async_wait_for(req, &rc_orig);
-		
-		if (null_id != -1)
-			loc_null_destroy(null_id);
-		
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	
 	vfs_exchange_end(exch);
 	free(mpa);
@@ -289,43 +305,15 @@
 }
 
-/** Open file (internal).
- *
- * @param abs Absolute path to file
- * @param abs_size Size of @a abs string
- * @param lflag L_xxx flags
- * @param oflag O_xxx flags
- * @param fd Place to store new file descriptor
- *
- * @return EOK on success, non-zero error code on error
- */
-static int open_internal(const char *abs, size_t abs_size, int lflag, int oflag,
-    int *fd)
-{
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, VFS_IN_OPEN, lflag, oflag, 0, &answer);
-	sysarg_t rc = async_data_write_start(exch, abs, abs_size);
-	
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-
-		sysarg_t rc_orig;
-		async_wait_for(req, &rc_orig);
-		
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	
-	vfs_exchange_end(exch);
-	async_wait_for(req, &rc);
-	
-	if (rc != EOK)
-	    return (int) rc;
-	
-	*fd = (int) IPC_GET_ARG1(answer);
-	return EOK;
+static int walk_flags(int oflags)
+{
+	int flags = 0;
+	if (oflags & O_CREAT) {
+		if (oflags & O_EXCL) {
+			flags |= WALK_MUST_CREATE;
+		} else {
+			flags |= WALK_MAY_CREATE;
+		}
+	}
+	return flags;
 }
 
@@ -341,22 +329,44 @@
 int open(const char *path, int oflag, ...)
 {
+	// FIXME: Some applications call this incorrectly.
+	if ((oflag & (O_RDONLY|O_WRONLY|O_RDWR)) == 0) {
+		oflag |= O_RDWR;
+	}
+
+	assert((((oflag & O_RDONLY) != 0) + ((oflag & O_WRONLY) != 0) + ((oflag & O_RDWR) != 0)) == 1);
+	
 	size_t abs_size;
 	char *abs = vfs_absolutize(path, &abs_size);
-	int fd = -1;
-	
-	if (abs == NULL) {
-		errno = ENOMEM;
-		return -1;
-	}
-	
-	int rc = open_internal(abs, abs_size, L_FILE, oflag, &fd);
-	free(abs);
-	
-	if (rc != EOK) {
-		errno = rc;
-		return -1;
-	}
-	
-	return fd;
+	if (!abs) {
+		return ENOMEM;
+	}
+	
+	int ret = _vfs_walk(-1, abs, walk_flags(oflag) | WALK_REGULAR);
+	if (ret < 0) {
+		return ret;
+	}
+	
+	int mode =
+		((oflag & O_RDWR) ? MODE_READ|MODE_WRITE : 0) |
+		((oflag & O_RDONLY) ? MODE_READ : 0) |
+		((oflag & O_WRONLY) ? MODE_WRITE : 0) |
+		((oflag & O_APPEND) ? MODE_APPEND : 0);
+	
+	int rc = _vfs_open(ret, mode); 
+	if (rc < 0) {
+		// _vfs_put(ret);
+		close(ret);
+		return rc;
+	}
+	
+	if (oflag & O_TRUNC) {
+		assert(oflag & O_WRONLY || oflag & O_RDWR);
+		assert(!(oflag & O_APPEND));
+		
+		// _vfs_resize
+		(void) ftruncate(ret, 0);
+	}
+
+	return ret;
 }
 
@@ -670,50 +680,18 @@
 int stat(const char *path, struct stat *stat)
 {
-	sysarg_t rc;
-	sysarg_t rc_orig;
-	aid_t req;
-	
 	size_t pa_size;
 	char *pa = vfs_absolutize(path, &pa_size);
-	if (pa == NULL) {
-		errno = ENOMEM;
-		return -1;
-	}
-	
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	req = async_send_0(exch, VFS_IN_STAT, NULL);
-	rc = async_data_write_start(exch, pa, pa_size);
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-		free(pa);
-		async_wait_for(req, &rc_orig);
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		if (rc != EOK) {
-			errno = rc;
-			return -1;
-		}
-	}
-	rc = async_data_read_start(exch, stat, sizeof(struct stat));
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-		free(pa);
-		async_wait_for(req, &rc_orig);
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		if (rc != EOK) {
-			errno = rc;
-			return -1;
-		}
-	}
-	vfs_exchange_end(exch);
-	free(pa);
-	async_wait_for(req, &rc);
-	if (rc != EOK) {
-		errno = rc;
-		return -1;
-	}
-	return 0;
+	if (!pa) {
+		return ENOMEM;
+	}
+	
+	int fd = _vfs_walk(-1, pa, 0);
+	if (fd < 0) {
+		return fd;
+	}
+	
+	int rc = fstat(fd, stat);
+	close(fd);
+	return rc;
 }
 
@@ -727,7 +705,5 @@
 {
 	DIR *dirp = malloc(sizeof(DIR));
-	int fd = -1;
-	
-	if (dirp == NULL) {
+	if (!dirp) {
 		errno = ENOMEM;
 		return NULL;
@@ -742,14 +718,22 @@
 	}
 	
-	int rc = open_internal(abs, abs_size, L_DIRECTORY, 0, &fd);
+	int ret = _vfs_walk(-1, abs, WALK_DIRECTORY);
 	free(abs);
 	
-	if (rc != EOK) {
+	if (ret < EOK) {
 		free(dirp);
+		errno = ret;
+		return NULL;
+	}
+	
+	int rc = _vfs_open(ret, MODE_READ);
+	if (rc < 0) {
+		free(dirp);
+		close(ret);
 		errno = rc;
 		return NULL;
 	}
 	
-	dirp->fd = fd;
+	dirp->fd = ret;
 	return dirp;
 }
@@ -809,120 +793,70 @@
 int mkdir(const char *path, mode_t mode)
 {
+	size_t pa_size;
+	char *pa = vfs_absolutize(path, &pa_size);
+	if (!pa) {
+		return ENOMEM;
+	}
+	
+	int ret = _vfs_walk(-1, pa, WALK_MUST_CREATE | WALK_DIRECTORY);
+	if (ret < 0) {
+		return ret;
+	}
+	
+	close(ret);
+	return EOK;
+}
+
+static int _vfs_unlink2(int parent, const char *path, int expect, int wflag)
+{
 	sysarg_t rc;
 	aid_t req;
 	
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	req = async_send_3(exch, VFS_IN_UNLINK2, parent, expect, wflag, NULL);
+	rc = async_data_write_start(exch, path, str_size(path));
+	
+	vfs_exchange_end(exch);
+	
+	sysarg_t rc_orig;
+	async_wait_for(req, &rc_orig);
+	
+	if (rc_orig != EOK) {
+		return (int) rc_orig;
+	}
+	return rc;
+}
+
+/** Unlink file or directory.
+ *
+ * @param path Path
+ * @return EOk on success, error code on error
+ */
+int unlink(const char *path)
+{
 	size_t pa_size;
 	char *pa = vfs_absolutize(path, &pa_size);
-	if (pa == NULL) {
-		errno = ENOMEM;
-		return -1;
-	}
-	
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	req = async_send_1(exch, VFS_IN_MKDIR, mode, NULL);
-	rc = async_data_write_start(exch, pa, pa_size);
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-		free(pa);
-		
-		sysarg_t rc_orig;
-		async_wait_for(req, &rc_orig);
-		
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		
-		if (rc != EOK) {
-			errno = rc;
-			return -1;
-		}
-		
-		return 0;
-	}
-	
-	vfs_exchange_end(exch);
-	free(pa);
-	async_wait_for(req, &rc);
-	
-	if (rc != EOK) {
-		errno = rc;
-		return -1;
-	}
-	
-	return 0;
-}
-
-/** Unlink a file or directory.
- *
- * @param path Path to file or empty directory
- * @param lflag L_xxx flag (L_NONE, L_FILE or L_DIRECTORY)
- * @return EOK on success, non-zero error code on error
- */
-static int _unlink(const char *path, int lflag)
-{
-	sysarg_t rc;
-	aid_t req;
-	
+	if (!pa) {
+		return ENOMEM;
+	}
+	
+	return _vfs_unlink2(-1, pa, -1, 0);
+}
+
+/** Remove empty directory.
+ *
+ * @param path Path
+ * @return 0 on success. On error returns -1 and sets errno.
+ */
+int rmdir(const char *path)
+{
 	size_t pa_size;
 	char *pa = vfs_absolutize(path, &pa_size);
-	if (pa == NULL)
+	if (!pa) {
 		return ENOMEM;
-	
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	req = async_send_1(exch, VFS_IN_UNLINK, lflag, NULL);
-	rc = async_data_write_start(exch, pa, pa_size);
-	if (rc != EOK) {
-		vfs_exchange_end(exch);
-		free(pa);
-
-		sysarg_t rc_orig;
-		async_wait_for(req, &rc_orig);
-
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	vfs_exchange_end(exch);
-	free(pa);
-	async_wait_for(req, &rc);
-	return rc;
-}
-
-/** Unlink file or directory.
- *
- * @param path Path
- * @return EOk on success, error code on error
- */
-int unlink(const char *path)
-{
-	int rc;
-
-	rc = _unlink(path, L_NONE);
-	if (rc != EOK) {
-		errno = rc;
-		return -1;
-	}
-
-	return 0;
-}
-
-/** Remove empty directory.
- *
- * @param path Path
- * @return 0 on success. On error returns -1 and sets errno.
- */
-int rmdir(const char *path)
-{
-	int rc;
-
-	rc = _unlink(path, L_DIRECTORY);
-	if (rc != EOK) {
-		errno = rc;
-		return -1;
-	}
-
-	return 0;
+	}
+	
+	return _vfs_unlink2(-1, pa, -1, WALK_DIRECTORY);
 }
 
@@ -957,5 +891,5 @@
 	async_exch_t *exch = vfs_exchange_begin();
 	
-	req = async_send_0(exch, VFS_IN_RENAME, NULL);
+	req = async_send_1(exch, VFS_IN_RENAME, -1, NULL);
 	rc = async_data_write_start(exch, olda, olda_size);
 	if (rc != EOK) {
@@ -1018,16 +952,11 @@
 	size_t abs_size;
 	char *abs = vfs_absolutize(path, &abs_size);
-	int fd = -1;
-	
-	if (abs == NULL) {
-		errno = ENOMEM;
-		return -1;
-	}
-	
-	int rc = open_internal(abs, abs_size, L_DIRECTORY, O_DESC, &fd);
-	
-	if (rc != EOK) {
+	if (!abs)
+		return ENOMEM;
+	
+	int fd = _vfs_walk(-1, abs, WALK_DIRECTORY);
+	if (fd < 0) {
 		free(abs);
-		errno = rc;
+		errno = fd;
 		return -1;
 	}
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/lib/c/include/ipc/vfs.h	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -63,6 +63,5 @@
 
 typedef enum {
-	VFS_IN_OPEN = IPC_FIRST_USER_METHOD,
-	VFS_IN_READ,
+	VFS_IN_READ = IPC_FIRST_USER_METHOD,
 	VFS_IN_WRITE,
 	VFS_IN_SEEK,
@@ -75,12 +74,13 @@
 	VFS_IN_SYNC,
 	VFS_IN_REGISTER,
-	VFS_IN_MKDIR,
 	VFS_IN_UNLINK,
 	VFS_IN_RENAME,
-	VFS_IN_STAT,
 	VFS_IN_DUP,
 	VFS_IN_WAIT_HANDLE,
 	VFS_IN_MTAB_GET,
-	VFS_IN_STATFS
+	VFS_IN_STATFS,
+	VFS_IN_WALK,
+	VFS_IN_OPEN2,
+	VFS_IN_UNLINK2,
 } vfs_in_request_t;
 
@@ -91,11 +91,12 @@
 	VFS_OUT_TRUNCATE,
 	VFS_OUT_CLOSE,
-	VFS_OUT_MOUNT,
 	VFS_OUT_MOUNTED,
-	VFS_OUT_UNMOUNT,
 	VFS_OUT_UNMOUNTED,
+	VFS_OUT_GET_SIZE,
+	VFS_OUT_IS_EMPTY,
 	VFS_OUT_SYNC,
 	VFS_OUT_STAT,
 	VFS_OUT_LOOKUP,
+	VFS_OUT_LINK,
 	VFS_OUT_DESTROY,
 	VFS_OUT_STATFS,
@@ -127,8 +128,8 @@
 
 /**
- * Lookup will succeed only if the object is a root directory. The flag is
- * mutually exclusive with L_FILE and L_MP.
+ * Lookup will not cross any mount points.
+ * If the lookup would have to cross a mount point, it returns EXDEV instead.
  */
-#define L_ROOT			4
+#define L_DISABLE_MOUNTS	4
 
 /**
@@ -151,9 +152,4 @@
 
 /**
- * L_LINK is used for linking to an already existing nodes.
- */
-#define L_LINK			64
-
-/**
  * L_UNLINK is used to remove leaves from the file system namespace. This flag
  * cannot be passed directly by the client, but will be set by VFS during
@@ -170,4 +166,30 @@
 #define L_OPEN			256
 
+/*
+ * Walk flags.
+ */
+enum {
+	/**
+	 * WALK_PARTIAL requests that if the whole path cannot be traversed,
+	 * the walk() operation should return the last visited file, along
+	 * with an indication of how many directories have been traversed.
+	 */
+	//WALK_PARTIAL = (1 << 0),
+	
+	WALK_MAY_CREATE = (1 << 1),
+	WALK_MUST_CREATE = (1 << 2),
+	
+	WALK_REGULAR = (1 << 3),
+	WALK_DIRECTORY = (1 << 4),
+	
+	WALK_ALL_FLAGS = WALK_MAY_CREATE | WALK_MUST_CREATE | WALK_REGULAR | WALK_DIRECTORY,
+};
+
+enum {
+	MODE_READ = 1,
+	MODE_WRITE = 2,
+	MODE_APPEND = 4,
+};
+
 #endif
 
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/lib/c/include/vfs/vfs.h	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -64,4 +64,7 @@
 extern void vfs_exchange_end(async_exch_t *);
 
+extern int _vfs_walk(int parent, const char *path, int flags);
+extern int _vfs_open(int file, int mode);
+
 #endif
 
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/lib/fs/libfs.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -36,5 +36,4 @@
 
 #include "libfs.h"
-#include "../../srv/vfs/vfs.h"
 #include <macros.h>
 #include <errno.h>
@@ -47,4 +46,5 @@
 #include <sys/statfs.h>
 #include <stdlib.h>
+#include <fibril_synch.h>
 
 #define on_error(rc, action) \
@@ -63,4 +63,9 @@
 	} while (0)
 
+#define DPRINTF(...)
+
+#define LOG_EXIT(rc) \
+	DPRINTF("Exiting %s() with rc = %d at line %d\n", __FUNC__, rc, __LINE__);
+
 static fs_reg_t reg;
 
@@ -68,6 +73,6 @@
 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_link(libfs_ops_t *, fs_handle_t, ipc_callid_t,
+    ipc_call_t *);
 static void libfs_lookup(libfs_ops_t *, fs_handle_t, ipc_callid_t,
     ipc_call_t *);
@@ -104,9 +109,4 @@
 }
 
-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)
 {
@@ -119,8 +119,7 @@
 }
 
-static void vfs_out_unmount(ipc_callid_t rid, ipc_call_t *req)
-{
-		
-	libfs_unmount(libfs_ops, rid, req);
+static void vfs_out_link(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_link(libfs_ops, reg.fs_handle, rid, req);
 }
 
@@ -193,8 +192,15 @@
 	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
 	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+
 	int rc;
-
-	rc = vfs_out_ops->destroy(service_id, index);
-
+	fs_node_t *node = NULL;
+	rc = libfs_ops->node_get(&node, service_id, index);
+	if (rc == EOK && node != NULL) {
+		bool destroy = (libfs_ops->lnkcnt_get(node) == 0);
+		libfs_ops->node_put(node);
+		if (destroy) {
+			rc = vfs_out_ops->destroy(service_id, index);
+		}
+	}
 	async_answer_0(rid, rc);
 }
@@ -225,4 +231,51 @@
 	libfs_statfs(libfs_ops, reg.fs_handle, rid, req);
 }
+
+static void vfs_out_get_size(ipc_callid_t rid, ipc_call_t *req)
+{
+	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	int rc;
+
+	fs_node_t *node = NULL;
+	rc = libfs_ops->node_get(&node, service_id, index);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+	}
+	if (node == NULL) {
+		async_answer_0(rid, EINVAL);
+	}
+	
+	uint64_t size = libfs_ops->size_get(node);
+	libfs_ops->node_put(node);
+	
+	async_answer_2(rid, EOK, LOWER32(size), UPPER32(size));
+}
+
+static void vfs_out_is_empty(ipc_callid_t rid, ipc_call_t *req)
+{
+	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*req);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*req);
+	int rc;
+
+	fs_node_t *node = NULL;
+	rc = libfs_ops->node_get(&node, service_id, index);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+	}
+	if (node == NULL) {
+		async_answer_0(rid, EINVAL);
+	}
+	
+	bool children = false;
+	rc = libfs_ops->has_children(&children, node);
+	libfs_ops->node_put(node);
+	
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+	}
+	async_answer_0(rid, children ? ENOTEMPTY : EOK);
+}
+
 static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
@@ -247,12 +300,9 @@
 			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);
+		case VFS_OUT_LINK:
+			vfs_out_link(callid, &call);
 			break;
 		case VFS_OUT_LOOKUP:
@@ -285,4 +335,10 @@
 		case VFS_OUT_STATFS:
 			vfs_out_statfs(callid, &call);
+			break;
+		case VFS_OUT_GET_SIZE:
+			vfs_out_get_size(callid, &call);
+			break;
+		case VFS_OUT_IS_EMPTY:
+			vfs_out_is_empty(callid, &call);
 			break;
 		default:
@@ -383,116 +439,88 @@
 }
 
-void libfs_mount(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
-    ipc_call_t *req)
-{
-	service_id_t mp_service_id = (service_id_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);
-	service_id_t mr_service_id = (service_id_t) IPC_GET_ARG4(*req);
-	
-	async_sess_t *mountee_sess = async_clone_receive(EXCHANGE_PARALLEL);
-	if (mountee_sess == NULL) {
-		async_answer_0(rid, EINVAL);
+static char plb_get_char(unsigned pos)
+{
+	return reg.plb_ro[pos % PLB_SIZE];
+}
+
+static int plb_get_component(char *dest, unsigned *sz, unsigned *ppos, unsigned last)
+{
+	unsigned pos = *ppos;
+	unsigned size = 0;
+	
+	if (pos == last) {
+		*sz = 0;
+		return ERANGE;
+	}
+
+	char c = plb_get_char(pos); 
+	if (c == '/') {
+		pos++;
+	}
+	
+	for (int i = 0; i <= NAME_MAX; i++) {
+		c = plb_get_char(pos);
+		if (pos == last || c == '/') {
+			dest[i] = 0;
+			*ppos = pos;
+			*sz = size;
+			return EOK;
+		}
+		dest[i] = c;
+		pos++;
+		size++;
+	}
+	return ENAMETOOLONG;
+}
+
+static int receive_fname(char *buffer)
+{
+	size_t size;
+	ipc_callid_t wcall;
+	
+	if (!async_data_write_receive(&wcall, &size)) {
+		return ENOENT;
+	}
+	if (size > NAME_MAX + 1) {
+		async_answer_0(wcall, ERANGE);
+		return ERANGE;
+	}
+	return async_data_write_finalize(wcall, buffer, size);
+}
+
+/** Link a file at a path.
+ */
+void libfs_link(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid, ipc_call_t *req)
+{
+	service_id_t parent_sid = IPC_GET_ARG1(*req);
+	fs_index_t parent_index = IPC_GET_ARG2(*req);
+	fs_index_t child_index = IPC_GET_ARG3(*req);
+	
+	char component[NAME_MAX + 1];
+	int rc = receive_fname(component);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
 		return;
 	}
-	
-	fs_node_t *fn;
-	int res = ops->node_get(&fn, mp_service_id, mp_fs_index);
-	if ((res != EOK) || (!fn)) {
-		async_hangup(mountee_sess);
-		async_data_write_void(combine_rc(res, ENOENT));
-		async_answer_0(rid, combine_rc(res, ENOENT));
+
+	fs_node_t *parent = NULL;
+	rc = ops->node_get(&parent, parent_sid, parent_index);
+	if (parent == NULL) {
+		async_answer_0(rid, rc == EOK ? EBADF : rc);
 		return;
 	}
 	
-	if (fn->mp_data.mp_active) {
-		async_hangup(mountee_sess);
-		(void) ops->node_put(fn);
-		async_data_write_void(EBUSY);
-		async_answer_0(rid, EBUSY);
+	fs_node_t *child = NULL;
+	rc = ops->node_get(&child, parent_sid, child_index);
+	if (child == NULL) {
+		async_answer_0(rid, rc == EOK ? EBADF : rc);
+		ops->node_put(parent);
 		return;
 	}
 	
-	async_exch_t *exch = async_exchange_begin(mountee_sess);
-	async_sess_t *sess = async_clone_establish(EXCHANGE_PARALLEL, exch);
-	
-	if (!sess) {
-		async_exchange_end(exch);
-		async_hangup(mountee_sess);
-		(void) ops->node_put(fn);
-		async_data_write_void(errno);
-		async_answer_0(rid, errno);
-		return;
-	}
-	
-	ipc_call_t answer;
-	int rc = async_data_write_forward_1_1(exch, VFS_OUT_MOUNTED,
-	    mr_service_id, &answer);
-	async_exchange_end(exch);
-	
-	if (rc == EOK) {
-		fn->mp_data.mp_active = true;
-		fn->mp_data.fs_handle = mr_fs_handle;
-		fn->mp_data.service_id = mr_service_id;
-		fn->mp_data.sess = mountee_sess;
-	}
-	
-	/*
-	 * Do not release the FS node so that it stays in memory.
-	 */
-	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)
-{
-	service_id_t mp_service_id = (service_id_t) IPC_GET_ARG1(*req);
-	fs_index_t mp_fs_index = (fs_index_t) IPC_GET_ARG2(*req);
-	fs_node_t *fn;
-	int res;
-
-	res = ops->node_get(&fn, mp_service_id, mp_fs_index);
-	if ((res != EOK) || (!fn)) {
-		async_answer_0(rid, combine_rc(res, ENOENT));
-		return;
-	}
-
-	/*
-	 * We are clearly expecting to find the mount point active.
-	 */
-	if (!fn->mp_data.mp_active) {
-		(void) ops->node_put(fn);
-		async_answer_0(rid, EINVAL);
-		return;
-	}
-
-	/*
-	 * Tell the mounted file system to unmount.
-	 */
-	async_exch_t *exch = async_exchange_begin(fn->mp_data.sess);
-	res = async_req_1_0(exch, VFS_OUT_UNMOUNTED, fn->mp_data.service_id);
-	async_exchange_end(exch);
-
-	/*
-	 * If everything went well, perform the clean-up on our side.
-	 */
-	if (res == EOK) {
-		async_hangup(fn->mp_data.sess);
-		fn->mp_data.mp_active = false;
-		fn->mp_data.fs_handle = 0;
-		fn->mp_data.service_id = 0;
-		fn->mp_data.sess = NULL;
-		
-		/* Drop the reference created in libfs_mount(). */
-		(void) ops->node_put(fn);
-	}
-
-	(void) ops->node_put(fn);
-	async_answer_0(rid, res);
-}
-
-static char plb_get_char(unsigned pos)
-{
-	return reg.plb_ro[pos % PLB_SIZE];
+	rc = ops->link(parent, child, component);
+	ops->node_put(parent);
+	ops->node_put(child);
+	async_answer_0(rid, rc);
 }
 
@@ -513,292 +541,219 @@
     ipc_call_t *req)
 {
-	unsigned int first = IPC_GET_ARG1(*req);
-	unsigned int last = IPC_GET_ARG2(*req);
-	unsigned int next = first;
+	unsigned first = IPC_GET_ARG1(*req);
+	unsigned len = IPC_GET_ARG2(*req);
 	service_id_t service_id = IPC_GET_ARG3(*req);
-	int lflag = IPC_GET_ARG4(*req);
-	fs_index_t index = IPC_GET_ARG5(*req);
+	fs_index_t index = IPC_GET_ARG4(*req);
+	int lflag = IPC_GET_ARG5(*req);
+	
+	assert((int) index != -1);
+	
+	DPRINTF("Entered libfs_lookup()\n");
+	
+	// TODO: Validate flags.
+	
+	unsigned next = first;
+	unsigned last = first + len;
+	
 	char component[NAME_MAX + 1];
-	int len;
 	int rc;
-	
-	if (last < next)
-		last += PLB_SIZE;
 	
 	fs_node_t *par = NULL;
 	fs_node_t *cur = NULL;
 	fs_node_t *tmp = NULL;
-	
-	rc = ops->root_get(&cur, service_id);
-	on_error(rc, goto out_with_answer);
-	
-	if (cur->mp_data.mp_active) {
-		async_exch_t *exch = async_exchange_begin(cur->mp_data.sess);
-		async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next, last,
-		    cur->mp_data.service_id, lflag, index,
-		    IPC_FF_ROUTE_FROM_ME);
-		async_exchange_end(exch);
+	unsigned clen = 0;
+	
+	rc = ops->node_get(&cur, service_id, index);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		LOG_EXIT(rc);
+		goto out;
+	}
+	
+	assert(cur != NULL);
+	
+	/* Find the file and its parent. */
+	
+	unsigned last_next = 0;
+	
+	while (next != last) {
+		if (cur == NULL) {
+			assert(par != NULL);
+			goto out1;
+		}
+
+		if (!ops->is_directory(cur)) {
+			async_answer_0(rid, ENOTDIR);
+			LOG_EXIT(ENOTDIR);
+			goto out;
+		}
 		
-		(void) ops->node_put(cur);
-		return;
-	}
-	
-	/* Eat slash */
-	if (plb_get_char(next) == '/')
-		next++;
-	
-	while (next <= last) {
-		bool has_children;
+		last_next = next;
+		/* Collect the component */
+		rc = plb_get_component(component, &clen, &next, last);
+		assert(rc != ERANGE);
+		if (rc != EOK) {
+			async_answer_0(rid, rc);
+			LOG_EXIT(rc);
+			goto out;
+		}
 		
-		rc = ops->has_children(&has_children, cur);
-		on_error(rc, goto out_with_answer);
-		if (!has_children)
-			break;
+		if (clen == 0) {
+			/* The path is just "/". */
+			break;
+		}
 		
-		/* Collect the component */
-		len = 0;
-		while ((next <= last) && (plb_get_char(next) != '/')) {
-			if (len + 1 == NAME_MAX) {
-				/* Component length overflow */
-				async_answer_0(rid, ENAMETOOLONG);
-				goto out;
-			}
-			component[len++] = plb_get_char(next);
-			/* Process next character */
-			next++;
-		}
-		
-		assert(len);
-		component[len] = '\0';
-		/* Eat slash */
-		next++;
+		assert(component[clen] == 0);
 		
 		/* Match the component */
 		rc = ops->match(&tmp, cur, component);
-		on_error(rc, goto out_with_answer);
+		if (rc != EOK) {
+			async_answer_0(rid, rc);
+			LOG_EXIT(rc);
+			goto out;
+		}
 		
-		/*
-		 * If the matching component is a mount point, there are two
-		 * legitimate semantics of the lookup operation. The first is
-		 * the commonly used one in which the lookup crosses each mount
-		 * point into the mounted file system. The second semantics is
-		 * used mostly during unmount() and differs from the first one
-		 * only in that the last mount point in the looked up path,
-		 * which is also its last component, is not crossed.
-		 */
-
-		if ((tmp) && (tmp->mp_data.mp_active) &&
-		    (!(lflag & L_MP) || (next <= last))) {
-			if (next > last)
-				next = last = first;
-			else
-				next--;
+		/* Descend one level */
+		if (par) {
+			rc = ops->node_put(par);
+			if (rc != EOK) {
+				async_answer_0(rid, rc);
+				LOG_EXIT(rc); 
+				goto out;
+			}
+		}
+		
+		par = cur;
+		cur = tmp;
+		tmp = NULL;
+	}
+	
+	/* At this point, par is either NULL or a directory.
+	 * If cur is NULL, the looked up file does not exist yet.
+	 */
+	 
+	assert(par == NULL || ops->is_directory(par));
+	assert(par != NULL || cur != NULL);
+	
+	/* Check for some error conditions. */
+	
+	if (cur && (lflag & L_FILE) && (ops->is_directory(cur))) {
+		async_answer_0(rid, EISDIR);
+		LOG_EXIT(EISDIR);
+		goto out;
+	}
+	
+	if (cur && (lflag & L_DIRECTORY) && (ops->is_file(cur))) {
+		async_answer_0(rid, ENOTDIR);
+		LOG_EXIT(ENOTDIR);
+		goto out;
+	}
+	
+	/* Unlink. */
+	
+	if (lflag & L_UNLINK) {
+		if (!cur) {
+			async_answer_0(rid, ENOENT);
+			LOG_EXIT(ENOENT);
+			goto out;
+		}
+		if (!par) {
+			async_answer_0(rid, EINVAL);
+			LOG_EXIT(EINVAL);
+			goto out;
+		}
+		
+		rc = ops->unlink(par, cur, component);
+		if (rc == EOK) {
+			int64_t size = ops->size_get(cur);
+			int32_t lsize = LOWER32(size);
+			if (lsize != size) {
+				lsize = -1;
+			}
 			
-			async_exch_t *exch = async_exchange_begin(tmp->mp_data.sess);
-			async_forward_slow(rid, exch, VFS_OUT_LOOKUP, next,
-			    last, tmp->mp_data.service_id, lflag, index,
-			    IPC_FF_ROUTE_FROM_ME);
-			async_exchange_end(exch);
-			
-			(void) ops->node_put(cur);
-			(void) ops->node_put(tmp);
-			if (par)
-				(void) ops->node_put(par);
-			return;
-		}
-		
-		/* Handle miss: match amongst siblings */
-		if (!tmp) {
-			if (next <= last) {
-				/* There are unprocessed components */
-				async_answer_0(rid, ENOENT);
+			async_answer_5(rid, fs_handle, service_id,
+			    ops->index_get(cur), last, lsize,
+			    ops->is_directory(cur));
+			LOG_EXIT(EOK);
+		} else {
+			async_answer_0(rid, rc);
+			LOG_EXIT(rc);
+		}
+		goto out;
+	}
+	
+	/* Create. */
+	
+	if (lflag & L_CREATE) {
+		if (cur && (lflag & L_EXCLUSIVE)) {
+			async_answer_0(rid, EEXIST);
+			LOG_EXIT(EEXIST);
+			goto out;
+		}
+	
+		if (!cur) {
+			rc = ops->create(&cur, service_id, lflag & (L_FILE|L_DIRECTORY));
+			if (rc != EOK) {
+				async_answer_0(rid, rc);
+				LOG_EXIT(rc);
+				goto out;
+			}
+			if (!cur) {
+				async_answer_0(rid, ENOSPC);
+				LOG_EXIT(ENOSPC);
 				goto out;
 			}
 			
-			/* Miss in the last component */
-			if (lflag & (L_CREATE | L_LINK)) {
-				/* Request to create a new link */
-				if (!ops->is_directory(cur)) {
-					async_answer_0(rid, ENOTDIR);
-					goto out;
-				}
-				
-				fs_node_t *fn;
-				if (lflag & L_CREATE)
-					rc = ops->create(&fn, service_id,
-					    lflag);
-				else
-					rc = ops->node_get(&fn, service_id,
-					    index);
-				on_error(rc, goto out_with_answer);
-				
-				if (fn) {
-					rc = ops->link(cur, fn, component);
-					if (rc != EOK) {
-						if (lflag & L_CREATE)
-							(void) ops->destroy(fn);
-						else
-							(void) ops->node_put(fn);
-						async_answer_0(rid, rc);
-					} else {
-						(void) ops->node_put(cur);
-						cur = fn;
-						goto out_with_answer;
-					}
-				} else
-					async_answer_0(rid, ENOSPC);
-				
+			rc = ops->link(par, cur, component);
+			if (rc != EOK) {
+				(void) ops->destroy(cur);
+				cur = NULL;
+				async_answer_0(rid, rc);
+				LOG_EXIT(rc);
 				goto out;
 			}
-			
-			async_answer_0(rid, ENOENT);
+		}
+	}
+	
+	/* Return. */
+out1:
+	if (!cur) {
+		async_answer_5(rid, fs_handle, service_id,
+			ops->index_get(par), last_next, -1, true);
+		LOG_EXIT(EOK);
+		goto out;
+	}
+	
+	if (lflag & L_OPEN) {
+		rc = ops->node_open(cur);
+		if (rc != EOK) {
+			async_answer_0(rid, rc);
+			LOG_EXIT(rc);
 			goto out;
 		}
-		
-		if (par) {
-			rc = ops->node_put(par);
-			on_error(rc, goto out_with_answer);
-		}
-		
-		/* Descend one level */
-		par = cur;
-		cur = tmp;
-		tmp = NULL;
-	}
-	
-	/* Handle miss: excessive components */
-	if (next <= last) {
-		bool has_children;
-		rc = ops->has_children(&has_children, cur);
-		on_error(rc, goto out_with_answer);
-		
-		if (has_children)
-			goto skip_miss;
-		
-		if (lflag & (L_CREATE | L_LINK)) {
-			if (!ops->is_directory(cur)) {
-				async_answer_0(rid, ENOTDIR);
-				goto out;
-			}
-			
-			/* Collect next component */
-			len = 0;
-			while (next <= last) {
-				if (plb_get_char(next) == '/') {
-					/* More than one component */
-					async_answer_0(rid, ENOENT);
-					goto out;
-				}
-				
-				if (len + 1 == NAME_MAX) {
-					/* Component length overflow */
-					async_answer_0(rid, ENAMETOOLONG);
-					goto out;
-				}
-				
-				component[len++] = plb_get_char(next);
-				/* Process next character */
-				next++;
-			}
-			
-			assert(len);
-			component[len] = '\0';
-			
-			fs_node_t *fn;
-			if (lflag & L_CREATE)
-				rc = ops->create(&fn, service_id, lflag);
-			else
-				rc = ops->node_get(&fn, service_id, index);
-			on_error(rc, goto out_with_answer);
-			
-			if (fn) {
-				rc = ops->link(cur, fn, component);
-				if (rc != EOK) {
-					if (lflag & L_CREATE)
-						(void) ops->destroy(fn);
-					else
-						(void) ops->node_put(fn);
-					async_answer_0(rid, rc);
-				} else {
-					(void) ops->node_put(cur);
-					cur = fn;
-					goto out_with_answer;
-				}
-			} else
-				async_answer_0(rid, ENOSPC);
-			
-			goto out;
-		}
-		
-		async_answer_0(rid, ENOENT);
-		goto out;
-	}
-	
-skip_miss:
-	
-	/* Handle hit */
-	if (lflag & L_UNLINK) {
-		unsigned int old_lnkcnt = ops->lnkcnt_get(cur);
-		rc = ops->unlink(par, cur, component);
-		
-		if (rc == EOK) {
-			aoff64_t size = ops->size_get(cur);
-			async_answer_5(rid, fs_handle, service_id,
-			    ops->index_get(cur), LOWER32(size), UPPER32(size),
-			    old_lnkcnt);
-		} else
-			async_answer_0(rid, rc);
-		
-		goto out;
-	}
-	
-	if (((lflag & (L_CREATE | L_EXCLUSIVE)) == (L_CREATE | L_EXCLUSIVE)) ||
-	    (lflag & L_LINK)) {
-		async_answer_0(rid, EEXIST);
-		goto out;
-	}
-	
-	if ((lflag & L_FILE) && (ops->is_directory(cur))) {
-		async_answer_0(rid, EISDIR);
-		goto out;
-	}
-	
-	if ((lflag & L_DIRECTORY) && (ops->is_file(cur))) {
-		async_answer_0(rid, ENOTDIR);
-		goto out;
-	}
-
-	if ((lflag & L_ROOT) && par) {
-		async_answer_0(rid, EINVAL);
-		goto out;
-	}
-	
-out_with_answer:
-	
-	if (rc == EOK) {
-		if (lflag & L_OPEN)
-			rc = ops->node_open(cur);
-		
-		if (rc == EOK) {
-			aoff64_t size = ops->size_get(cur);
-			async_answer_5(rid, fs_handle, service_id,
-			    ops->index_get(cur), LOWER32(size), UPPER32(size),
-			    ops->lnkcnt_get(cur));
-		} else
-			async_answer_0(rid, rc);
-		
-	} else
-		async_answer_0(rid, rc);
-	
+	}
+	
+	int64_t size = ops->size_get(cur);
+	int32_t lsize = LOWER32(size);
+	if (lsize != size) {
+		lsize = -1;
+	}
+	
+	async_answer_5(rid, fs_handle, service_id, ops->index_get(cur), last,
+	    lsize, ops->is_directory(cur));
+	
+	LOG_EXIT(EOK);
 out:
-	
-	if (par)
+	if (par) {
 		(void) ops->node_put(par);
-	
-	if (cur)
+	}
+	
+	if (cur) {
 		(void) ops->node_put(cur);
-	
-	if (tmp)
+	}
+	
+	if (tmp) {
 		(void) ops->node_put(tmp);
+	}
 }
 
Index: uspace/lib/fs/libfs.h
===================================================================
--- uspace/lib/fs/libfs.h	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/lib/fs/libfs.h	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -56,12 +56,4 @@
 
 typedef struct {
-	bool mp_active;
-	async_sess_t *sess;
-	fs_handle_t fs_handle;
-	service_id_t service_id;
-} mp_data_t;
-
-typedef struct {
-	mp_data_t mp_data;  /**< Mount point info. */
 	void *data;         /**< Data of the file system implementation. */
 } fs_node_t;
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -104,6 +104,9 @@
 			vfs_unmount_srv(callid, &call);
 			break;
-		case VFS_IN_OPEN:
-			vfs_open(callid, &call);
+		case VFS_IN_WALK:
+			vfs_walk(callid, &call);
+			break;
+		case VFS_IN_OPEN2:
+			vfs_open2(callid, &call);
 			break;
 		case VFS_IN_CLOSE:
@@ -125,12 +128,6 @@
 			vfs_fstat(callid, &call);
 			break;
-		case VFS_IN_STAT:
-			vfs_stat(callid, &call);
-			break;
-		case VFS_IN_MKDIR:
-			vfs_mkdir(callid, &call);
-			break;
-		case VFS_IN_UNLINK:
-			vfs_unlink(callid, &call);
+		case VFS_IN_UNLINK2:
+			vfs_unlink2(callid, &call);
 			break;
 		case VFS_IN_RENAME:
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs.h	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -94,5 +94,4 @@
 	vfs_node_type_t type;
 	aoff64_t size;
-	unsigned int lnkcnt;
 } vfs_lookup_res_t;
 
@@ -101,5 +100,5 @@
  * which may be associated with it.
  */
-typedef struct {
+typedef struct _vfs_node {
 	VFS_TRIPLET;		/**< Identity of the node. */
 
@@ -110,12 +109,9 @@
 	unsigned refcnt;
 	
-	/** Number of names this node has in the file system namespace. */
-	unsigned lnkcnt;
-
 	ht_link_t nh_link;		/**< Node hash-table link. */
 
 	vfs_node_type_t type;	/**< Partial info about the node type. */
 
-	aoff64_t size;		/**< Cached size if the node is a file. */
+	int64_t size;		/**< Cached size if the node is a file. */
 
 	/**
@@ -123,4 +119,6 @@
 	 */
 	fibril_rwlock_t contents_rwlock;
+	
+	struct _vfs_node *mount;
 } vfs_node_t;
 
@@ -138,4 +136,8 @@
 	unsigned refcnt;
 
+	int permissions;
+	bool open_read;
+	bool open_write;
+
 	/** Append on write. */
 	bool append;
@@ -176,13 +178,16 @@
 extern vfs_info_t *fs_handle_to_info(fs_handle_t);
 
-extern int vfs_lookup_internal(char *, int, vfs_lookup_res_t *,
-    vfs_pair_t *, ...);
+extern int vfs_lookup_internal(vfs_node_t *, char *, int, vfs_lookup_res_t *);
+extern int vfs_link_internal(vfs_node_t *, char *, vfs_triplet_t *);
 
 extern bool vfs_nodes_init(void);
 extern vfs_node_t *vfs_node_get(vfs_lookup_res_t *);
+extern vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result);
 extern void vfs_node_put(vfs_node_t *);
 extern void vfs_node_forget(vfs_node_t *);
 extern unsigned vfs_nodes_refcount_sum_get(fs_handle_t, service_id_t);
 
+extern int64_t vfs_node_get_size(vfs_node_t *node);
+extern bool vfs_node_has_children(vfs_node_t *node);
 
 #define MAX_OPEN_FILES	128
@@ -207,5 +212,4 @@
 extern void vfs_mount_srv(ipc_callid_t, ipc_call_t *);
 extern void vfs_unmount_srv(ipc_callid_t, ipc_call_t *);
-extern void vfs_open(ipc_callid_t, ipc_call_t *);
 extern void vfs_sync(ipc_callid_t, ipc_call_t *);
 extern void vfs_dup(ipc_callid_t, ipc_call_t *);
@@ -216,7 +220,4 @@
 extern void vfs_truncate(ipc_callid_t, ipc_call_t *);
 extern void vfs_fstat(ipc_callid_t, ipc_call_t *);
-extern void vfs_stat(ipc_callid_t, ipc_call_t *);
-extern void vfs_mkdir(ipc_callid_t, ipc_call_t *);
-extern void vfs_unlink(ipc_callid_t, ipc_call_t *);
 extern void vfs_rename(ipc_callid_t, ipc_call_t *);
 extern void vfs_wait_handle(ipc_callid_t, ipc_call_t *);
@@ -233,4 +234,8 @@
 extern int vfs_rdwr_internal(int, bool, rdwr_io_chunk_t *);
 
+extern void vfs_walk(ipc_callid_t, ipc_call_t *);
+extern void vfs_open2(ipc_callid_t, ipc_call_t *);
+extern void vfs_unlink2(ipc_callid_t, ipc_call_t *);
+
 #endif
 
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs_file.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -177,5 +177,7 @@
 		 * endpoint FS and drop our reference to the underlying VFS node.
 		 */
-		rc = vfs_file_close_remote(file);
+		if (file->open_read || file->open_write) {
+			rc = vfs_file_close_remote(file);
+		}
 		vfs_node_delref(file->node);
 		free(file);
@@ -393,6 +395,11 @@
 	 */
 	acceptor_file->node = donor_file->node;
+	acceptor_file->permissions = donor_file->permissions;
+	
+	// TODO: The file should not inherit its open status, but clients depend on this.
+	acceptor_file->pos = donor_file->pos;
 	acceptor_file->append = donor_file->append;
-	acceptor_file->pos = donor_file->pos;
+	acceptor_file->open_read = donor_file->open_read;
+	acceptor_file->open_write = donor_file->open_write;
 
 out:
Index: uspace/srv/vfs/vfs_lookup.c
===================================================================
--- uspace/srv/vfs/vfs_lookup.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs_lookup.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -46,4 +46,8 @@
 #include <adt/list.h>
 #include <vfs/canonify.h>
+#include <dirent.h>
+#include <assert.h>
+
+#define DPRINTF(...)
 
 #define min(a, b)  ((a) < (b) ? (a) : (b))
@@ -53,49 +57,10 @@
 uint8_t *plb = NULL;
 
-/** Perform a path lookup.
- *
- * @param path    Path to be resolved; it must be a NULL-terminated
- *                string.
- * @param lflag   Flags to be used during lookup.
- * @param result  Empty structure where the lookup result will be stored.
- *                Can be NULL.
- * @param altroot If non-empty, will be used instead of rootfs as the root
- *                of the whole VFS tree.
- *
- * @return EOK on success or an error code from errno.h.
- *
- */
-int vfs_lookup_internal(char *path, int lflag, vfs_lookup_res_t *result,
-    vfs_pair_t *altroot, ...)
-{
-	vfs_pair_t *root;
-
-	if (altroot)
-		root = altroot;
-	else
-		root = &rootfs;
-
-	if (!root->fs_handle)
-		return ENOENT;
-	
-	size_t len;
-	path = canonify(path, &len);
-	if (!path)
-		return EINVAL;
-	
-	fs_index_t index = 0;
-	if (lflag & L_LINK) {
-		va_list ap;
-
-		va_start(ap, altroot);
-		index = va_arg(ap, fs_index_t);
-		va_end(ap);
-	}
-	
+static int plb_insert_entry(plb_entry_t *entry, char *path, size_t *start, size_t len)
+{
 	fibril_mutex_lock(&plb_mutex);
 
-	plb_entry_t entry;
-	link_initialize(&entry.plb_link);
-	entry.len = len;
+	link_initialize(&entry->plb_link);
+	entry->len = len;
 
 	size_t first;	/* the first free index */
@@ -138,6 +103,6 @@
 	 */
 
-	entry.index = first;
-	entry.len = len;
+	entry->index = first;
+	entry->len = len;
 
 	/*
@@ -145,5 +110,5 @@
 	 * buffer.
 	 */
-	list_append(&entry.plb_link, &plb_entries);
+	list_append(&entry->plb_link, &plb_entries);
 	
 	fibril_mutex_unlock(&plb_mutex);
@@ -158,45 +123,232 @@
 	memcpy(plb, &path[cnt1], cnt2);
 
-	ipc_call_t answer;
-	async_exch_t *exch = vfs_exchange_grab(root->fs_handle);
-	aid_t req = async_send_5(exch, VFS_OUT_LOOKUP, (sysarg_t) first,
-	    (sysarg_t) (first + len - 1) % PLB_SIZE,
-	    (sysarg_t) root->service_id, (sysarg_t) lflag, (sysarg_t) index,
-	    &answer);
-	
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	vfs_exchange_release(exch);
-	
+	*start = first;
+	return EOK;
+}
+
+static void plb_clear_entry(plb_entry_t *entry, size_t first, size_t len)
+{
 	fibril_mutex_lock(&plb_mutex);
-	list_remove(&entry.plb_link);
+	list_remove(&entry->plb_link);
 	/*
 	 * Erasing the path from PLB will come handy for debugging purposes.
 	 */
+	size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
+	size_t cnt2 = len - cnt1;
 	memset(&plb[first], 0, cnt1);
 	memset(plb, 0, cnt2);
 	fibril_mutex_unlock(&plb_mutex);
-	
-	if ((int) rc < EOK)
+}
+
+static char *_strrchr(char *path, int c)
+{
+	char *res = NULL;
+	while (*path != 0) {
+		if (*path == c) {
+			res = path;
+		}
+		path++;
+	}
+	return res;
+}
+
+int vfs_link_internal(vfs_node_t *base, char *path, vfs_triplet_t *child)
+{
+	assert(base != NULL);
+	assert(child != NULL);
+	assert(base->fs_handle);
+	assert(child->fs_handle);
+	assert(path != NULL);
+	
+	vfs_lookup_res_t res;
+	char component[NAME_MAX + 1];
+	int rc;
+	
+	size_t len;
+	char *npath = canonify(path, &len);
+	if (!npath) {
+		rc = EINVAL;
+		goto out;
+	}
+	path = npath;
+	
+	vfs_triplet_t *triplet;
+	
+	char *slash = _strrchr(path, '/');
+	if (slash && slash != path) {
+		if (slash[1] == 0) {
+			rc = EINVAL;
+			goto out;
+		}
+		
+		memcpy(component, slash + 1, str_size(slash));
+		*slash = 0;
+		
+		rc = vfs_lookup_internal(base, path, L_DIRECTORY, &res);
+		if (rc != EOK) {
+			goto out;
+		}
+		triplet = &res.triplet;
+		
+		*slash = '/';
+	} else {
+		if (base->mount != NULL) {
+			rc = EINVAL;
+			goto out;
+		}
+		
+		memcpy(component, path + 1, str_size(path));
+		triplet = (vfs_triplet_t *) base;
+	}
+	
+	if (triplet->fs_handle != child->fs_handle || triplet->service_id != child->service_id) {
+		rc = EXDEV;
+		goto out;
+	}
+	
+	async_exch_t *exch = vfs_exchange_grab(triplet->fs_handle);
+	aid_t req = async_send_3(exch, VFS_OUT_LINK, triplet->service_id, triplet->index, child->index, NULL);
+	
+	rc = async_data_write_start(exch, component, str_size(component) + 1);
+	sysarg_t orig_rc;
+	async_wait_for(req, &orig_rc);
+	vfs_exchange_release(exch);
+	if (orig_rc != EOK) {
+		rc = orig_rc;
+	}
+	
+out:
+	DPRINTF("vfs_link_internal() with path '%s' returns %d\n", path, rc);
+	return rc;
+}
+
+static int out_lookup(vfs_triplet_t *base, size_t *pfirst, size_t *plen,
+	int lflag, vfs_lookup_res_t *result)
+{
+	assert(base);
+	assert(result);
+	
+	sysarg_t rc;
+	ipc_call_t answer;
+	async_exch_t *exch = vfs_exchange_grab(base->fs_handle);
+	aid_t req = async_send_5(exch, VFS_OUT_LOOKUP, (sysarg_t) *pfirst, (sysarg_t) *plen,
+	    (sysarg_t) base->service_id, (sysarg_t) base->index, (sysarg_t) lflag, &answer);
+	async_wait_for(req, &rc);
+	vfs_exchange_release(exch);
+	
+	if ((int) rc < 0) {
 		return (int) rc;
-
-	if (!result)
-		return EOK;
+	}
+	
+	unsigned last = *pfirst + *plen;
+	*pfirst = IPC_GET_ARG3(answer);
+	*plen = last - *pfirst;
 	
 	result->triplet.fs_handle = (fs_handle_t) rc;
 	result->triplet.service_id = (service_id_t) IPC_GET_ARG1(answer);
 	result->triplet.index = (fs_index_t) IPC_GET_ARG2(answer);
-	result->size =
-	    (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(answer), IPC_GET_ARG4(answer));
-	result->lnkcnt = (unsigned int) IPC_GET_ARG5(answer);
-	
-	if (lflag & L_FILE)
-		result->type = VFS_NODE_FILE;
-	else if (lflag & L_DIRECTORY)
-		result->type = VFS_NODE_DIRECTORY;
-	else
-		result->type = VFS_NODE_UNKNOWN;
-	
+	result->size = (int64_t)(int32_t) IPC_GET_ARG4(answer);
+	result->type = IPC_GET_ARG5(answer) ? VFS_NODE_DIRECTORY : VFS_NODE_FILE;
 	return EOK;
+}
+
+/** Perform a path lookup.
+ *
+ * @param base    The file from which to perform the lookup.
+ * @param path    Path to be resolved; it must be a NULL-terminated
+ *                string.
+ * @param lflag   Flags to be used during lookup.
+ * @param result  Empty structure where the lookup result will be stored.
+ *                Can be NULL.
+ *
+ * @return EOK on success or an error code from errno.h.
+ *
+ */
+int vfs_lookup_internal(vfs_node_t *base, char *path, int lflag, vfs_lookup_res_t *result)
+{
+	assert(base != NULL);
+	assert(path != NULL);
+	
+	size_t len;
+	int rc;
+	char *npath = canonify(path, &len);
+	if (!npath) {
+		DPRINTF("vfs_lookup_internal() can't canonify path: %s\n", path);
+		rc = EINVAL;
+		return rc;
+	}
+	path = npath;
+	
+	assert(path[0] == '/');
+	
+	size_t first;
+	
+	plb_entry_t entry;
+	rc = plb_insert_entry(&entry, path, &first, len);
+	if (rc != EOK) {
+		DPRINTF("vfs_lookup_internal() can't insert entry into PLB: %d\n", rc);
+		return rc;
+	}
+	
+	size_t next = first;
+	size_t nlen = len;
+	
+	vfs_lookup_res_t res;
+	
+	/* Resolve path as long as there are mount points to cross. */
+	while (nlen > 0) {
+		while (base->mount != NULL) {
+			if (lflag & L_DISABLE_MOUNTS) {
+				rc = EXDEV;
+				goto out;
+			}
+			
+			base = base->mount;
+		}
+		
+		rc = out_lookup((vfs_triplet_t *) base, &next, &nlen, lflag, &res);
+		if (rc != EOK) {
+			goto out;
+		}
+		
+		if (nlen > 0) {
+			base = vfs_node_peek(&res);
+			if (base == NULL || base->mount == NULL) {
+				rc = ENOENT;
+				goto out;
+			}
+			if (lflag & L_DISABLE_MOUNTS) {
+				rc = EXDEV;
+				goto out;
+			}
+		}
+	}
+	
+	assert(nlen == 0);
+	rc = EOK;
+	
+	if (result != NULL) {
+		/* The found file may be a mount point. Try to cross it. */
+		if (!(lflag & L_MP)) {
+			base = vfs_node_peek(&res);
+			if (base != NULL && base->mount != NULL) {
+				while (base->mount != NULL) {
+					base = base->mount;
+				}
+				
+				result->triplet = *(vfs_triplet_t *)base;
+				result->type = base->type;
+				result->size = base->size;				
+				goto out;
+			}
+		}
+
+		memcpy(result, &res, sizeof(vfs_lookup_res_t));
+	}
+	
+out:
+	plb_clear_entry(&entry, first, len);
+	DPRINTF("vfs_lookup_internal() with path '%s' returns %d\n", path, rc);
+	return rc;
 }
 
Index: uspace/srv/vfs/vfs_node.c
===================================================================
--- uspace/srv/vfs/vfs_node.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs_node.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -45,4 +45,5 @@
 #include <async.h>
 #include <errno.h>
+#include <macros.h>
 
 /** Mutex protecting the VFS node hash table. */
@@ -106,10 +107,10 @@
 void vfs_node_delref(vfs_node_t *node)
 {
-	bool free_vfs_node = false;
-	bool free_fs_node = false;
-	
-	fibril_mutex_lock(&nodes_mutex);
-	
-	if (node->refcnt-- == 1) {
+	bool free_node = false;
+	
+	fibril_mutex_lock(&nodes_mutex);
+	
+	node->refcnt--;
+	if (node->refcnt == 0) {
 		
 		/*
@@ -119,29 +120,21 @@
 		
 		hash_table_remove_item(&nodes, &node->nh_link);
-		free_vfs_node = true;
-		
-		if (!node->lnkcnt)
-			free_fs_node = true;
-	}
-	
-	fibril_mutex_unlock(&nodes_mutex);
-	
-	if (free_fs_node) {
-		
+		free_node = true;
+	}
+	
+	fibril_mutex_unlock(&nodes_mutex);
+	
+	if (free_node) {
 		/*
-		 * The node is not visible in the file system namespace.
-		 * Free up its resources.
+		 * DESTROY will free up the file's resources if there are no more hard links.
 		 */
 		
 		async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
-		sysarg_t rc = async_req_2_0(exch, VFS_OUT_DESTROY,
-		    (sysarg_t) node->service_id, (sysarg_t)node->index);
-		
-		assert(rc == EOK);
+		async_msg_2(exch, VFS_OUT_DESTROY,
+			(sysarg_t) node->service_id, (sysarg_t)node->index);
 		vfs_exchange_release(exch);
-	}
-	
-	if (free_vfs_node)
+
 		free(node);
+	}
 }
 
@@ -190,5 +183,4 @@
 		node->index = result->triplet.index;
 		node->size = result->size;
-		node->lnkcnt = result->lnkcnt;
 		node->type = result->type;
 		fibril_rwlock_initialize(&node->contents_rwlock);
@@ -196,16 +188,21 @@
 	} else {
 		node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
-		if (node->type == VFS_NODE_UNKNOWN &&
-		    result->type != VFS_NODE_UNKNOWN) {
-			/* Upgrade the node type. */
-			node->type = result->type;
-		}
-	}
-
-	assert(node->size == result->size || node->type != VFS_NODE_FILE);
-	assert(node->lnkcnt == result->lnkcnt);
-	assert(node->type == result->type || result->type == VFS_NODE_UNKNOWN);
+	}
 
 	_vfs_node_addref(node);
+	fibril_mutex_unlock(&nodes_mutex);
+
+	return node;
+}
+
+vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result)
+{
+	vfs_node_t *node = NULL;
+
+	fibril_mutex_lock(&nodes_mutex);
+	ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
+	if (tmp) {
+		node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
+	}
 	fibril_mutex_unlock(&nodes_mutex);
 
@@ -318,4 +315,28 @@
 }
 
+int64_t vfs_node_get_size(vfs_node_t *node)
+{
+	if (node->size == -1) {
+		sysarg_t sz1 = 0;
+		sysarg_t sz2 = 0;
+		
+		async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+		(void) async_req_2_2(exch, VFS_OUT_GET_SIZE,
+			node->service_id, node->index, &sz1, &sz2);
+		vfs_exchange_release(exch);
+		
+		node->size = MERGE_LOUP32(sz1, sz2);
+	}
+	return node->size;
+}
+
+bool vfs_node_has_children(vfs_node_t *node)
+{
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	int rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id, node->index);
+	vfs_exchange_release(exch);
+	return rc == ENOTEMPTY;
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision 5b46ec8b4cb1d1075e4c18e9f366fca913758c8c)
+++ uspace/srv/vfs/vfs_ops.c	(revision 1dff985eeea7ec928487e2814a82eb105072a35a)
@@ -68,206 +68,126 @@
 FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
 
-vfs_pair_t rootfs = {
-	.fs_handle = 0,
-	.service_id = 0
-};
-
-static int vfs_mount_internal(ipc_callid_t rid, service_id_t service_id,
-    fs_handle_t fs_handle, char *mp, char *opts)
-{
-	vfs_lookup_res_t mp_res;
-	vfs_lookup_res_t mr_res;
-	vfs_node_t *mp_node = NULL;
-	vfs_node_t *mr_node;
-	fs_index_t rindex;
-	aoff64_t rsize;
-	unsigned rlnkcnt;
-	async_exch_t *exch;
-	sysarg_t rc;
-	aid_t msg;
+vfs_node_t *root = NULL;
+
+static int vfs_connect_internal(service_id_t service_id, unsigned flags, unsigned instance,
+	char *options, char *fsname, vfs_node_t **root)
+{
+	fs_handle_t fs_handle = 0;
+	
+	fibril_mutex_lock(&fs_list_lock);
+	while (1) {
+		fs_handle = fs_name_to_handle(instance, fsname, false);
+		
+		if (fs_handle != 0 || !(flags & IPC_FLAG_BLOCKING)) {
+			break;
+		}
+		
+		fibril_condvar_wait(&fs_list_cv, &fs_list_lock);
+	}
+	fibril_mutex_unlock(&fs_list_lock);
+
+	if (fs_handle == 0) {
+		return ENOENT;
+	}
+	
+	/* Tell the mountee that it is being mounted. */
 	ipc_call_t answer;
-	
+	async_exch_t *exch = vfs_exchange_grab(fs_handle);
+	aid_t msg = async_send_1(exch, VFS_OUT_MOUNTED, (sysarg_t) service_id, &answer);
+	/* Send the mount options */
+	sysarg_t rc = async_data_write_start(exch, options, str_size(options));
+	if (rc != EOK) {
+		async_forget(msg);
+		vfs_exchange_release(exch);
+		return rc;
+	}
+	async_wait_for(msg, &rc);
+	vfs_exchange_release(exch);
+	
+	if (rc != EOK) {
+		return rc;
+	}
+	
+	vfs_lookup_res_t res;
+	res.triplet.fs_handle = fs_handle;
+	res.triplet.service_id = service_id;
+	res.triplet.index = (fs_index_t) IPC_GET_ARG1(answer);
+	res.size = (int64_t) MERGE_LOUP32(IPC_GET_ARG2(answer), IPC_GET_ARG3(answer));
+	res.type = VFS_NODE_DIRECTORY;
+	
+	/* Add reference to the mounted root. */
+	*root = vfs_node_get(&res); 
+	assert(*root);
+			
+	return EOK;
+}
+
+static int vfs_mount_internal(service_id_t service_id, unsigned flags, unsigned instance,
+	char *opts, char *fs_name, char *mp)
+{
 	/* Resolve the path to the mountpoint. */
-	fibril_rwlock_write_lock(&namespace_rwlock);
-	if (rootfs.fs_handle) {
-		/* We already have the root FS. */
-		if (str_cmp(mp, "/") == 0) {
-			/* Trying to mount root FS over root FS */
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			async_answer_0(rid, EBUSY);
-			return EBUSY;
-		}
-		
-		rc = vfs_lookup_internal(mp, L_MP, &mp_res, NULL);
-		if (rc != EOK) {
-			/* The lookup failed for some reason. */
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			async_answer_0(rid, rc);
-			return rc;
-		}
-		
-		mp_node = vfs_node_get(&mp_res);
-		if (!mp_node) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			async_answer_0(rid, ENOMEM);
-			return ENOMEM;
-		}
-		
-		/*
-		 * Now we hold a reference to mp_node.
-		 * It will be dropped upon the corresponding VFS_IN_UNMOUNT.
-		 * This prevents the mount point from being deleted.
-		 */
-	} else {
+	
+	if (root == NULL) {
 		/* We still don't have the root file system mounted. */
-		if (str_cmp(mp, "/") == 0) {
-			/*
-			 * For this simple, but important case,
-			 * we are almost done.
-			 */
-			
-			/* Tell the mountee that it is being mounted. */
-			exch = vfs_exchange_grab(fs_handle);
-			msg = async_send_1(exch, VFS_OUT_MOUNTED,
-			    (sysarg_t) service_id, &answer);
-			/* Send the mount options */
-			rc = async_data_write_start(exch, (void *)opts,
-			    str_size(opts));
-			vfs_exchange_release(exch);
-			
-			if (rc != EOK) {
-				async_forget(msg);
-				fibril_rwlock_write_unlock(&namespace_rwlock);
-				async_answer_0(rid, rc);
-				return rc;
-			}
-			async_wait_for(msg, &rc);
-			
-			if (rc != EOK) {
-				fibril_rwlock_write_unlock(&namespace_rwlock);
-				async_answer_0(rid, rc);
-				return rc;
-			}
-
-			rindex = (fs_index_t) IPC_GET_ARG1(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;
-			mr_res.triplet.service_id = service_id;
-			mr_res.triplet.index = rindex;
-			mr_res.size = rsize;
-			mr_res.lnkcnt = rlnkcnt;
-			mr_res.type = VFS_NODE_DIRECTORY;
-			
-			rootfs.fs_handle = fs_handle;
-			rootfs.service_id = service_id;
-			
-			/* Add reference to the mounted root. */
-			mr_node = vfs_node_get(&mr_res); 
-			assert(mr_node);
-			
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			async_answer_0(rid, rc);
-			return rc;
-		} else {
+		if (str_cmp(mp, "/") != 0) {
 			/*
 			 * We can't resolve this without the root filesystem
 			 * being mounted first.
 			 */
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			async_answer_0(rid, ENOENT);
 			return ENOENT;
 		}
-	}
-	
-	/*
-	 * At this point, we have all necessary pieces: file system handle
-	 * and service ID, and we know the mount point VFS node.
-	 */
-	
-	async_exch_t *mountee_exch = vfs_exchange_grab(fs_handle);
-	assert(mountee_exch);
-	
-	exch = vfs_exchange_grab(mp_res.triplet.fs_handle);
-	msg = async_send_4(exch, VFS_OUT_MOUNT,
-	    (sysarg_t) mp_res.triplet.service_id,
-	    (sysarg_t) mp_res.triplet.index,
-	    (sysarg_t) fs_handle,
-	    (sysarg_t) service_id, &answer);
-	
-	/* Send connection */
-	rc = async_exchange_clone(exch, mountee_exch);
-	vfs_exchange_release(mountee_exch);
-	
-	if (rc != EOK) {
-		vfs_exchange_release(exch);
-		async_forget(msg);
-		
-		/* Mount failed, drop reference to mp_node. */
-		if (mp_node)
-			vfs_node_put(mp_node);
-		
-		async_answer_0(rid, rc);
-		fibril_rwlock_write_unlock(&namespace_rwlock);
+		
+		return vfs_connect_internal(service_id, flags, instance, opts, fs_name, &root);
+	}
+	
+	/* We already have the root FS. */
+	if (str_cmp(mp, "/") == 0) {
+		/* Trying to mount root FS over root FS */
+		return EBUSY;
+	}
+	
+	vfs_lookup_res_t mp_res;
+	int rc = vfs_lookup_internal(root, mp, L_DIRECTORY, &mp_res);
+	if (rc != EOK) {
+		/* The lookup failed. */
 		return rc;
 	}
 	
-	/* send the mount options */
-	rc = async_data_write_start(exch, (void *) opts, str_size(opts));
-	if (rc != EOK) {
-		vfs_exchange_release(exch);
-		async_forget(msg);
-		
-		/* Mount failed, drop reference to mp_node. */
-		if (mp_node)
-			vfs_node_put(mp_node);
-		
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		return rc;
-	}
-	
-	/*
-	 * Wait for the answer before releasing the exchange to avoid deadlock
-	 * in case the answer depends on further calls to the same file system.
-	 * Think of a case when mounting a FS on a file_bd backed by a file on
-	 * the same FS. 
-	 */
-	async_wait_for(msg, &rc);
-	vfs_exchange_release(exch);
-	
-	if (rc == EOK) {
-		rindex = (fs_index_t) IPC_GET_ARG1(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;
-		mr_res.triplet.service_id = service_id;
-		mr_res.triplet.index = rindex;
-		mr_res.size = rsize;
-		mr_res.lnkcnt = rlnkcnt;
-		mr_res.type = VFS_NODE_DIRECTORY;
-		
-		/* Add reference to the mounted root. */
-		mr_node = vfs_node_get(&mr_res); 
-		assert(mr_node);
-	} else {
-		/* Mount failed, drop reference to mp_node. */
-		if (mp_node)
-			vfs_node_put(mp_node);
-	}
-	
-	async_answer_0(rid, rc);
-	fibril_rwlock_write_unlock(&namespace_rwlock);
-	return rc;
+	vfs_node_t *mp_node;
+	mp_node = vfs_node_get(&mp_res);
+	if (!mp_node) {
+		return ENOMEM;
+	}
+	
+	if (mp_node->mount != NULL) {
+		return EBUSY;
+	}
+	
+	if (mp_node->type != VFS_NODE_DIRECTORY) {
+		printf("%s node not a directory, type=%d\n", mp, mp_node->type);
+		return ENOTDIR;
+	}
+	
+	if (vfs_node_has_children(mp_node)) {
+		return ENOTEMPTY;
+	}
+	
+	vfs_node_t *mountee;
+	
+	rc = vfs_connect_internal(service_id, flags, instance, opts, fs_name, &mountee);
+	if (rc != EOK) {
+		vfs_node_put(mp_node);
+		return ENOMEM;
+	}
+	
+	mp_node->mount = mountee;
+	/* The two references to nodes are held by the mount so that they cannot be freed.
+	 * They are removed in detach_internal().
+	 */
+	return EOK;
 }
 
 void vfs_mount_srv(ipc_callid_t rid, ipc_call_t *request)
 {
-	service_id_t service_id;
-
 	/*
 	 * We expect the library to do the device-name to device-handle
@@ -275,5 +195,5 @@
 	 * in the request.
 	 */
-	service_id = (service_id_t) IPC_GET_ARG1(*request);
+	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*request);
 	
 	/*
@@ -301,6 +221,6 @@
 	    0, NULL);
 	if (rc != EOK) {
+		async_answer_0(rid, rc);
 		free(mp);
-		async_answer_0(rid, rc);
 		return;
 	}
@@ -314,53 +234,13 @@
 	    FS_NAME_MAXLEN, 0, NULL);
 	if (rc != EOK) {
+		async_answer_0(rid, rc);
 		free(mp);
 		free(opts);
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	/*
-	 * Wait for VFS_IN_PING so that we can return an error if we don't know
-	 * fs_name.
-	 */
-	ipc_call_t data;
-	ipc_callid_t callid = async_get_call(&data);
-	if (IPC_GET_IMETHOD(data) != VFS_IN_PING) {
-		async_answer_0(callid, ENOTSUP);
-		async_answer_0(rid, ENOTSUP);
-		free(mp);
-		free(opts);
-		free(fs_name);
-		return;
-	}
-
-	/*
-	 * Check if we know a file system with the same name as is in fs_name.
-	 * This will also give us its file system handle.
-	 */
-	fibril_mutex_lock(&fs_list_lock);
-	fs_handle_t fs_handle;
-recheck:
-	fs_handle = fs_name_to_handle(instance, fs_name, false);
-	if (!fs_handle) {
-		if (flags & IPC_FLAG_BLOCKING) {
-			fibril_condvar_wait(&fs_list_cv, &fs_list_lock);
-			goto recheck;
-		}
-		
-		fibril_mutex_unlock(&fs_list_lock);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(rid, ENOENT);
-		free(mp);
-		free(fs_name);
-		free(opts);
-		return;
-	}
-	fibril_mutex_unlock(&fs_list_lock);
-
+		return;
+	}
+	
 	/* Add the filesystem info to the list of mounted filesystems */
 	mtab_ent_t *mtab_ent = malloc(sizeof(mtab_ent_t));
 	if (!mtab_ent) {
-		async_answer_0(callid, ENOMEM);
 		async_answer_0(rid, ENOMEM);
 		free(mp);
@@ -369,53 +249,40 @@
 		return;
 	}
-
-	/* Do the mount */
-	rc = vfs_mount_internal(rid, service_id, fs_handle, mp, opts);
-	if (rc != EOK) {
-		async_answer_0(callid, ENOTSUP);
-		async_answer_0(rid, ENOTSUP);
-		free(mtab_ent);
-		free(mp);
-		free(opts);
-		free(fs_name);
-		return;
-	}
+	
+	/* Mount the filesystem. */
+	fibril_rwlock_write_lock(&namespace_rwlock);
+	rc = vfs_mount_internal(service_id, flags, instance, opts, fs_name, mp);
+	fibril_rwlock_write_unlock(&namespace_rwlock);
 
 	/* Add the filesystem info to the list of mounted filesystems */
-
-	str_cpy(mtab_ent->mp, MAX_PATH_LEN, mp);
-	str_cpy(mtab_ent->fs_name, FS_NAME_MAXLEN, fs_name);
-	str_cpy(mtab_ent->opts, MAX_MNTOPTS_LEN, opts);
-	mtab_ent->instance = instance;
-	mtab_ent->service_id = service_id;
-
-	link_initialize(&mtab_ent->link);
-
-	fibril_mutex_lock(&mtab_list_lock);
-	list_append(&mtab_ent->link, &mtab_list);
-	mtab_size++;
-	fibril_mutex_unlock(&mtab_list_lock);
+	if (rc == EOK) {
+		str_cpy(mtab_ent->mp, MAX_PATH_LEN, mp);
+		str_cpy(mtab_ent->fs_name, FS_NAME_MAXLEN, fs_name);
+		str_cpy(mtab_ent->opts, MAX_MNTOPTS_LEN, opts);
+		mtab_ent->instance = instance;
+		mtab_ent->service_id = service_id;
+
+		link_initialize(&mtab_ent->link);
+
+		fibril_mutex_lock(&mtab_list_lock);
+		list_append(&mtab_ent->link, &mtab_list);
+		mtab_size++;
+		fibril_mutex_unlock(&mtab_list_lock);
+	}
+	
+	async_answer_0(rid, rc);
 
 	free(mp);
 	free(fs_name);
 	free(opts);
-
-	/* Acknowledge that we know fs_name. */
-	async_answer_0(callid, EOK);
 }
 
 void vfs_unmount_srv(ipc_callid_t rid, ipc_call_t *request)
 {
-	int rc;
+	/*
+	 * Receive the mount point path.
+	 */
 	char *mp;
-	vfs_lookup_res_t mp_res;
-	vfs_lookup_res_t mr_res;
-	vfs_node_t *mr_node;
-	async_exch_t *exch;
-	
-	/*
-	 * Receive the mount point path.
-	 */
-	rc = async_data_write_accept((void **) &mp, true, 0, MAX_PATH_LEN,
+	int rc = async_data_write_accept((void **) &mp, true, 0, MAX_PATH_LEN,
 	    0, NULL);
 	if (rc != EOK)
@@ -431,40 +298,7 @@
 	 */
 	fibril_rwlock_write_lock(&namespace_rwlock);
-	
-	/*
-	 * Lookup the mounted root and instantiate it.
-	 */
-	rc = vfs_lookup_internal(mp, L_ROOT, &mr_res, NULL);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
+		
+	if (str_cmp(mp, "/") == 0) {
 		free(mp);
-		async_answer_0(rid, rc);
-		return;
-	}
-	mr_node = vfs_node_get(&mr_res);
-	if (!mr_node) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		free(mp);
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
-	
-	/*
-	 * Count the total number of references for the mounted file system. We
-	 * are expecting at least two. One which we got above and one which we
-	 * got when the file system was mounted. If we find more, it means that
-	 * the file system cannot be gracefully unmounted at the moment because
-	 * someone is working with it.
-	 */
-	if (vfs_nodes_refcount_sum_get(mr_node->fs_handle,
-	    mr_node->service_id) != 2) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(mr_node);
-		free(mp);
-		async_answer_0(rid, EBUSY);
-		return;
-	}
-	
-	if (str_cmp(mp, "/") == 0) {
 		
 		/*
@@ -475,75 +309,90 @@
 		 */
 		
-		exch = vfs_exchange_grab(mr_node->fs_handle);
-		rc = async_req_1_0(exch, VFS_OUT_UNMOUNTED,
-		    mr_node->service_id);
+		if (!root) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			async_answer_0(rid, ENOENT);
+			return;
+		}
+		
+		/*
+		 * Count the total number of references for the mounted file system. We
+		 * are expecting at least one, which we got when the file system was mounted.
+		 * If we find more, it means that
+		 * the file system cannot be gracefully unmounted at the moment because
+		 * someone is working with it.
+		 */
+		if (vfs_nodes_refcount_sum_get(root->fs_handle, root->service_id) != 1) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			async_answer_0(rid, EBUSY);
+			return;
+		}
+		
+		async_exch_t *exch = vfs_exchange_grab(root->fs_handle);
+		rc = async_req_1_0(exch, VFS_OUT_UNMOUNTED, root->service_id);
 		vfs_exchange_release(exch);
 		
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			free(mp);
-			vfs_node_put(mr_node);
-			async_answer_0(rid, rc);
-			return;
-		}
-		
-		rootfs.fs_handle = 0;
-		rootfs.service_id = 0;
-	} else {
-		
-		/*
-		 * Unmounting a non-root file system.
-		 *
-		 * We have a regular mount point node representing the parent
-		 * file system, so we delegate the operation to it.
-		 */
-		
-		rc = vfs_lookup_internal(mp, L_MP, &mp_res, NULL);
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			free(mp);
-			vfs_node_put(mr_node);
-			async_answer_0(rid, rc);
-			return;
-		}
-		
-		vfs_node_t *mp_node = vfs_node_get(&mp_res);
-		if (!mp_node) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			free(mp);
-			vfs_node_put(mr_node);
-			async_answer_0(rid, ENOMEM);
-			return;
-		}
-		
-		exch = vfs_exchange_grab(mp_node->fs_handle);
-		rc = async_req_2_0(exch, VFS_OUT_UNMOUNT,
-		    mp_node->service_id, mp_node->index);
-		vfs_exchange_release(exch);
-		
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			free(mp);
-			vfs_node_put(mp_node);
-			vfs_node_put(mr_node);
-			async_answer_0(rid, rc);
-			return;
-		}
-		
-		/* Drop the reference we got above. */
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		if (rc == EOK) {
+			vfs_node_forget(root);
+			root = NULL;
+		}
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	/*
+	 * Lookup the mounted root and instantiate it.
+	 */
+	vfs_lookup_res_t mp_res;
+	rc = vfs_lookup_internal(root, mp, L_MP, &mp_res);
+	if (rc != EOK) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		free(mp);
+		async_answer_0(rid, rc);
+		return;
+	}
+	vfs_node_t *mp_node = vfs_node_get(&mp_res);
+	if (!mp_node) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		free(mp);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+	
+	if (mp_node->mount == NULL) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
 		vfs_node_put(mp_node);
-		/* Drop the reference from when the file system was mounted. */
+		free(mp);
+		async_answer_0(rid, ENOENT);
+		return;
+	}
+	
+	/*
+	 * Count the total number of references for the mounted file system. We
+	 * are expecting at least one, which we got when the file system was mounted.
+	 * If we find more, it means that
+	 * the file system cannot be gracefully unmounted at the moment because
+	 * someone is working with it.
+	 */
+	if (vfs_nodes_refcount_sum_get(mp_node->mount->fs_handle, mp_node->mount->service_id) != 1) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
 		vfs_node_put(mp_node);
-	}
-	
-	/*
-	 * All went well, the mounted file system was successfully unmounted.
-	 * The only thing left is to forget the unmounted root VFS node.
-	 */
-	vfs_node_forget(mr_node);
+		free(mp);
+		async_answer_0(rid, EBUSY);
+		return;
+	}
+	
+	/* Unmount the filesystem. */
+	async_exch_t *exch = vfs_exchange_grab(mp_node->mount->fs_handle);
+	rc = async_req_1_0(exch, VFS_OUT_UNMOUNTED, mp_node->mount->service_id);
+	vfs_exchange_release(exch);
+	
+	vfs_node_forget(mp_node->mount);
+	mp_node->mount = NULL;
+	
+	vfs_node_put(mp_node);
 	fibril_rwlock_write_unlock(&namespace_rwlock);
-
+	
 	fibril_mutex_lock(&mtab_list_lock);
-
 	int found = 0;
 
@@ -560,133 +409,176 @@
 	fibril_mutex_unlock(&mtab_list_lock);
 
-	free(mp);
-
+	free(mp);	
+	
 	async_answer_0(rid, EOK);
-}
-
-void vfs_open(ipc_callid_t rid, ipc_call_t *request)
-{
-	/*
-	 * The POSIX interface is open(path, oflag, mode).
-	 * We can receive oflags and mode along with the VFS_IN_OPEN call;
-	 * the path will need to arrive in another call.
-	 *
-	 * We also receive one private, non-POSIX set of flags called lflag
-	 * used to pass information to vfs_lookup_internal().
-	 */
-	int lflag = IPC_GET_ARG1(*request);
-	int oflag = IPC_GET_ARG2(*request);
-	int mode = IPC_GET_ARG3(*request);
-
-	/* Ignore mode for now. */
-	(void) mode;
-	
-	/*
-	 * Make sure that we are called with exactly one of L_FILE and
-	 * L_DIRECTORY. Make sure that the user does not pass L_OPEN,
-	 * L_ROOT or L_MP.
-	 */
-	if (((lflag & (L_FILE | L_DIRECTORY)) == 0) ||
-	    ((lflag & (L_FILE | L_DIRECTORY)) == (L_FILE | L_DIRECTORY)) ||
-	    (lflag & (L_OPEN | L_ROOT | L_MP))) {
+	return;
+}
+
+static inline bool walk_flags_valid(int flags)
+{
+	if ((flags&~WALK_ALL_FLAGS) != 0) {
+		return false;
+	}
+	if ((flags&WALK_MAY_CREATE) && (flags&WALK_MUST_CREATE)) {
+		return false;
+	}
+	if ((flags&WALK_REGULAR) && (flags&WALK_DIRECTORY)) {
+		return false;
+	}
+	if ((flags&WALK_MAY_CREATE) || (flags&WALK_MUST_CREATE)) {
+		if (!(flags&WALK_DIRECTORY) && !(flags&WALK_REGULAR)) {
+			return false;
+		}
+	}
+	return true;
+}
+
+static inline int walk_lookup_flags(int flags)
+{
+	int lflags = 0;
+	if (flags&WALK_MAY_CREATE || flags&WALK_MUST_CREATE) {
+		lflags |= L_CREATE;
+	}
+	if (flags&WALK_MUST_CREATE) {
+		lflags |= L_EXCLUSIVE;
+	}
+	if (flags&WALK_REGULAR) {
+		lflags |= L_FILE;
+	}
+	if (flags&WALK_DIRECTORY) {
+		lflags |= L_DIRECTORY;
+	}
+	return lflags;
+}
+
+void vfs_walk(ipc_callid_t rid, ipc_call_t *request)
+{
+	/*
+	 * Parent is our relative root for file lookup.
+	 * For defined flags, see <ipc/vfs.h>.
+	 */
+	int parentfd = IPC_GET_ARG1(*request);
+	int flags = IPC_GET_ARG2(*request);
+	
+	if (!walk_flags_valid(flags)) {
 		async_answer_0(rid, EINVAL);
 		return;
 	}
 	
-	if (oflag & O_CREAT)
-		lflag |= L_CREATE;
-	if (oflag & O_EXCL)
-		lflag |= L_EXCLUSIVE;
-	
 	char *path;
-	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
-	if (rc != EOK) {
+	int rc = async_data_write_accept((void **)&path, true, 0, 0, 0, NULL);
+	
+	/* Lookup the file structure corresponding to the file descriptor. */
+	vfs_file_t *parent = NULL;
+	vfs_node_t *parent_node = root;
+	// TODO: Client-side root.
+	if (parentfd != -1) {
+		parent = vfs_file_get(parentfd);
+		if (!parent) {
+			free(path);
+			async_answer_0(rid, EBADF);
+			return;
+		}
+		parent_node = parent->node;
+	}
+	
+	fibril_rwlock_read_lock(&namespace_rwlock);
+	
+	vfs_lookup_res_t lr;
+	rc = vfs_lookup_internal(parent_node, path, walk_lookup_flags(flags), &lr);
+	free(path);
+
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		if (parent) {
+			vfs_file_put(parent);
+		}
 		async_answer_0(rid, rc);
 		return;
 	}
 	
-	/*
-	 * Avoid the race condition in which the file can be deleted before we
-	 * find/create-and-lock the VFS node corresponding to the looked-up
-	 * triplet.
-	 */
-	if (lflag & L_CREATE)
-		fibril_rwlock_write_lock(&namespace_rwlock);
-	else
-		fibril_rwlock_read_lock(&namespace_rwlock);
-	
-	/* The path is now populated and we can call vfs_lookup_internal(). */
-	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal(path, lflag | L_OPEN, &lr, NULL);
-	if (rc != EOK) {
-		if (lflag & L_CREATE)
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-		else
-			fibril_rwlock_read_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		free(path);
-		return;
-	}
-	
-	/* Path is no longer needed. */
-	free(path);
-	
 	vfs_node_t *node = vfs_node_get(&lr);
-	if (lflag & L_CREATE)
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-	else
-		fibril_rwlock_read_unlock(&namespace_rwlock);
-
-	if (!node) {
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
-	
-	/* Truncate the file if requested and if necessary. */
-	if (oflag & O_TRUNC) {
-		fibril_rwlock_write_lock(&node->contents_rwlock);
-		if (node->size) {
-			rc = vfs_truncate_internal(node->fs_handle,
-			    node->service_id, node->index, 0);
-			if (rc) {
-				fibril_rwlock_write_unlock(&node->contents_rwlock);
-				vfs_node_put(node);
-				async_answer_0(rid, rc);
-				return;
-			}
-			node->size = 0;
-		}
-		fibril_rwlock_write_unlock(&node->contents_rwlock);
-	}
-	
-	/*
-	 * Get ourselves a file descriptor and the corresponding vfs_file_t
-	 * structure.
-	 */
-	int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
+	
+	int fd = vfs_fd_alloc(false);
 	if (fd < 0) {
 		vfs_node_put(node);
+		if (parent) {
+			vfs_file_put(parent);
+		}
 		async_answer_0(rid, fd);
 		return;
 	}
+	
 	vfs_file_t *file = vfs_file_get(fd);
-	assert(file);
+	assert(file != NULL);
+	
 	file->node = node;
-	if (oflag & O_APPEND)
-		file->append = true;
-	
-	/*
-	 * The following increase in reference count is for the fact that the
-	 * file is being opened and that a file structure is pointing to it.
-	 * It is necessary so that the file will not disappear when
-	 * vfs_node_put() is called. The reference will be dropped by the
-	 * respective VFS_IN_CLOSE.
-	 */
-	vfs_node_addref(node);
-	vfs_node_put(node);
+	if (parent) {
+		file->permissions = parent->permissions;
+	} else {
+		file->permissions = MODE_READ | MODE_WRITE | MODE_APPEND;
+	}
+	file->open_read = false;
+	file->open_write = false;
+	
 	vfs_file_put(file);
-	
-	/* Success! Return the new file descriptor to the client. */
+	if (parent) {
+		vfs_file_put(parent);
+	}
+	
+	fibril_rwlock_read_unlock(&namespace_rwlock);
+
 	async_answer_1(rid, EOK, fd);
+}
+
+void vfs_open2(ipc_callid_t rid, ipc_call_t *request)
+{
+	int fd = IPC_GET_ARG1(*request);
+	int flags = IPC_GET_ARG2(*request);
+
+	if (flags == 0) {
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+
+	vfs_file_t *file = vfs_file_get(fd);
+	if (!file) {
+		async_answer_0(rid, EBADF);
+		return;
+	}
+	
+	if ((flags & ~file->permissions) != 0) {
+		vfs_file_put(file);
+		async_answer_0(rid, EPERM);
+		return;
+	}
+	
+	file->open_read = (flags & MODE_READ) != 0;
+	file->open_write = (flags & (MODE_WRITE | MODE_APPEND)) != 0;
+	file->append = (flags & MODE_APPEND) != 0;
+	
+	if (!file->open_read && !file->open_write) {
+		vfs_file_put(file);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+	
+	if (file->node->type == VFS_NODE_DIRECTORY && file->open_write) {
+		file->open_read = file->open_write = false;
+		vfs_file_put(file);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+	
+	int rc = vfs_open_node_remote(file->node);
+	if (rc != EOK) {
+		file->open_read = file->open_write = false;
+		vfs_file_put(file);
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	vfs_file_put(file);
+	async_answer_0(rid, EOK);
 }
 
@@ -816,4 +708,9 @@
 	fibril_mutex_lock(&file->lock);
 	
+	if ((read && !file->open_read) || (!read && !file->open_write)) {
+		fibril_mutex_unlock(&file->lock);
+		return EINVAL;
+	}
+	
 	vfs_info_t *fs_info = fs_handle_to_info(file->node->fs_handle);
 	assert(fs_info);
@@ -854,6 +751,7 @@
 	size_t bytes = IPC_GET_ARG1(answer);
 	
-	if (file->node->type == VFS_NODE_DIRECTORY)
+	if (file->node->type == VFS_NODE_DIRECTORY) {
 		fibril_rwlock_read_unlock(&namespace_rwlock);
+	}
 	
 	/* Unlock the VFS node. */
@@ -953,5 +851,5 @@
 	case SEEK_END:
 		fibril_rwlock_read_lock(&file->node->contents_rwlock);
-		aoff64_t size = file->node->size;
+		aoff64_t size = vfs_node_get_size(file->node);
 		
 		if ((off >= 0) && (size + off < size)) {
@@ -1061,8 +959,25 @@
 }
 
-void vfs_stat(ipc_callid_t rid, ipc_call_t *request)
-{
+static void out_destroy(vfs_triplet_t *file)
+{
+	async_exch_t *exch = vfs_exchange_grab(file->fs_handle);
+	async_msg_2(exch, VFS_OUT_DESTROY,
+		(sysarg_t) file->service_id, (sysarg_t) file->index);
+	vfs_exchange_release(exch);
+}
+
+void vfs_unlink2(ipc_callid_t rid, ipc_call_t *request)
+{
+	int rc;
 	char *path;
-	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
+	vfs_file_t *parent = NULL;
+	vfs_file_t *expect = NULL;
+	vfs_node_t *parent_node = root;
+	
+	int parentfd = IPC_GET_ARG1(*request);
+	int expectfd = IPC_GET_ARG2(*request);
+	int wflag = IPC_GET_ARG3(*request);
+	
+	rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
 	if (rc != EOK) {
 		async_answer_0(rid, rc);
@@ -1070,124 +985,186 @@
 	}
 	
-	ipc_callid_t callid;
-	if (!async_data_read_receive(&callid, NULL)) {
+	fibril_rwlock_write_lock(&namespace_rwlock);
+	
+	int lflag = (wflag&WALK_DIRECTORY) ? L_DIRECTORY: 0;
+
+	if (parentfd >= 0) {
+		parent = vfs_file_get(parentfd);
+		if (!parent) {
+			rc = ENOENT;
+			goto exit;
+		}
+		parent_node = parent->node;
+	}
+	
+	if (expectfd >= 0) {
+		expect = vfs_file_get(expectfd);
+		if (!expect) {
+			rc = ENOENT;
+			goto exit;
+		}
+		
+		vfs_lookup_res_t lr;
+		rc = vfs_lookup_internal(parent_node, path, lflag, &lr);
+		if (rc != EOK) {
+			goto exit;
+		}
+		
+		if (__builtin_memcmp(&lr.triplet, expect->node, sizeof(vfs_triplet_t)) != 0) {
+			rc = ENOENT;
+			goto exit;
+		}
+		
+		vfs_file_put(expect);
+		expect = NULL;
+	}
+	
+	vfs_lookup_res_t lr;
+	rc = vfs_lookup_internal(parent_node, path, lflag | L_UNLINK, &lr);
+	if (rc != EOK) {
+		goto exit;
+	}
+
+	/* If the node is not held by anyone, try to destroy it. */
+	if (vfs_node_peek(&lr) == NULL) {
+		out_destroy(&lr.triplet);
+	}
+
+exit:
+	if (path) {
 		free(path);
-		async_answer_0(callid, EINVAL);
-		async_answer_0(rid, EINVAL);
-		return;
-	}
-
-	vfs_lookup_res_t lr;
-	fibril_rwlock_read_lock(&namespace_rwlock);
-	rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
-	free(path);
-	if (rc != EOK) {
-		fibril_rwlock_read_unlock(&namespace_rwlock);
-		async_answer_0(callid, rc);
-		async_answer_0(rid, rc);
-		return;
-	}
-	vfs_node_t *node = vfs_node_get(&lr);
-	if (!node) {
-		fibril_rwlock_read_unlock(&namespace_rwlock);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(rid, ENOMEM);
-		return;
-	}
-
-	fibril_rwlock_read_unlock(&namespace_rwlock);
-
-	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
-	
-	aid_t msg;
-	msg = async_send_3(exch, VFS_OUT_STAT, node->service_id,
-	    node->index, false, NULL);
-	async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
-	
-	vfs_exchange_release(exch);
-	
-	sysarg_t rv;
-	async_wait_for(msg, &rv);
-
-	async_answer_0(rid, rv);
-
-	vfs_node_put(node);
-}
-
-void vfs_mkdir(ipc_callid_t rid, ipc_call_t *request)
-{
-	int mode = IPC_GET_ARG1(*request);
-	
-	char *path;
-	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	/* Ignore mode for now. */
-	(void) mode;
+	}
+	if (parent) {
+		vfs_file_put(parent);
+	}
+	if (expect) {
+		vfs_file_put(expect);
+	}
+	fibril_rwlock_write_unlock(&namespace_rwlock);
+	async_answer_0(rid, rc);
+}
+
+static size_t shared_path(char *a, char *b)
+{
+	size_t res = 0;
+	
+	while (a[res] == b[res] && a[res] != 0) {
+		res++;
+	}
+	
+	if (a[res] == b[res]) {
+		return res;
+	}
+	
+	res--;
+	while (a[res] != '/') {
+		res--;
+	}
+	return res;
+}
+
+static int vfs_rename_internal(vfs_node_t *base, char *old, char *new)
+{
+	assert(base != NULL);
+	assert(old != NULL);
+	assert(new != NULL);
+	
+	vfs_lookup_res_t base_lr;
+	vfs_lookup_res_t old_lr;
+	vfs_lookup_res_t new_lr_orig;
+	bool orig_unlinked = false;
+	
+	int rc;
+	
+	size_t shared = shared_path(old, new);
+	
+	/* Do not allow one path to be a prefix of the other. */
+	if (old[shared] == 0 || new[shared] == 0) {
+		return EINVAL;
+	}
+	assert(old[shared] == '/');
+	assert(new[shared] == '/');
 	
 	fibril_rwlock_write_lock(&namespace_rwlock);
-	int lflag = L_DIRECTORY | L_CREATE | L_EXCLUSIVE;
-	rc = vfs_lookup_internal(path, lflag, NULL, NULL);
+	
+	/* Resolve the shared portion of the path first. */
+	if (shared != 0) {
+		old[shared] = 0;
+		rc = vfs_lookup_internal(base, old, L_DIRECTORY, &base_lr);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			return rc;
+		}
+		
+		base = vfs_node_get(&base_lr);
+		old[shared] = '/';
+		old += shared;
+		new += shared;
+	} else {
+		vfs_node_addref(base);
+	}
+	
+	
+	rc = vfs_lookup_internal(base, new, L_UNLINK | L_DISABLE_MOUNTS, &new_lr_orig);
+	if (rc == EOK) {
+		orig_unlinked = true;
+	} else if (rc != ENOENT) {
+		vfs_node_put(base);
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		return rc;
+	}
+	
+	rc = vfs_lookup_internal(base, old, L_UNLINK | L_DISABLE_MOUNTS, &old_lr);
+	if (rc != EOK) {
+		if (orig_unlinked) {
+			vfs_link_internal(base, new, &new_lr_orig.triplet);
+		}
+		vfs_node_put(base);
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		return rc;
+	}
+	
+	rc = vfs_link_internal(base, new, &old_lr.triplet);
+	if (rc != EOK) {
+		vfs_link_internal(base, old, &old_lr.triplet);
+		if (orig_unlinked) {
+			vfs_link_internal(base, new, &new_lr_orig.triplet);
+		}
+		vfs_node_put(base);
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		return rc;
+	}
+	
+	/* If the node is not held by anyone, try to destroy it. */
+	if (orig_unlinked && vfs_node_peek(&new_lr_orig) == NULL) {
+		out_destroy(&new_lr_orig.triplet);
+	}
+	
+	vfs_node_put(base);
 	fibril_rwlock_write_unlock(&namespace_rwlock);
-	free(path);
-	async_answer_0(rid, rc);
-}
-
-void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
-{
-	int lflag = IPC_GET_ARG1(*request);
-	
-	char *path;
-	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	fibril_rwlock_write_lock(&namespace_rwlock);
-	lflag &= L_DIRECTORY;	/* sanitize lflag */
-	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal(path, lflag | L_UNLINK, &lr, NULL);
-	free(path);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		return;
-	}
-
-	/*
-	 * The name has already been unlinked by vfs_lookup_internal().
-	 * We have to get and put the VFS node to ensure that it is
-	 * VFS_OUT_DESTROY'ed after the last reference to it is dropped.
-	 */
-	vfs_node_t *node = vfs_node_get(&lr);
-	fibril_mutex_lock(&nodes_mutex);
-	node->lnkcnt--;
-	fibril_mutex_unlock(&nodes_mutex);
-	fibril_rwlock_write_unlock(&namespace_rwlock);
-	vfs_node_put(node);
-	async_answer_0(rid, EOK);
+	return EOK;
 }
 
 void vfs_rename(ipc_callid_t rid, ipc_call_t *request)
 {
+	/* The common base directory. */
+	int basefd;
+	char *old = NULL;
+	char *new = NULL;
+	vfs_file_t *base = NULL;
+	int rc;
+	
+	basefd = IPC_GET_ARG1(*request);
+	
 	/* Retrieve the old path. */
-	char *old;
-	int rc = async_data_write_accept((void **) &old, true, 0, 0, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(rid, rc);
-		return;
+	rc = async_data_write_accept((void **) &old, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		goto out;
 	}
 	
 	/* Retrieve the new path. */
-	char *new;
 	rc = async_data_write_accept((void **) &new, true, 0, 0, 0, NULL);
 	if (rc != EOK) {
-		free(old);
-		async_answer_0(rid, rc);
-		return;
+		goto out;
 	}
 	
@@ -1198,166 +1175,37 @@
 	
 	if ((!oldc) || (!newc)) {
-		async_answer_0(rid, EINVAL);
+		rc = EINVAL;
+		goto out;
+	}
+	
+	assert(oldc[olen] == '\0');
+	assert(newc[nlen] == '\0');
+	
+	/* Lookup the file structure corresponding to the file descriptor. */
+	vfs_node_t *base_node = root;
+	// TODO: Client-side root.
+	if (basefd != -1) {
+		base = vfs_file_get(basefd);
+		if (!base) {
+			rc = EBADF;
+			goto out;
+		}
+		base_node = base->node;
+	}
+	
+	rc = vfs_rename_internal(base_node, oldc, newc);
+
+out:
+	async_answer_0(rid, rc);
+
+	if (old) {
 		free(old);
+	}
+	if (new) {
 		free(new);
-		return;
-	}
-	
-	oldc[olen] = '\0';
-	newc[nlen] = '\0';
-	
-	if ((!str_lcmp(newc, oldc, str_length(oldc))) &&
-	    ((newc[str_length(oldc)] == '/') ||
-	    (str_length(oldc) == 1) ||
-	    (str_length(oldc) == str_length(newc)))) {
-	    	/*
-		 * oldc is a prefix of newc and either
-		 * - newc continues with a / where oldc ends, or
-		 * - oldc was / itself, or
-		 * - oldc and newc are equal.
-		 */
-		async_answer_0(rid, EINVAL);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	vfs_lookup_res_t old_lr;
-	vfs_lookup_res_t new_lr;
-	vfs_lookup_res_t new_par_lr;
-	fibril_rwlock_write_lock(&namespace_rwlock);
-	
-	/* Lookup the node belonging to the old file name. */
-	rc = vfs_lookup_internal(oldc, L_NONE, &old_lr, NULL);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	vfs_node_t *old_node = vfs_node_get(&old_lr);
-	if (!old_node) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		async_answer_0(rid, ENOMEM);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	/* Determine the path to the parent of the node with the new name. */
-	char *parentc = str_dup(newc);
-	if (!parentc) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		async_answer_0(rid, rc);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	char *lastsl = str_rchr(parentc + 1, '/');
-	if (lastsl)
-		*lastsl = '\0';
-	else
-		parentc[1] = '\0';
-	
-	/* Lookup parent of the new file name. */
-	rc = vfs_lookup_internal(parentc, L_NONE, &new_par_lr, NULL);
-	free(parentc);	/* not needed anymore */
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		async_answer_0(rid, rc);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	/* Check whether linking to the same file system instance. */
-	if ((old_node->fs_handle != new_par_lr.triplet.fs_handle) ||
-	    (old_node->service_id != new_par_lr.triplet.service_id)) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		async_answer_0(rid, EXDEV);	/* different file systems */
-		free(old);
-		free(new);
-		return;
-	}
-	
-	/* Destroy the old link for the new name. */
-	vfs_node_t *new_node = NULL;
-	rc = vfs_lookup_internal(newc, L_UNLINK, &new_lr, NULL);
-	
-	switch (rc) {
-	case ENOENT:
-		/* simply not in our way */
-		break;
-	case EOK:
-		new_node = vfs_node_get(&new_lr);
-		if (!new_node) {
-			fibril_rwlock_write_unlock(&namespace_rwlock);
-			vfs_node_put(old_node);
-			async_answer_0(rid, ENOMEM);
-			free(old);
-			free(new);
-			return;
-		}
-		fibril_mutex_lock(&nodes_mutex);
-		new_node->lnkcnt--;
-		fibril_mutex_unlock(&nodes_mutex);
-		break;
-	default:
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		async_answer_0(rid, ENOTEMPTY);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	/* Create the new link for the new name. */
-	rc = vfs_lookup_internal(newc, L_LINK, NULL, NULL, old_node->index);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		if (new_node)
-			vfs_node_put(new_node);
-		async_answer_0(rid, rc);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	fibril_mutex_lock(&nodes_mutex);
-	old_node->lnkcnt++;
-	fibril_mutex_unlock(&nodes_mutex);
-	
-	/* Destroy the link for the old name. */
-	rc = vfs_lookup_internal(oldc, L_UNLINK, NULL, NULL);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&namespace_rwlock);
-		vfs_node_put(old_node);
-		if (new_node)
-			vfs_node_put(new_node);
-		async_answer_0(rid, rc);
-		free(old);
-		free(new);
-		return;
-	}
-	
-	fibril_mutex_lock(&nodes_mutex);
-	old_node->lnkcnt--;
-	fibril_mutex_unlock(&nodes_mutex);
-	fibril_rwlock_write_unlock(&namespace_rwlock);
-	vfs_node_put(old_node);
-	
-	if (new_node)
-		vfs_node_put(new_node);
-	
-	free(old);
-	free(new);
-	async_answer_0(rid, EOK);
+	}
+	if (base) {
+		vfs_file_put(base);
+	}
 }
 
@@ -1487,5 +1335,5 @@
 	vfs_lookup_res_t lr;
 	fibril_rwlock_read_lock(&namespace_rwlock);
-	rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
+	rc = vfs_lookup_internal(root, path, L_NONE, &lr);
 	free(path);
 	if (rc != EOK) {
