Index: uspace/srv/fs/devfs/devfs.c
===================================================================
--- uspace/srv/fs/devfs/devfs.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/devfs/devfs.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -75,4 +75,10 @@
 			devfs_mount(callid, &call);
 			break;
+		case VFS_OUT_UNMOUNTED:
+			devfs_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			devfs_unmount(callid, &call);
+			break;
 		case VFS_OUT_LOOKUP:
 			devfs_lookup(callid, &call);
Index: uspace/srv/fs/devfs/devfs_ops.c
===================================================================
--- uspace/srv/fs/devfs/devfs_ops.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/devfs/devfs_ops.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -434,4 +434,14 @@
 }
 
+void devfs_unmounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	ipc_answer_0(rid, ENOTSUP);
+}
+
+void devfs_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_unmount(&devfs_libfs_ops, rid, request);
+}
+
 void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
 {
Index: uspace/srv/fs/devfs/devfs_ops.h
===================================================================
--- uspace/srv/fs/devfs/devfs_ops.h	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/devfs/devfs_ops.h	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -41,4 +41,6 @@
 extern void devfs_mounted(ipc_callid_t, ipc_call_t *);
 extern void devfs_mount(ipc_callid_t, ipc_call_t *);
+extern void devfs_unmounted(ipc_callid_t, ipc_call_t *);
+extern void devfs_unmount(ipc_callid_t, ipc_call_t *);
 extern void devfs_lookup(ipc_callid_t, ipc_call_t *);
 extern void devfs_open_node(ipc_callid_t, ipc_call_t *);
Index: uspace/srv/fs/fat/fat.c
===================================================================
--- uspace/srv/fs/fat/fat.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/fat/fat.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -100,4 +100,10 @@
 			fat_mount(callid, &call);
 			break;
+		case VFS_OUT_UNMOUNTED:
+			fat_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			fat_unmount(callid, &call);
+			break;
 		case VFS_OUT_LOOKUP:
 			fat_lookup(callid, &call);
Index: uspace/srv/fs/fat/fat.h
===================================================================
--- uspace/srv/fs/fat/fat.h	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/fat/fat.h	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -204,4 +204,6 @@
 extern void fat_mounted(ipc_callid_t, ipc_call_t *);
 extern void fat_mount(ipc_callid_t, ipc_call_t *);
+extern void fat_unmounted(ipc_callid_t, ipc_call_t *);
+extern void fat_unmount(ipc_callid_t, ipc_call_t *);
 extern void fat_lookup(ipc_callid_t, ipc_call_t *);
 extern void fat_read(ipc_callid_t, ipc_call_t *);
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -1117,4 +1117,14 @@
 }
 
+void fat_unmounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	ipc_answer_0(rid, ENOTSUP);
+}
+
+void fat_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_unmount(&fat_libfs_ops, rid, request);
+}
+
 void fat_lookup(ipc_callid_t rid, ipc_call_t *request)
 {
Index: uspace/srv/fs/tmpfs/tmpfs.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/tmpfs/tmpfs.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -106,4 +106,10 @@
 			tmpfs_mount(callid, &call);
 			break;
+		case VFS_OUT_UNMOUNTED:
+			tmpfs_unmounted(callid, &call);
+			break;
+		case VFS_OUT_UNMOUNT:
+			tmpfs_unmount(callid, &call);
+			break;
 		case VFS_OUT_LOOKUP:
 			tmpfs_lookup(callid, &call);
Index: uspace/srv/fs/tmpfs/tmpfs.h
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs.h	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/tmpfs/tmpfs.h	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -83,4 +83,6 @@
 extern void tmpfs_mounted(ipc_callid_t, ipc_call_t *);
 extern void tmpfs_mount(ipc_callid_t, ipc_call_t *);
+extern void tmpfs_unmounted(ipc_callid_t, ipc_call_t *);
+extern void tmpfs_unmount(ipc_callid_t, ipc_call_t *);
 extern void tmpfs_lookup(ipc_callid_t, ipc_call_t *);
 extern void tmpfs_read(ipc_callid_t, ipc_call_t *);
Index: uspace/srv/fs/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -147,6 +147,6 @@
 hash_table_t nodes;
 
-#define NODES_KEY_INDEX	0
-#define NODES_KEY_DEV	1
+#define NODES_KEY_DEV	0	
+#define NODES_KEY_INDEX	1
 
 /* Implementation of hash table interface for the nodes hash table. */
@@ -166,4 +166,22 @@
 static void nodes_remove_callback(link_t *item)
 {
+	tmpfs_node_t *nodep = hash_table_get_instance(item, tmpfs_node_t,
+	    nh_link);
+
+	while (!list_empty(&nodep->cs_head)) {
+		tmpfs_dentry_t *dentryp = list_get_instance(nodep->cs_head.next,
+		    tmpfs_dentry_t, link);
+
+		assert(nodep->type == TMPFS_DIRECTORY);
+		list_remove(&dentryp->link);
+		free(dentryp);
+	}
+
+	if (nodep->data) {
+		assert(nodep->type == TMPFS_FILE);
+		free(nodep->data);
+	}
+	free(nodep->bp);
+	free(nodep);
 }
 
@@ -215,4 +233,19 @@
 }
 
