Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision e7045039e22decbd208b7b85570324c5de0c9301)
+++ uspace/srv/vfs/vfs.c	(revision 343dc9e3aefbe27389e36a69d1d7829327a7e06c)
@@ -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 343dc9e3aefbe27389e36a69d1d7829327a7e06c)
@@ -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 343dc9e3aefbe27389e36a69d1d7829327a7e06c)
@@ -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);
+}
+
 /**
  * @}
