Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/lib/c/include/ipc/vfs.h	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -95,4 +95,5 @@
 	VFS_OUT_UNMOUNTED,
 	VFS_OUT_GET_SIZE,
+	VFS_OUT_IS_EMPTY,
 	VFS_OUT_SYNC,
 	VFS_OUT_STAT,
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/lib/fs/libfs.c	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -259,4 +259,29 @@
 }
 
+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)
 {
@@ -322,4 +347,7 @@
 		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:
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/srv/vfs/vfs.h	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -100,5 +100,5 @@
  * which may be associated with it.
  */
-typedef struct {
+typedef struct _vfs_node {
 	VFS_TRIPLET;		/**< Identity of the node. */
 
@@ -119,4 +119,6 @@
 	 */
 	fibril_rwlock_t contents_rwlock;
+	
+	struct _vfs_node *mount;
 } vfs_node_t;
 
@@ -176,14 +178,16 @@
 extern vfs_info_t *fs_handle_to_info(fs_handle_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 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);
 
-int64_t vfs_node_get_size(vfs_node_t *node);
+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
Index: uspace/srv/vfs/vfs_lookup.c
===================================================================
--- uspace/srv/vfs/vfs_lookup.c	(revision 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/srv/vfs/vfs_lookup.c	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -153,5 +153,5 @@
 }
 
-int vfs_link_internal(vfs_triplet_t *base, char *path, vfs_triplet_t *child)
+int vfs_link_internal(vfs_node_t *base, char *path, vfs_triplet_t *child)
 {
 	assert(base != NULL);
@@ -173,4 +173,6 @@
 	path = npath;
 	
+	vfs_triplet_t *triplet;
+	
 	char *slash = _strrchr(path, '/');
 	if (slash && slash != path) {
@@ -187,18 +189,24 @@
 			goto out;
 		}
-		base = &res.triplet;
+		triplet = &res.triplet;
 		
 		*slash = '/';
 	} else {
+		if (base->mount != NULL) {
+			rc = EINVAL;
+			goto out;
+		}
+		
 		memcpy(component, path + 1, str_size(path));
-	}
-	
-	if (base->fs_handle != child->fs_handle || base->service_id != child->service_id) {
+		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(base->fs_handle);
-	aid_t req = async_send_3(exch, VFS_OUT_LINK, base->service_id, base->index, child->index, NULL);
+	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);
@@ -215,4 +223,34 @@
 }
 
+static int out_lookup(vfs_triplet_t *base, unsigned *pfirst, unsigned *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;
+	}
+	
+	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 = (int64_t)(int32_t) IPC_GET_ARG4(answer);
+	result->type = IPC_GET_ARG5(answer);
+	return EOK;
+}
+
 /** Perform a path lookup.
  *
@@ -227,21 +265,16 @@
  *
  */
-int vfs_lookup_internal(vfs_triplet_t *base, char *path, int lflag, vfs_lookup_res_t *result)
+int vfs_lookup_internal(vfs_node_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;
+	int rc;
 	char *npath = canonify(path, &len);
 	if (!npath) {
+		DPRINTF("vfs_lookup_internal() can't canonify path: %s\n", path);
 		rc = EINVAL;
-		goto out;
+		return rc;
 	}
 	path = npath;
@@ -254,40 +287,66 @@
 	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);
-	
+		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;
+			}
+		}
+
+		__builtin_memcpy(result, &res, sizeof(vfs_lookup_res_t));
+	}
+	
+out:
 	plb_clear_entry(&entry, first, len);
-	
-	if ((int) rc < 0) {
-		goto out;
-	}
-	
-	unsigned last = IPC_GET_ARG3(answer);
-	if (last != first + len) {
-		/* The path wasn't processed entirely. */
-		rc = ENOENT;
-		goto out;
-	}
-	
-	if (!result) {
-		rc = EOK;
-		goto out;
-	}
-	
-	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 = (int64_t)(int32_t) IPC_GET_ARG4(answer);
-	result->type = IPC_GET_ARG5(answer);
-	rc = EOK;
-
-out:
 	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 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/srv/vfs/vfs_node.c	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -196,4 +196,18 @@
 }
 