+static void tmpfs_instance_done(dev_handle_t dev_handle)
+{
+	unsigned long key[] = {
+		[NODES_KEY_DEV] = dev_handle
+	};
+	/*
+	 * Here we are making use of one special feature of our hash table
+	 * implementation, which allows to remove more items based on a partial
+	 * key match. In the following, we are going to remove all nodes
+	 * matching our device handle. The nodes_remove_callback() function will
+	 * take care of resource deallocation.
+	 */
+	hash_table_remove(&nodes, key, 1);
+}
+
 int tmpfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 {
@@ -237,6 +270,6 @@
 {
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = index,
-		[NODES_KEY_DEV] = dev_handle
+		[NODES_KEY_DEV] = dev_handle,
+		[NODES_KEY_INDEX] = index
 	};
 	link_t *lnk = hash_table_find(&nodes, key);
@@ -296,6 +329,6 @@
 	/* Insert the new node into the nodes hash table. */
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = nodep->index,
-		[NODES_KEY_DEV] = nodep->dev_handle
+		[NODES_KEY_DEV] = nodep->dev_handle,
+		[NODES_KEY_INDEX] = nodep->index
 	};
 	hash_table_insert(&nodes, key, &nodep->nh_link);
@@ -312,13 +345,13 @@
 
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = nodep->index,
-		[NODES_KEY_DEV] = nodep->dev_handle
+		[NODES_KEY_DEV] = nodep->dev_handle,
+		[NODES_KEY_INDEX] = nodep->index
 	};
 	hash_table_remove(&nodes, key, 2);
 
-	if (nodep->type == TMPFS_FILE)
-		free(nodep->data);
-	free(nodep->bp);
-	free(nodep);
+	/*
+	 * The nodes_remove_callback() function takes care of the actual
+	 * resource deallocation.
+	 */
 	return EOK;
 }
@@ -424,4 +457,5 @@
 	/* Initialize TMPFS instance. */
 	if (!tmpfs_instance_init(dev_handle)) {
+		free(opts);
 		ipc_answer_0(rid, ENOMEM);
 		return;
@@ -442,4 +476,5 @@
 		    rootp->lnkcnt);
 	}
+	free(opts);
 }
 
@@ -447,4 +482,17 @@
 {
 	libfs_mount(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
+}
+
+void tmpfs_unmounted(ipc_callid_t rid, ipc_call_t *request)
+{
+	dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
+
+	tmpfs_instance_done(dev_handle);
+	ipc_answer_0(rid, EOK);
+}
+
+void tmpfs_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_unmount(&tmpfs_libfs_ops, rid, request);
 }
 
@@ -465,6 +513,6 @@
 	link_t *hlp;
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = index,
 		[NODES_KEY_DEV] = dev_handle,
+		[NODES_KEY_INDEX] = index
 	};
 	hlp = hash_table_find(&nodes, key);
