Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision d60ce4abaa4bfa71b3cf2c3701c3157ffc9c341f)
+++ uspace/lib/c/include/ipc/vfs.h	(revision bf9dc4e2a497f5d183351f2b788ef6d7723ba6cf)
@@ -97,4 +97,5 @@
 	VFS_OUT_STAT,
 	VFS_OUT_LOOKUP,
+	VFS_OUT_LINK,
 	VFS_OUT_DESTROY,
 	VFS_OUT_LAST
@@ -125,10 +126,4 @@
 
 /**
- * Lookup will succeed only if the object is a root directory. The flag is
- * mutually exclusive with L_FILE and L_MP.
- */
-#define L_ROOT			4
-
-/**
  * Lookup will succeed only if the object is a mount point. The flag is mutually
  * exclusive with L_FILE and L_ROOT.
@@ -147,9 +142,4 @@
  */
 #define L_CREATE		32
-
-/**
- * L_LINK is used for linking to an already existing nodes.
- */
-#define L_LINK			64
 
 /**
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision d60ce4abaa4bfa71b3cf2c3701c3157ffc9c341f)
+++ uspace/lib/fs/libfs.c	(revision bf9dc4e2a497f5d183351f2b788ef6d7723ba6cf)
@@ -62,4 +62,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;
 
@@ -69,4 +74,6 @@
 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 *);
@@ -121,4 +128,9 @@
 		
 	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);
 }
 
@@ -250,4 +262,7 @@
 			vfs_out_unmount(callid, &call);
 			break;
+		case VFS_OUT_LINK:
+			vfs_out_link(callid, &call);
+			break;
 		case VFS_OUT_LOOKUP:
 			vfs_out_lookup(callid, &call);
@@ -486,4 +501,85 @@
 }
 
+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 *parent = NULL;
+	rc = ops->node_get(&parent, parent_sid, parent_index);
+	if (parent == NULL) {
+		async_answer_0(rid, rc == EOK ? EBADF : rc);
+		return;
+	}
+	
+	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;
+	}
+	
+	rc = ops->link(parent, child, component);
+	ops->node_put(parent);
+	ops->node_put(child);
+	async_answer_0(rid, rc);
+}
+
 /** Lookup VFS triplet by name in the file system name space.
  *
@@ -499,71 +595,88 @@
  *
  */
-void libfs_lookup(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
-    ipc_call_t *req)
-{
-	unsigned int first = IPC_GET_ARG1(*req);
-	unsigned int last = IPC_GET_ARG2(*req);
-	unsigned int next = first;
+void libfs_lookup(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid, ipc_call_t *req)
+{
+	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);
+	
+	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);
+	unsigned clen = 0;
+	
+	if (index == (fs_index_t)-1) {
+		rc = ops->root_get(&cur, service_id);
+	} else {
+		rc = ops->node_get(&cur, service_id, index);
+	}
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		LOG_EXIT(rc);
+		goto out;
+	}
+	
+	assert(cur != NULL);
 	
 	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_forward_slow(rid, exch, VFS_OUT_LOOKUP, next, last - next,
+		    cur->mp_data.service_id, (fs_index_t) -1, lflag, IPC_FF_ROUTE_FROM_ME);
 		async_exchange_end(exch);
 		
 		(void) ops->node_put(cur);
