Index: uspace/lib/libc/Makefile
===================================================================
--- uspace/lib/libc/Makefile	(revision 9bb85f3480d8697d332ae4f46dabcd45bc5ab0c5)
+++ uspace/lib/libc/Makefile	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
@@ -75,5 +75,6 @@
 	generic/stdlib.c \
 	generic/mman.c \
-	generic/vfs.c
+	generic/vfs/vfs.c \
+	generic/vfs/canonify.c
 
 ARCH_SOURCES += \
Index: uspace/lib/libc/generic/vfs.c
===================================================================
--- uspace/lib/libc/generic/vfs.c	(revision 9bb85f3480d8697d332ae4f46dabcd45bc5ab0c5)
+++ 	(revision )
@@ -1,382 +1,0 @@
-/*
- * Copyright (c) 2008 Jakub Jermar 
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup libc
- * @{
- */
-/** @file
- */
- 
-#include <vfs.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <ipc/ipc.h>
-#include <ipc/services.h>
-#include <async.h>
-#include <atomic.h>
-#include <futex.h>
-#include <errno.h>
-#include <string.h>
-#include "../../srv/vfs/vfs.h"
-
-int vfs_phone = -1;
-atomic_t vfs_phone_futex = FUTEX_INITIALIZER;
-
-static int vfs_connect(void)
-{
-	if (vfs_phone < 0)
-		vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
-	return vfs_phone;
-}
-
-int mount(const char *fs_name, const char *mp, const char *dev)
-{
-	int res;
-	ipcarg_t rc;
-	aid_t req;
-
-	int dev_handle = 0;	/* TODO */
-
-	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_MOUNT, dev_handle, NULL);
-	rc = ipc_data_write_start(vfs_phone, (void *)fs_name, strlen(fs_name));
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		return (int) rc;
-	}
-	rc = ipc_data_write_start(vfs_phone, (void *)mp, strlen(mp));
-	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 (int) rc;
-}
-
-static int _open(const char *path, int lflag, int oflag, ...)
-{
-	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_3(vfs_phone, VFS_OPEN, lflag, oflag, 0, &answer);
-	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 (int) IPC_GET_ARG1(answer);
-}
-
-int open(const char *path, int oflag, ...)
-{
-	return _open(path, L_FILE, oflag);
-}
-
-int close(int fildes)
-{
-	int res;
-	ipcarg_t rc;
-
-	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;
-		}
-	}
-		
-	rc = async_req_1_0(vfs_phone, VFS_CLOSE, fildes);
-
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-	
-	return (int)rc;
-}
-
-ssize_t read(int fildes, void *buf, size_t nbyte) 
-{
-	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_READ, fildes, &answer);
-	rc = ipc_data_read_start(vfs_phone, (void *)buf, nbyte);
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		return (ssize_t) rc;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-	if (rc == EOK)
-		return (ssize_t) IPC_GET_ARG1(answer);
-	else
-		return -1;
-}
-
-ssize_t write(int fildes, const void *buf, size_t nbyte) 
-{
-	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_WRITE, fildes, &answer);
-	rc = ipc_data_write_start(vfs_phone, (void *)buf, nbyte);
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		return (ssize_t) rc;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-	if (rc == EOK)
-		return (ssize_t) IPC_GET_ARG1(answer);
-	else
-		return -1;
-}
-
-off_t lseek(int fildes, off_t offset, int whence)
-{
-	int res;
-	ipcarg_t rc;
-
-	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;
-		}
-	}
-		
-	off_t newoffs;
-	rc = async_req_3_1(vfs_phone, VFS_SEEK, fildes, offset, whence,
-	    (ipcarg_t)&newoffs);
-
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-
-	if (rc != EOK)
-		return (off_t) -1;
-	
-	return newoffs;
-}
-
-int ftruncate(int fildes, off_t length)
-{
-	int res;
-	ipcarg_t rc;
-	
-	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;
-		}
-	}
-	rc = async_req_2_0(vfs_phone, VFS_TRUNCATE, fildes, length);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-	return (int) rc;
-}
-
-DIR *opendir(const char *dirname)
-{
-	DIR *dirp = malloc(sizeof(DIR));
-	if (!dirp)
-		return NULL;
-	dirp->fd = _open(dirname, L_DIRECTORY, 0);
-	if (dirp->fd < 0) {
-		free(dirp);
-		return NULL;
-	}
-	return dirp;
-}
-
-struct dirent *readdir(DIR *dirp)
-{
-	ssize_t len = read(dirp->fd, &dirp->res.d_name[0], NAME_MAX + 1);
-	if (len <= 0)
-		return NULL;
-	return &dirp->res;
-}
-
-void rewinddir(DIR *dirp)
-{
-	(void) lseek(dirp->fd, 0, SEEK_SET);
-}
-
-int closedir(DIR *dirp)
-{
-	(void) close(dirp->fd);
-	free(dirp);
-	return 0;
-}
-
-int mkdir(const char *path, mode_t mode)
-{
-	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_1(vfs_phone, VFS_MKDIR, mode, 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; 
-}
-
-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/generic/vfs/canonify.c
===================================================================
--- uspace/lib/libc/generic/vfs/canonify.c	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
+++ uspace/lib/libc/generic/vfs/canonify.c	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc 
+ * @{
+ */ 
+
+/**
+ * @file
+ * @brief
+ */
+
+#include <stdlib.h>
+
+/** Token types used for tokenization of path. */
+typedef enum {
+	TK_INVALID,
+	TK_SLASH,
+	TK_DOT,
+	TK_DOTDOT,
+	TK_COMP,
+	TK_NUL
+} tokval_t;
+
+typedef struct {
+	tokval_t kind;
+	char *start;
+	char *stop;
+} token_t;
+
+/** Fake up the TK_SLASH token. */
+static token_t slash_token(char *start)
+{
+	token_t ret;
+	ret.kind = TK_SLASH;
+	ret.start = start;
+	ret.stop = start;
+	return ret;
+}
+
+/** Given a token, return the next token. */
+static token_t next_token(token_t *cur)
+{
+	token_t ret;
+
+	if (cur->stop[1] == '\0') {
+		ret.kind = TK_NUL;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '/') {
+		ret.kind = TK_SLASH;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '.' && (!cur->stop[2] || cur->stop[2] == '/')) {
+		ret.kind = TK_DOT;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '.' && cur->stop[2] == '.' &&
+	    (!cur->stop[3] || cur->stop[3] == '/')) {
+		ret.kind = TK_DOTDOT;
+		ret.start = cur->stop + 1;
+		ret.stop = cur->stop + 2;
+		return ret;
+	}
+	unsigned i;
+	for (i = 1; cur->stop[i] && cur->stop[i] != '/'; i++)
+		;
+	ret.kind = TK_COMP;
+	ret.start = &cur->stop[1];
+	ret.stop = &cur->stop[i - 1];
+	return ret;
+}
+
+/** States used by canonify(). */
+typedef enum {
+	S_INI,
+	S_A,
+	S_B,
+	S_C,
+	S_ACCEPT,
+	S_RESTART,
+	S_REJECT
+} state_t;
+
+typedef struct {
+	state_t s;
+	void (* f)(token_t *, token_t *, token_t *);
+} change_state_t;
+
+/*
+ * Actions that can be performed when transitioning from one
+ * state of canonify() to another.
+ */
+static void set_first_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	*tfsl = *t;
+}
+static void save_component(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	*tlcomp = *t;
+}
+static void terminate_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	if (tfsl->stop[1])	/* avoid writing to a well-formatted path */
+		tfsl->stop[1] = '\0';
+}
+static void remove_trailing_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	t->start[-1] = '\0';
+}
+/** Eat the extra '/'..
+ *
+ * @param t		The current TK_SLASH token.
+ */
+static void shift_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = t->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+/** Eat the extra '.'.
+ *
+ * @param t		The current TK_DOT token.
+ */
+static void shift_dot(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = t->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+/** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
+ *
+ * @param t		The current TK_DOTDOT token.
+ * @param tlcomp	The last TK_COMP token.
+ */
+static void shift_dotdot(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = tlcomp->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+
+/** Transition function for canonify(). */
+static change_state_t trans[4][6] = {
+	[S_INI] = {
+		[TK_SLASH] = {
+			.s = S_A,
+			.f = set_first_slash,
+		},
+		[TK_DOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_NUL] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_A] = {
+		[TK_SLASH] = {
+			.s = S_A,
+			.f = set_first_slash,
+		},
+		[TK_DOT] = {
+			.s = S_A,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_A,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_B,
+			.f = save_component,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = terminate_slash,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_B] = {
+		[TK_SLASH] = {
+			.s = S_C,
+			.f = NULL,
+		},
+		[TK_DOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = NULL,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_C] = {
+		[TK_SLASH] = {
+			.s = S_RESTART,
+			.f = shift_slash,
+		},
+		[TK_DOT] = {
+			.s = S_RESTART,
+			.f = shift_dot,
+		},
+		[TK_DOTDOT] = {
+			.s = S_RESTART,
+			.f = shift_dotdot,
+		},
+		[TK_COMP] = {
+			.s = S_B,
+			.f = save_component,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = remove_trailing_slash,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	}
+};
+
+/** Canonify a file system path.
+ *
+ * A file system path is canonical, if the following holds:
+ * 1) the path is absolute (i.e. a/b/c is not canonical)
+ * 2) there is no trailing slash in the path (i.e. /a/b/c is not canonical)
+ * 3) there is no extra slash in the path (i.e. /a//b/c is not canonical)
+ * 4) there is no '.' component in the path (i.e. /a/./b/c is not canonical)
+ * 5) there is no '..' component in the path (i.e. /a/b/../c is not canonical) 
+ *
+ * This function makes a potentially non-canonical file system path canonical.
+ * It works in-place and requires a NULL-terminated input string.
+ *
+ * @param path		Path to be canonified.
+ * @param lenp		Pointer where the length of the final path will be
+ *			stored. Can be NULL.
+ *
+ * @return		Canonified path or NULL on failure.
+ */
+char *canonify(char *path, size_t *lenp)
+{
+	state_t state;
+	token_t t;
+	token_t tfsl;		/* first slash */
+	token_t tlcomp;		/* last component */
+	if (*path != '/')
+		return NULL;
+	tfsl = slash_token(path);
+restart:
+	state = S_INI;
+	t = tfsl;
+	tlcomp = tfsl;
+	while (state != S_ACCEPT && state != S_RESTART && state != S_REJECT) {
+		if (trans[state][t.kind].f)
+			trans[state][t.kind].f(&t, &tfsl, &tlcomp);
+		state = trans[state][t.kind].s;
+		t = next_token(&t);
+	}
+	
+	switch (state) {
+	case S_RESTART:
+		goto restart;
+	case S_REJECT:
+		return NULL;
+	case S_ACCEPT:
+		if (lenp)
+			*lenp = (size_t)((tlcomp.stop - tfsl.start) + 1);
+		return tfsl.start; 
+	default:
+		abort();
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/libc/generic/vfs/vfs.c
===================================================================
--- uspace/lib/libc/generic/vfs/vfs.c	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
+++ uspace/lib/libc/generic/vfs/vfs.c	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+ 
+#include <vfs.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ipc/ipc.h>
+#include <ipc/services.h>
+#include <async.h>
+#include <atomic.h>
+#include <futex.h>
+#include <errno.h>
+#include <string.h>
+#include "../../srv/vfs/vfs.h"
+
+int vfs_phone = -1;
+atomic_t vfs_phone_futex = FUTEX_INITIALIZER;
+
+static int vfs_connect(void)
+{
+	if (vfs_phone < 0)
+		vfs_phone = ipc_connect_me_to(PHONE_NS, SERVICE_VFS, 0, 0);
+	return vfs_phone;
+}
+
+int mount(const char *fs_name, const char *mp, const char *dev)
+{
+	int res;
+	ipcarg_t rc;
+	aid_t req;
+
+	int dev_handle = 0;	/* TODO */
+
+	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_MOUNT, dev_handle, NULL);
+	rc = ipc_data_write_start(vfs_phone, (void *)fs_name, strlen(fs_name));
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		return (int) rc;
+	}
+	rc = ipc_data_write_start(vfs_phone, (void *)mp, strlen(mp));
+	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 (int) rc;
+}
+
+static int _open(const char *path, int lflag, int oflag, ...)
+{
+	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_3(vfs_phone, VFS_OPEN, lflag, oflag, 0, &answer);
+	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 (int) IPC_GET_ARG1(answer);
+}
+
+int open(const char *path, int oflag, ...)
+{
+	return _open(path, L_FILE, oflag);
+}
+
+int close(int fildes)
+{
+	int res;
+	ipcarg_t rc;
+
+	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;
+		}
+	}
+		
+	rc = async_req_1_0(vfs_phone, VFS_CLOSE, fildes);
+
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	return (int)rc;
+}
+
+ssize_t read(int fildes, void *buf, size_t nbyte) 
+{
+	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_READ, fildes, &answer);
+	rc = ipc_data_read_start(vfs_phone, (void *)buf, nbyte);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		return (ssize_t) rc;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	if (rc == EOK)
+		return (ssize_t) IPC_GET_ARG1(answer);
+	else
+		return -1;
+}
+
+ssize_t write(int fildes, const void *buf, size_t nbyte) 
+{
+	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_WRITE, fildes, &answer);
+	rc = ipc_data_write_start(vfs_phone, (void *)buf, nbyte);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		return (ssize_t) rc;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	if (rc == EOK)
+		return (ssize_t) IPC_GET_ARG1(answer);
+	else
+		return -1;
+}
+
+off_t lseek(int fildes, off_t offset, int whence)
+{
+	int res;
+	ipcarg_t rc;
+
+	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;
+		}
+	}
+		
+	off_t newoffs;
+	rc = async_req_3_1(vfs_phone, VFS_SEEK, fildes, offset, whence,
+	    (ipcarg_t)&newoffs);
+
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+
+	if (rc != EOK)
+		return (off_t) -1;
+	
+	return newoffs;
+}
+
+int ftruncate(int fildes, off_t length)
+{
+	int res;
+	ipcarg_t rc;
+	
+	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;
+		}
+	}
+	rc = async_req_2_0(vfs_phone, VFS_TRUNCATE, fildes, length);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	return (int) rc;
+}
+
+DIR *opendir(const char *dirname)
+{
+	DIR *dirp = malloc(sizeof(DIR));
+	if (!dirp)
+		return NULL;
+	dirp->fd = _open(dirname, L_DIRECTORY, 0);
+	if (dirp->fd < 0) {
+		free(dirp);
+		return NULL;
+	}
+	return dirp;
+}
+
+struct dirent *readdir(DIR *dirp)
+{
+	ssize_t len = read(dirp->fd, &dirp->res.d_name[0], NAME_MAX + 1);
+	if (len <= 0)
+		return NULL;
+	return &dirp->res;
+}
+
+void rewinddir(DIR *dirp)
+{
+	(void) lseek(dirp->fd, 0, SEEK_SET);
+}
+
+int closedir(DIR *dirp)
+{
+	(void) close(dirp->fd);
+	free(dirp);
+	return 0;
+}
+
+int mkdir(const char *path, mode_t mode)
+{
+	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_1(vfs_phone, VFS_MKDIR, mode, 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; 
+}
+
+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/vfs/canonify.h
===================================================================
--- uspace/lib/libc/include/vfs/canonify.h	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
+++ uspace/lib/libc/include/vfs/canonify.h	(revision dadcec1dfd773eb63e3a2420db90b31207fcc6a2)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_VFS_CANONIFY_H_
+#define LIBC_VFS_CANONIFY_H_
+
+extern char *canonify(char *, size_t *);
+
+#endif
+
+/** @}
+ */