@@ -539,6 +587,6 @@
 	link_t *hlp;
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = index,
-		[NODES_KEY_DEV] = dev_handle
+		[NODES_KEY_DEV] = dev_handle,
+		[NODES_KEY_INDEX] = index
 	};
 	hlp = hash_table_find(&nodes, key);
@@ -603,6 +651,6 @@
 	link_t *hlp;
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = index,
-		[NODES_KEY_DEV] = dev_handle
+		[NODES_KEY_DEV] = dev_handle,
+		[NODES_KEY_INDEX] = index
 	};
 	hlp = hash_table_find(&nodes, key);
@@ -646,6 +694,6 @@
 	link_t *hlp;
 	unsigned long key[] = {
-		[NODES_KEY_INDEX] = index,
-		[NODES_KEY_DEV] = dev_handle
+		[NODES_KEY_DEV] = dev_handle,
+		[NODES_KEY_INDEX] = index
 	};
 	hlp = hash_table_find(&nodes, key);
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/vfs/vfs.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -86,4 +86,7 @@
 		case VFS_IN_MOUNT:
 			vfs_mount(callid, &call);
+			break;
+		case VFS_IN_UNMOUNT:
+			vfs_unmount(callid, &call);
 			break;
 		case VFS_IN_OPEN:
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/vfs/vfs.h	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -181,4 +181,7 @@
 extern vfs_node_t *vfs_node_get(vfs_lookup_res_t *);
 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, dev_handle_t);
+
 
 #define MAX_OPEN_FILES	128
@@ -198,4 +201,5 @@
 extern void vfs_register(ipc_callid_t, ipc_call_t *);
 extern void vfs_mount(ipc_callid_t, ipc_call_t *);
+extern void vfs_unmount(ipc_callid_t, ipc_call_t *);
 extern void vfs_open(ipc_callid_t, ipc_call_t *);
 extern void vfs_open_node(ipc_callid_t, ipc_call_t *);
Index: uspace/srv/vfs/vfs_node.c
===================================================================
--- uspace/srv/vfs/vfs_node.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/vfs/vfs_node.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -137,4 +137,24 @@
 	if (free_vfs_node)
 		free(node);
+}
+
+/** Forget node.
+ *
+ * This function will remove the node from the node hash table and deallocate
+ * its memory, regardless of the node's reference count.
+ *
+ * @param node	Node to be forgotten.
+ */
+void vfs_node_forget(vfs_node_t *node)
+{
+	fibril_mutex_lock(&nodes_mutex);
+	unsigned long key[] = {
+		[KEY_FS_HANDLE] = node->fs_handle,
+		[KEY_DEV_HANDLE] = node->dev_handle,
+		[KEY_INDEX] = node->index
+	};
+	hash_table_remove(&nodes, key, 3);
+	fibril_mutex_unlock(&nodes_mutex);
+	free(node);
 }
 
@@ -231,4 +251,37 @@
 }
 
+struct refcnt_data {
+	/** Sum of all reference counts for this file system instance. */
+	unsigned refcnt;
+	fs_handle_t fs_handle;
+	dev_handle_t dev_handle;
+};
+
+static void refcnt_visitor(link_t *item, void *arg)
+{
+	vfs_node_t *node = hash_table_get_instance(item, vfs_node_t, nh_link);
+	struct refcnt_data *rd = (void *) arg;
+
+	if ((node->fs_handle == rd->fs_handle) &&
+	    (node->dev_handle == rd->dev_handle))
+		rd->refcnt += node->refcnt;
+}
+
+unsigned
+vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, dev_handle_t dev_handle)
+{
+	struct refcnt_data rd = {
+		.refcnt = 0,
+		.fs_handle = fs_handle,
+		.dev_handle = dev_handle
+	};
+
+	fibril_mutex_lock(&nodes_mutex);
+	hash_table_apply(&nodes, refcnt_visitor, &rd);
+	fibril_mutex_unlock(&nodes_mutex);
+
+	return rd.refcnt;
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision 3698e4420e4bdd79a3251c212fbc1886ecf45436)
+++ uspace/srv/vfs/vfs_ops.c	(revision 231438126d2f4a823b7106c694ea8eecf2d00e54)
@@ -92,5 +92,5 @@
 		}
 		