-		return;
-	}
-	
-	/* Eat slash */
-	if (plb_get_char(next) == '/')
-		next++;
-	
-	while (next <= last) {
-		bool has_children;
-		
-		rc = ops->has_children(&has_children, cur);
-		on_error(rc, goto out_with_answer);
-		if (!has_children)
-			break;
+		DPRINTF("Passing to another filesystem instance.\n");
+		return;
+	}
+	
+	/* Find the file and its parent. */
+	
+	while (next != last) {
+		if (cur == NULL) {
+			async_answer_0(rid, ENOENT);
+			LOG_EXIT(ENOENT);
+			goto out;
+		}
+		if (!ops->is_directory(cur)) {
+			async_answer_0(rid, ENOTDIR);
+			LOG_EXIT(ENOTDIR);
+			goto out;
+		}
 		
 		/* 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++;
+		rc = plb_get_component(component, &clen, &next, last);
+		assert(rc != ERANGE);
+		if (rc != EOK) {
+			async_answer_0(rid, rc);
+			LOG_EXIT(rc);
+			goto out;
+		}
+		
+		if (clen == 0) {
+			/* The path is just "/". */
+			break;
+		}
+		
+		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;
+		}
 		
 		/*
@@ -578,77 +691,24 @@
 
 		if ((tmp) && (tmp->mp_data.mp_active) &&
-		    (!(lflag & L_MP) || (next <= last))) {
-			if (next > last)
-				next = last = first;
-			else
-				next--;
-			
+		    (!(lflag & L_MP) || (next < last))) {
 			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,
+			    last - next, tmp->mp_data.service_id, (fs_index_t) -1, lflag,
 			    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);
+			DPRINTF("Passing to another filesystem instance.\n");
+			goto out;
+		}
+		
+		/* Descend one level */
+		if (par) {
+			rc = ops->node_put(par);
+			if (rc != EOK) {
+				async_answer_0(rid, rc);
+				LOG_EXIT(rc); 
 				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);
-				
-				goto out;
-			}
-			
-			async_answer_0(rid, ENOENT);
-			goto out;
-		}
-		
-		if (par) {
-			rc = ops->node_put(par);
-			on_error(rc, goto out_with_answer);
-		}
-		
-		/* Descend one level */
+		}
+		
 		par = cur;
 		cur = tmp;
@@ -656,79 +716,41 @@
 	}
 	
-	/* 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);
-			
+	/* 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;
 		}
-		
-		async_answer_0(rid, ENOENT);
-		goto out;
-	}
-	
-skip_miss:
-	
-	/* Handle hit */
-	if (lflag & L_UNLINK) {
+		if (!par) {
+			async_answer_0(rid, EINVAL);
+			LOG_EXIT(EINVAL);
+			goto out;
+		}
+		
 		unsigned int old_lnkcnt = ops->lnkcnt_get(cur);
 		rc = ops->unlink(par, cur, component);
-		
 		if (rc == EOK) {
 			aoff64_t size = ops->size_get(cur);
@@ -736,58 +758,80 @@
 			    ops->index_get(cur), LOWER32(size), UPPER32(size),
 			    old_lnkcnt);
-		} else
+			LOG_EXIT(EOK);
+		} else {
 			async_answer_0(rid, rc);
-		
+			LOG_EXIT(rc);
+		}
 		goto out;
 	}
 	
-	if (((lflag & (L_CREATE | L_EXCLUSIVE)) == (L_CREATE | L_EXCLUSIVE)) ||
-	    (lflag & L_LINK)) {
-		async_answer_0(rid, EEXIST);
+	/* 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;
+			}
+			
+			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;
+			}
+		}
+	}
+	
+	/* Return. */
+	
+	if (!cur) {
+		async_answer_0(rid, ENOENT);
+		LOG_EXIT(ENOENT);
 		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
+	if (lflag & L_OPEN) {
+		rc = ops->node_open(cur);
+		if (rc != EOK) {
 			async_answer_0(rid, rc);
-		
-	} else
-		async_answer_0(rid, rc);
-	
+			LOG_EXIT(rc);
+			goto out;
+		}
+	}
+	
+	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));
+	
+	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/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision d60ce4abaa4bfa71b3cf2c3701c3157ffc9c341f)
+++ uspace/srv/vfs/vfs.h	(revision bf9dc4e2a497f5d183351f2b788ef6d7723ba6cf)
@@ -180,6 +180,6 @@
 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_triplet_t *, char *, int, vfs_lookup_res_t *);
+extern int vfs_link_internal(vfs_triplet_t *, char *, vfs_triplet_t *);
 
 extern bool vfs_nodes_init(void);