+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);
+
+	return node;
+}
+
 /** Return VFS node when no longer needed by the caller.
  *
@@ -317,4 +331,12 @@
 }
 
+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 677745a8f02a62fcdda400d24b3f38e1b8940077)
+++ uspace/srv/vfs/vfs_ops.c	(revision 5bcd5b79a057eba51e703c7e6f5dc7d22317b8eb)
@@ -70,4 +70,42 @@
 vfs_node_t *root = NULL;
 
+#if 0
+static int vfs_attach_internal(vfs_node_t *mpoint, vfs_node_t *mountee)
+{
+	assert(mpoint != NULL);
+	assert(mountee != NULL);
+	
+	if (mpoint->mount != NULL) {
+		return EBUSY;
+	}
+	
+	if (mpoint->type != VFS_NODE_DIRECTORY) {
+		return ENOTDIR;
+	}
+	
+	if (vfs_node_has_children(mpoint)) {
+		return ENOTEMPTY;
+	}
+	
+	mpoint->mount = mountee;
+	vfs_node_addref(mountee);
+	/* Add reference to make sure the node is not freed. Removed in detach_internal(). */
+	vfs_node_addref(mpoint);
+	return EOK;
+}
+
+static int vfs_detach_internal(vfs_node_t *mpoint)
+{
+	assert(mpoint != NULL);
+	
+	if (mpoint->mount == NULL) {
+		return ENOENT;
+	}
+	vfs_node_put(mpoint->mount);
+	mpoint->mount = NULL;
+	vfs_node_put(mpoint);
+}
+#endif
+
 static int vfs_mount_internal(ipc_callid_t rid, service_id_t service_id,
     fs_handle_t fs_handle, char *mp, char *opts)
@@ -153,5 +191,5 @@
 	}
 	
-	rc = vfs_lookup_internal((vfs_triplet_t *) root, mp, L_DIRECTORY, &mp_res);
+	rc = vfs_lookup_internal(root, mp, L_DIRECTORY, &mp_res);
 	if (rc != EOK) {
 		/* The lookup failed. */
@@ -424,5 +462,5 @@
 	 * Lookup the mounted root and instantiate it.
 	 */
-	rc = vfs_lookup_internal((vfs_triplet_t *) root, mp, 0, &mr_res);
+	rc = vfs_lookup_internal(root, mp, 0, &mr_res);
 	if (rc != EOK) {
 		fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -487,5 +525,5 @@
 		 */
 		
-		rc = vfs_lookup_internal((vfs_triplet_t *) root, mp, L_MP, &mp_res);
+		rc = vfs_lookup_internal(root, mp, L_MP, &mp_res);
 		if (rc != EOK) {
 			fibril_rwlock_write_unlock(&namespace_rwlock);
@@ -627,5 +665,5 @@
 	
 	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, walk_lookup_flags(flags), &lr);
+	rc = vfs_lookup_internal(parent_node, path, walk_lookup_flags(flags), &lr);
 	free(path);
 
@@ -1050,4 +1088,12 @@
 }
 
+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)
 {
@@ -1089,5 +1135,5 @@
 		
 		vfs_lookup_res_t lr;
-		rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, lflag, &lr);
+		rc = vfs_lookup_internal(parent_node, path, lflag, &lr);
 		if (rc != EOK) {
 			goto exit;
@@ -1104,15 +1150,13 @@
 	
 	vfs_lookup_res_t lr;
-	rc = vfs_lookup_internal((vfs_triplet_t *) parent_node, path, lflag | L_UNLINK, &lr);
+	rc = vfs_lookup_internal(parent_node, path, lflag | L_UNLINK, &lr);
 	if (rc != EOK) {
 		goto exit;
 	}
 
-	/*
-	 * 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_put(vfs_node_get(&lr));
+	/* If the node is not held by anyone, try to destroy it. */
+	if (vfs_node_peek(&lr) == NULL) {
+		out_destroy(&lr.triplet);
+	}
 
 exit:
@@ -1149,5 +1193,5 @@
 }
 
-static int vfs_rename_internal(vfs_triplet_t *base, char *old, char *new)
+static int vfs_rename_internal(vfs_node_t *base, char *old, char *new)
 {
 	assert(base != NULL);
@@ -1182,8 +1226,10 @@
 		}
 		
-		base = &base_lr.triplet;
+		base = vfs_node_get(&base_lr);
 		old[shared] = '/';
 		old += shared;
 		new += shared;
+	} else {
+		vfs_node_addref(base);
 	}
 	
@@ -1193,4 +1239,5 @@
 		orig_unlinked = true;
 	} else if (rc != ENOENT) {
+		vfs_node_put(base);
 		fibril_rwlock_write_unlock(&namespace_rwlock);
 		return rc;
@@ -1202,4 +1249,5 @@
 			vfs_link_internal(base, new, &new_lr_orig.triplet);
 		}
+		vfs_node_put(base);
 		fibril_rwlock_write_unlock(&namespace_rwlock);
 		return rc;
@@ -1212,12 +1260,15 @@
 			vfs_link_internal(base, new, &new_lr_orig.triplet);
 		}
+		vfs_node_put(base);
 		fibril_rwlock_write_unlock(&namespace_rwlock);
 		return rc;
 	}
 	
-	if (orig_unlinked) {
-		vfs_node_put(vfs_node_get(&new_lr_orig));
-	}
-	
+	/* 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);
 	return EOK;
@@ -1272,5 +1323,5 @@
 	}
 	
-	rc = vfs_rename_internal((vfs_triplet_t *) base_node, oldc, newc);
+	rc = vfs_rename_internal(base_node, oldc, newc);
 
 out:
