Index: uspace/lib/libc/generic/vfs.c
===================================================================
--- uspace/lib/libc/generic/vfs.c	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/lib/libc/generic/vfs.c	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -311,18 +311,17 @@
 	int res;
 	ipcarg_t rc;
-	ipc_call_t answer;
-	aid_t req;
-	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	if (vfs_phone < 0) {
-		res = vfs_connect();
-		if (res < 0) {
-			async_serialize_end();
-			futex_up(&vfs_phone_futex);
-			return res;
-		}
-	}
-	req = async_send_1(vfs_phone, VFS_MKDIR, mode, &answer);
+	aid_t req;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	if (vfs_phone < 0) {
+		res = vfs_connect();
+		if (res < 0) {
+			async_serialize_end();
+			futex_up(&vfs_phone_futex);
+			return res;
+		}
+	}
+	req = async_send_1(vfs_phone, VFS_MKDIR, mode, NULL);
 	rc = ipc_data_write_start(vfs_phone, path, strlen(path));
 	if (rc != EOK) {
@@ -336,4 +335,44 @@
 	futex_up(&vfs_phone_futex);
 	return EOK; 
+}
+
+static int _unlink(const char *path, int lflag)
+{
+	int res;
+	ipcarg_t rc;
+	aid_t req;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	if (vfs_phone < 0) {
+		res = vfs_connect();
+		if (res < 0) {
+			async_serialize_end();
+			futex_up(&vfs_phone_futex);
+			return res;
+		}
+	}
+	req = async_send_0(vfs_phone, VFS_UNLINK, NULL);
+	rc = ipc_data_write_start(vfs_phone, path, strlen(path));
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		return (int) rc;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	return EOK; 
+}
+
+int unlink(const char *path)
+{
+	return _unlink(path, L_NONE);
+}
+
+int rmdir(const char *path)
+{
+	return _unlink(path, L_DIRECTORY);
 }
 
Index: uspace/lib/libc/include/unistd.h
===================================================================
--- uspace/lib/libc/include/unistd.h	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/lib/libc/include/unistd.h	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -51,4 +51,6 @@
 extern int ftruncate(int, off_t);
 extern int close(int);
+extern int unlink(const char *);
+extern int rmdir(const char *);
 
 extern void _exit(int status);
Index: uspace/srv/fs/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -331,8 +331,8 @@
 	/* handle hit */
 	if (lflag & L_DESTROY) {
+		unsigned old_lnkcnt = TMPFS_GET_LNKCNT(dcur);
 		int res = destroy_component(dcur);
-		unsigned lnkcnt = (res == EOK) ? 0 : TMPFS_GET_LNKCNT(dcur);
 		ipc_answer_5(rid, (ipcarg_t)res, tmpfs_reg.fs_handle,
-		    dev_handle, dcur->index, dcur->size, lnkcnt);
+		    dev_handle, dcur->index, dcur->size, old_lnkcnt);
 		return;
 	}
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/srv/vfs/vfs.c	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -112,4 +112,7 @@
 			vfs_mkdir(callid, &call);
 			break;
+		case VFS_UNLINK:
+			vfs_unlink(callid, &call);
+			break;
 		default:
 			ipc_answer_0(callid, ENOTSUP);
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/srv/vfs/vfs.h	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -142,4 +142,8 @@
  */
 /**
+ * No lookup flags used.
+ */
+#define L_NONE		0
+/**
  * Lookup will succeed only if the object is a regular file.  If L_CREATE is
  * specified, an empty file will be created. This flag is mutually exclusive
@@ -276,4 +280,5 @@
 extern void vfs_truncate(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 *);
 
 #endif
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/srv/vfs/vfs_ops.c	(revision f15cf1a69a399d0501f8c0168f4d93c0655b20c5)
@@ -591,6 +591,6 @@
 {
 	int mode = IPC_GET_ARG1(*request);
+
 	size_t len;
-
 	ipc_callid_t callid;
 
@@ -630,4 +630,61 @@
 }
 
+void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
+{
+	int lflag = IPC_GET_ARG1(*request);
+
+	size_t len;
+	ipc_callid_t callid;
+
+	if (!ipc_data_write_receive(&callid, &len)) {
+		ipc_answer_0(callid, EINVAL);
+		ipc_answer_0(rid, EINVAL);
+		return;
+	}
+
+	/*
+	 * Now we are on the verge of accepting the path.
+	 *
+	 * There is one optimization we could do in the future: copy the path
+	 * directly into the PLB using some kind of a callback.
+	 */
+	char *path = malloc(len);
+	
+	if (!path) {
+		ipc_answer_0(callid, ENOMEM);
+		ipc_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	int rc;
+	if ((rc = ipc_data_write_finalize(callid, path, len))) {
+		ipc_answer_0(rid, rc);
+		free(path);
+		return;
+	}
+	
+	rwlock_write_lock(&namespace_rwlock);
+	lflag &= L_DIRECTORY;	/* sanitize lflag */
+	vfs_lookup_res_t lr;
+	rc = vfs_lookup_internal(path, len, lflag | L_DESTROY, &lr, NULL);
+	free(path);
+	if (rc != EOK) {
+		rwlock_write_unlock(&namespace_rwlock);
+		ipc_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_FREE'd after the last reference to it is dropped.
+	 */
+	vfs_node_t *node = vfs_node_get(&lr);
+	node->lnkcnt--;
+	rwlock_write_unlock(&namespace_rwlock);
+	vfs_node_put(node);
+	ipc_answer_0(rid, EOK);
+}
+
 /**
  * @}