Index: uspace/srv/vfs/vfs_lookup.c
===================================================================
--- uspace/srv/vfs/vfs_lookup.c	(revision d60ce4abaa4bfa71b3cf2c3701c3157ffc9c341f)
+++ uspace/srv/vfs/vfs_lookup.c	(revision bf9dc4e2a497f5d183351f2b788ef6d7723ba6cf)
@@ -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,29 +123,155 @@
 	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)
-		return (int) rc;
-
-	if (!result)
-		return 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_triplet_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;
+	
+	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;
+		}
+		base = &res.triplet;
+		
+		*slash = '/';
+	} else {
+		memcpy(component, path + 1, str_size(path));
+	}
+	
+	if (base->fs_handle != child->fs_handle || base->service_id != child->service_id) {
+		rc = EXDEV;
+		goto out;
+	}
+	
+	async_exch_t *exch = vfs_exchange_grab(base->fs_handle);
+	aid_t req = async_send_3(exch, VFS_OUT_LINK, base->service_id, base->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;
+}
+
+/** 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_triplet_t *base, char *path, int lflag, vfs_lookup_res_t *result)
+{
+	assert(base != NULL);
+	assert(path != NULL);
+	
+	sysarg_t rc;
+	
+	if (!base->fs_handle) {
+		rc = ENOENT;
+		goto out;
+	}
+	
+	size_t len;
+	char *npath = canonify(path, &len);
+	if (!npath) {
+		rc = EINVAL;
+		goto out;
+	}
+	path = npath;
+	
+	assert(path[0] == '/');
+	
+	size_t first;
+	
+	plb_entry_t entry;
+	rc = plb_insert_entry(&entry, path, &first, len);
+	if (rc != EOK) {
+		goto out;
+	}
+	
+	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) first, (sysarg_t) len,
+	    (sysarg_t) base->service_id, (sysarg_t) base->index, (sysarg_t) lflag, &answer);
+	async_wait_for(req, &rc);
+	vfs_exchange_release(exch);
+	
+	plb_clear_entry(&entry, first, len);
+	
+	if ((int) rc < 0) {
+		goto out;
+	}
+	
+	if (!result) {
+		rc = EOK;
+		goto out;
+	}
 	
 	result->triplet.fs_handle = (fs_handle_t) rc;
@@ -197,6 +288,10 @@
 	else
 		result->type = VFS_NODE_UNKNOWN;
-	
-	return EOK;
+
+	rc = EOK;
+
+out:
+	DPRINTF("vfs_lookup_internal() with path '%s' returns %d\n", path, rc);
+	return rc;
 }
 
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision d60ce4abaa4bfa71b3cf2c3701c3157ffc9c341f)
+++ uspace/srv/vfs/vfs_ops.c	(revision bf9dc4e2a497f5d183351f2b788ef6d7723ba6cf)
@@ -68,8 +68,5 @@
 FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
 
-vfs_pair_t rootfs = {
-	.fs_handle = 0,
-	.service_id = 0
-};
+vfs_node_t *root = NULL;
 
 static int vfs_mount_internal(ipc_callid_t rid, service_id_t service_id,
@@ -90,87 +87,7 @@
 	/* 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
@@ -181,5 +98,82 @@
 			return ENOENT;
 		}
-	}
+		
+		/*
+		 * 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;
+			
+		/* Add reference to the mounted root. */
+		root = vfs_node_get(&mr_res); 
+		assert(root);
+			
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		async_answer_0(rid, rc);
+		return rc;
+	}
+	
+	/* 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((vfs_triplet_t *) root, mp, L_DIRECTORY, &mp_res);
+	if (rc != EOK) {
+		/* The lookup failed. */
+		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.
+	 */
 	
 	/*
@@ -435,5 +429,5 @@
 	 * Lookup the mounted root and instantiate it.
 	 */
-	rc = vfs_lookup_internal(mp, L_ROOT, &mr_res, NULL);
+	rc = vfs_lookup_internal((vfs_triplet_t *) root, mp, 0, &mr_res);
 	if (rc != EOK) {
 		fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -488,6 +482,5 @@
 		}
 		
