Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision c442f6354ee9f2d5ef6e0a6000ad7abf792a556d)
+++ uspace/lib/c/include/ipc/vfs.h	(revision cb65bbe6ec28f0093b952e0a2de1517506c6379d)
@@ -82,4 +82,6 @@
 	VFS_IN_WAIT_HANDLE,
 	VFS_IN_MTAB_GET,
+	VFS_IN_WALK,
+	VFS_IN_OPEN2,
 } vfs_in_request_t;
 
@@ -168,4 +170,24 @@
 #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_ALL_FLAGS = 0,
+};
+
+enum {
+	MODE_READ = 1,
+	MODE_WRITE = 2,
+	MODE_APPEND = 4,
+};
+
 #endif
 
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision c442f6354ee9f2d5ef6e0a6000ad7abf792a556d)
+++ uspace/srv/vfs/vfs.c	(revision cb65bbe6ec28f0093b952e0a2de1517506c6379d)
@@ -85,4 +85,10 @@
 			vfs_unmount(callid, &call);
 			break;
+		case VFS_IN_WALK:
+			vfs_walk(callid, &call);
+			break;
+		case VFS_IN_OPEN2:
+			vfs_open(callid, &call);
+			break;
 		case VFS_IN_OPEN:
 			vfs_open(callid, &call);
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision c442f6354ee9f2d5ef6e0a6000ad7abf792a556d)
+++ uspace/srv/vfs/vfs.h	(revision cb65bbe6ec28f0093b952e0a2de1517506c6379d)
@@ -137,4 +137,8 @@
 	/** Number of file handles referencing this file. */
 	unsigned refcnt;
+
+	int permissions;
+	bool open_read;
+	bool open_write;
 
 	/** Append on write. */
@@ -223,4 +227,8 @@
 extern void vfs_get_mtab(ipc_callid_t, ipc_call_t *);
 
+extern void vfs_walk(ipc_callid_t, ipc_call_t *);
+extern void vfs_create(ipc_callid_t, ipc_call_t *);
+extern void vfs_open2(ipc_callid_t, ipc_call_t *);
+
 #endif
 
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision c442f6354ee9f2d5ef6e0a6000ad7abf792a556d)
+++ uspace/srv/vfs/vfs_file.c	(revision cb65bbe6ec28f0093b952e0a2de1517506c6379d)
@@ -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);
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision c442f6354ee9f2d5ef6e0a6000ad7abf792a556d)
+++ uspace/srv/vfs/vfs_ops.c	(revision cb65bbe6ec28f0093b952e0a2de1517506c6379d)
@@ -568,4 +568,138 @@
 }
 
+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 ((flags&~WALK_ALL_FLAGS) != 0) {
+		/* Invalid flags. */
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+	
+	char *path;
+	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_pair_t *parent_node = NULL;
+	// TODO: Client-side root.
+	if (parentfd != -1) {
+		parent = vfs_file_get(parentfd);
+		if (!parent) {
+			free(path);
+			async_answer_0(rid, EBADF);
+			return;
+		}
+		parent_node = (vfs_pair_t *)parent->node;
+	}
+	
+	fibril_rwlock_read_lock(&namespace_rwlock);
+	
+	vfs_lookup_res_t lr;
+	rc = vfs_lookup_internal(path, 0, &lr, parent_node);
+	free(path);
+
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		if (parent) {
+			vfs_file_put(parent);
+		}
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	vfs_node_t *node = vfs_node_get(&lr);
+	
+	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 != NULL);
+	
+	file->node = 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_node_addref(node);
+	vfs_node_put(node);
+	vfs_file_put(file);
+	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);
+}
+
 void vfs_open(ipc_callid_t rid, ipc_call_t *request)
 {
@@ -669,6 +803,19 @@
 	}
 	vfs_file_t *file = vfs_file_get(fd);
-	assert(file);
+	
+	/* There is a potential race with another fibril of a malicious client. */
+	if (!file) {
+		vfs_node_put(node);
+		async_answer_0(rid, EBUSY);
+		return;
+	}
+	
 	file->node = node;
+	if (oflag & O_RDONLY)
+		file->open_read = true;
+	if (oflag & O_WRONLY)
+		file->open_write = true;
+	if (oflag & O_RDWR)
+		file->open_read = file->open_write = true;
 	if (oflag & O_APPEND)
 		file->append = true;
@@ -758,4 +905,10 @@
 	 */
 	fibril_mutex_lock(&file->lock);
+	
+	if ((read && !file->open_read) || (!read && !file->open_write)) {
+		fibril_mutex_unlock(&file->lock);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
 	
 	vfs_info_t *fs_info = fs_handle_to_info(file->node->fs_handle);