-		rc = vfs_lookup_internal(mp, L_DIRECTORY, &mp_res, NULL);
+		rc = vfs_lookup_internal(mp, L_MP, &mp_res, NULL);
 		if (rc != EOK) {
 			/* The lookup failed for some reason. */
@@ -429,4 +429,141 @@
 }
 
+void vfs_unmount(ipc_callid_t rid, ipc_call_t *request)
+{
+	int rc;
+	char *mp;
+	vfs_lookup_res_t mp_res;
+	vfs_lookup_res_t mr_res;
+	vfs_node_t *mp_node;
+	vfs_node_t *mr_node;
+	int phone;
+
+	/*
+	 * Receive the mount point path.
+	 */
+	rc = async_data_string_receive(&mp, MAX_PATH_LEN);
+	if (rc != EOK)
+		ipc_answer_0(rid, rc);
+
+	/*
+	 * Taking the namespace lock will do two things for us. First, it will
+	 * prevent races with other lookup operations. Second, it will stop new
+	 * references to already existing VFS nodes and creation of new VFS
+	 * nodes. This is because new references are added as a result of some
+	 * lookup operation or at least of some operation which is protected by
+	 * the namespace lock.
+	 */
+	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);
+		free(mp);
+		ipc_answer_0(rid, rc);
+		return;
+	}
+	mr_node = vfs_node_get(&mr_res);
+	if (!mr_node) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		free(mp);
+		ipc_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->dev_handle) != 2) {
+		fibril_rwlock_write_unlock(&namespace_rwlock);
+		vfs_node_put(mr_node);
+		free(mp);
+		ipc_answer_0(rid, EBUSY);
+		return;
+	}
+
+	if (str_cmp(mp, "/") == 0) {
+
+		/*
+		 * Unmounting the root file system.
+		 *
+		 * In this case, there is no mount point node and we send
+		 * VFS_OUT_UNMOUNTED directly to the mounted file system.
+		 */
+
+		free(mp);
+		phone = vfs_grab_phone(mr_node->fs_handle);
+		rc = async_req_1_0(phone, VFS_OUT_UNMOUNTED,
+		    mr_node->dev_handle);
+		vfs_release_phone(phone);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			vfs_node_put(mr_node);
+			ipc_answer_0(rid, rc);
+			return;
+		}
+		rootfs.fs_handle = 0;
+		rootfs.dev_handle = 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);
+		free(mp);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			vfs_node_put(mr_node);
+			ipc_answer_0(rid, rc);
+			return;
+		}
+		vfs_node_t *mp_node = vfs_node_get(&mp_res);
+		if (!mp_node) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			vfs_node_put(mr_node);
+			ipc_answer_0(rid, ENOMEM);
+			return;
+		}
+
+		phone = vfs_grab_phone(mp_node->fs_handle);
+		rc = async_req_2_0(phone, VFS_OUT_UNMOUNT, mp_node->dev_handle,
+		    mp_node->index);
+		vfs_release_phone(phone);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&namespace_rwlock);
+			vfs_node_put(mp_node);
+			vfs_node_put(mr_node);
+			ipc_answer_0(rid, rc);
+			return;
+		}
+
+		/* Drop the reference we got above. */
+		vfs_node_put(mp_node);
+		/* Drop the reference from when the file system was mounted. */
+		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);
+
+	fibril_rwlock_write_unlock(&namespace_rwlock);
+	ipc_answer_0(rid, EOK);
+}
+
 void vfs_open(ipc_callid_t rid, ipc_call_t *request)
 {
@@ -454,9 +591,10 @@
 	/*
 	 * 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_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) != 0)) {
+	    (lflag & (L_OPEN | L_ROOT | L_MP))) {
 		ipc_answer_0(rid, EINVAL);
 		return;