-		rootfs.fs_handle = 0;
-		rootfs.service_id = 0;
+		root = NULL;
 	} else {
 		
@@ -499,5 +492,5 @@
 		 */
 		
-		rc = vfs_lookup_internal(mp, L_MP, &mp_res, NULL);
+		rc = vfs_lookup_internal((vfs_triplet_t *) root, mp, L_MP, &mp_res);
 		if (rc != EOK) {
 			fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -624,5 +617,5 @@
 	/* Lookup the file structure corresponding to the file descriptor. */
 	vfs_file_t *parent = NULL;
-	vfs_pair_t *parent_node = NULL;
+	vfs_node_t *parent_node = root;
 	// TODO: Client-side root.
 	if (parentfd != -1) {
@@ -633,5 +626,5 @@
 			return;
 		}
-		parent_node = (vfs_pair_t *)parent->node;
+		parent_node = parent->node;
 	}
 	
@@ -639,5 +632,5 @@
 	
 	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal(path, walk_lookup_flags(flags), &lr, parent_node);
+	rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, walk_lookup_flags(flags), &lr);
 	free(path);
 
@@ -1069,5 +1062,5 @@
 	vfs_file_t *parent = NULL;
 	vfs_file_t *expect = NULL;
-	vfs_pair_t *parent_node = NULL;
+	vfs_node_t *parent_node = root;
 	
 	int parentfd = IPC_GET_ARG1(*request);
@@ -1091,5 +1084,5 @@
 			goto exit;
 		}
-		parent_node = (vfs_pair_t *)parent->node;
+		parent_node = parent->node;
 	}
 	
@@ -1102,5 +1095,5 @@
 		
 		vfs_lookup_res_t lr;
-		rc = vfs_lookup_internal(path, lflag, &lr, parent_node);
+		rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, lflag, &lr);
 		if (rc != EOK) {
 			goto exit;
@@ -1117,5 +1110,5 @@
 	
 	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal(path, lflag | L_UNLINK, &lr, parent_node);
+	rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, lflag | L_UNLINK, &lr);
 	if (rc != EOK) {
 		goto exit;
@@ -1201,5 +1194,5 @@
 	
 	/* Lookup the node belonging to the old file name. */
-	rc = vfs_lookup_internal(oldc, L_NONE, &old_lr, NULL);
+	rc = vfs_lookup_internal((vfs_triplet_t *) root, oldc, L_NONE, &old_lr);
 	if (rc != EOK) {
 		fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -1237,5 +1230,5 @@
 	
 	/* Lookup parent of the new file name. */
-	rc = vfs_lookup_internal(parentc, L_NONE, &new_par_lr, NULL);
+	rc = vfs_lookup_internal((vfs_triplet_t *) root, parentc, L_NONE, &new_par_lr);
 	free(parentc);	/* not needed anymore */
 	if (rc != EOK) {
@@ -1261,5 +1254,5 @@
 	/* Destroy the old link for the new name. */
 	vfs_node_t *new_node = NULL;
-	rc = vfs_lookup_internal(newc, L_UNLINK, &new_lr, NULL);
+	rc = vfs_lookup_internal((vfs_triplet_t *) root, newc, L_UNLINK, &new_lr);
 	
 	switch (rc) {
@@ -1291,5 +1284,5 @@
 	
 	/* Create the new link for the new name. */
-	rc = vfs_lookup_internal(newc, L_LINK, NULL, NULL, old_node->index);
+	rc = vfs_link_internal((vfs_triplet_t *) root, newc, (vfs_triplet_t *) old_node);
 	if (rc != EOK) {
 		fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -1308,5 +1301,5 @@
 	
 	/* Destroy the link for the old name. */
-	rc = vfs_lookup_internal(oldc, L_UNLINK, NULL, NULL);
+	rc = vfs_lookup_internal((vfs_triplet_t *) root, oldc, L_UNLINK, NULL);
 	if (rc != EOK) {
 		fibril_rwlock_write_unlock(&namespace_rwlock);
