Index: uspace/app/getvc/getvc.c
===================================================================
--- uspace/app/getvc/getvc.c	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/app/getvc/getvc.c	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -36,4 +36,5 @@
 
 #include <sys/types.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -46,13 +47,24 @@
 }
 
-static void closeall(void)
+static void reopen(FILE **stream, int fd, const char *path, int flags, const char *mode)
 {
-	fclose(stdin);
-	fclose(stdout);
-	fclose(stderr);
+	if (fclose(*stream))
+		return;
 	
-	close(0);
-	close(1);
-	close(2);
+	*stream = NULL;
+	
+	int oldfd = open(path, flags);
+	if (oldfd < 0)
+		return;
+	
+	if (oldfd != fd) {
+		if (dup2(oldfd, fd) != fd)
+			return;
+		
+		if (close(oldfd))
+			return;
+	}
+	
+	*stream = fdopen(fd, mode);
 }
 
@@ -74,7 +86,4 @@
 int main(int argc, char *argv[])
 {
-	task_exit_t texit;
-	int retval;
-
 	if (argc < 3) {
 		usage();
@@ -82,23 +91,28 @@
 	}
 	
-	closeall();
+	reopen(&stdin, 0, argv[1], O_RDONLY, "r");
+	reopen(&stdout, 1, argv[1], O_WRONLY, "w");
+	reopen(&stderr, 2, argv[1], O_WRONLY, "w");
 	
-	stdin = fopen(argv[1], "r");
-	stdout = fopen(argv[1], "w");
-	stderr = fopen(argv[1], "w");
-
 	/*
-	 * FIXME: fopen() should actually detect that we are opening a console
+	 * FIXME: fdopen() should actually detect that we are opening a console
 	 * and it should set line-buffering mode automatically.
 	 */
 	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
 	
-	if ((stdin == NULL)
-	    || (stdout == NULL)
-	    || (stderr == NULL))
+	if (stdin == NULL)
 		return -2;
+	
+	if (stdout == NULL)
+		return -3;
+	
+	if (stderr == NULL)
+		return -4;
 	
 	version_print(argv[1]);
 	task_id_t id = spawn(argv[2]);
+	
+	task_exit_t texit;
+	int retval;
 	task_wait(id, &texit, &retval);
 	
Index: uspace/lib/libc/generic/vfs/vfs.c
===================================================================
--- uspace/lib/libc/generic/vfs/vfs.c	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/lib/libc/generic/vfs/vfs.c	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -57,7 +57,7 @@
 static futex_t cwd_futex = FUTEX_INITIALIZER;
 
-DIR *cwd_dir = NULL;
-char *cwd_path = NULL;
-size_t cwd_size = 0;
+static int cwd_fd = -1;
+static char *cwd_path = NULL;
+static size_t cwd_size = 0;
 
 char *absolutize(const char *path, size_t *retlen)
@@ -197,37 +197,30 @@
 }
 
-static int _open(const char *path, int lflag, int oflag, ...)
-{
-	ipcarg_t rc;
+static int open_internal(const char *abs, size_t abs_size, int lflag, int oflag)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
 	ipc_call_t answer;
-	aid_t req;
-	
-	size_t pa_size;
-	char *pa = absolutize(path, &pa_size);
-	if (!pa)
-		return ENOMEM;
-	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
-	
-	req = async_send_3(vfs_phone, VFS_IN_OPEN, lflag, oflag, 0, &answer);
-	rc = async_data_write_start(vfs_phone, pa, pa_size);
+	aid_t req = async_send_3(vfs_phone, VFS_IN_OPEN, lflag, oflag, 0, &answer);
+	ipcarg_t rc = async_data_write_start(vfs_phone, abs, abs_size);
+	
 	if (rc != EOK) {
 		ipcarg_t rc_orig;
-	
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		free(pa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
-	free(pa);
+		async_wait_for(req, &rc_orig);
+		
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
 	
 	if (rc != EOK)
@@ -239,5 +232,13 @@
 int open(const char *path, int oflag, ...)
 {
-	return _open(path, L_FILE, oflag);
+	size_t abs_size;
+	char *abs = absolutize(path, &abs_size);
+	if (!abs)
+		return ENOMEM;
+	
+	int ret = open_internal(abs, abs_size, L_FILE, oflag);
+	free(abs);
+	
+	return ret;
 }
 
@@ -471,9 +472,21 @@
 	if (!dirp)
 		return NULL;
-	dirp->fd = _open(dirname, L_DIRECTORY, 0);
-	if (dirp->fd < 0) {
+	
+	size_t abs_size;
+	char *abs = absolutize(dirname, &abs_size);
+	if (!abs) {
+		free(dirp);
+		return ENOMEM;
+	}
+	
+	int ret = open_internal(abs, abs_size, L_DIRECTORY, 0);
+	free(abs);
+	
+	if (ret < 0) {
 		free(dirp);
 		return NULL;
 	}
+	
+	dirp->fd = ret;
 	return dirp;
 }
@@ -636,26 +649,29 @@
 int chdir(const char *path)
 {
-	size_t pa_size;
-	char *pa = absolutize(path, &pa_size);
-	if (!pa)
-		return ENOMEM;
-
-	DIR *d = opendir(pa);
-	if (!d) {
-		free(pa);
+	size_t abs_size;
+	char *abs = absolutize(path, &abs_size);
+	if (!abs)
+		return ENOMEM;
+	
+	int fd = open_internal(abs, abs_size, L_DIRECTORY, O_DESC);
+	
+	if (fd < 0) {
+		free(abs);
 		return ENOENT;
 	}
-
+	
 	futex_down(&cwd_futex);
-	if (cwd_dir) {
-		closedir(cwd_dir);
-		cwd_dir = NULL;
-		free(cwd_path);	
-		cwd_path = NULL;
-		cwd_size = 0;
-	}
-	cwd_dir = d;
-	cwd_path = pa;
-	cwd_size = pa_size;
+	
+	if (cwd_fd >= 0)
+		close(cwd_fd);
+	
+	
+	if (cwd_path)
+		free(cwd_path);
+	
+	cwd_fd = fd;
+	cwd_path = abs;
+	cwd_size = abs_size;
+	
 	futex_up(&cwd_futex);
 	return EOK;
@@ -664,15 +680,17 @@
 char *getcwd(char *buf, size_t size)
 {
-	if (!cwd_size)
+	if (size == 0)
 		return NULL;
-	if (!size)
-		return NULL;
+	
 	futex_down(&cwd_futex);
-	if (size < cwd_size + 1) {
+	
+	if ((cwd_size == 0) || (size < cwd_size + 1)) {
 		futex_up(&cwd_futex);
 		return NULL;
 	}
+	
 	str_cpy(buf, size, cwd_path);
 	futex_up(&cwd_futex);
+	
 	return buf;
 }
@@ -707,4 +725,22 @@
 }
 
+int dup2(int oldfd, int newfd)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipcarg_t ret;
+	ipcarg_t rc = async_req_2_1(vfs_phone, VFS_IN_DUP, oldfd, newfd, &ret);
+	
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	if (rc == EOK)
+		return (int) ret;
+	
+	return (int) rc;
+}
+
 /** @}
  */
Index: uspace/lib/libc/include/fcntl.h
===================================================================
--- uspace/lib/libc/include/fcntl.h	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/lib/libc/include/fcntl.h	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -43,4 +43,5 @@
 #define O_RDWR    32
 #define O_WRONLY  64
+#define O_DESC    128
 
 extern int open(const char *, int, ...);
Index: uspace/lib/libc/include/ipc/vfs.h
===================================================================
--- uspace/lib/libc/include/ipc/vfs.h	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/lib/libc/include/ipc/vfs.h	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -73,5 +73,6 @@
 	VFS_IN_UNLINK,
 	VFS_IN_RENAME,
-	VFS_IN_STAT
+	VFS_IN_STAT,
+	VFS_IN_DUP
 } vfs_in_request_t;
 
Index: uspace/lib/libc/include/unistd.h
===================================================================
--- uspace/lib/libc/include/unistd.h	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/lib/libc/include/unistd.h	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -51,4 +51,6 @@
 #endif
 
+extern int dup2(int oldfd, int newfd);
+
 extern ssize_t write(int, const void *, size_t);
 extern ssize_t read(int, void *, size_t);
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/srv/vfs/vfs.c	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -126,4 +126,6 @@
 			vfs_sync(callid, &call);
 			break;
+		case VFS_IN_DUP:
+			vfs_dup(callid, &call);
 		default:
 			ipc_answer_0(callid, ENOTSUP);
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/srv/vfs/vfs.h	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -186,5 +186,6 @@
 extern bool vfs_files_init(void);
 extern vfs_file_t *vfs_file_get(int);
-extern int vfs_fd_alloc(void);
+extern int vfs_fd_assign(vfs_file_t *file, int fd);
+extern int vfs_fd_alloc(bool desc);
 extern int vfs_fd_free(int);
 
@@ -200,4 +201,5 @@
 extern void vfs_open_node(ipc_callid_t, ipc_call_t *);
 extern void vfs_sync(ipc_callid_t, ipc_call_t *);
+extern void vfs_dup(ipc_callid_t, ipc_call_t *);
 extern void vfs_close(ipc_callid_t, ipc_call_t *);
 extern void vfs_read(ipc_callid_t, ipc_call_t *);
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/srv/vfs/vfs_file.c	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -76,8 +76,11 @@
 /** Allocate a file descriptor.
  *
- * @return		First available file descriptor or a negative error
- *			code.
- */
-int vfs_fd_alloc(void)
+ * @param desc If true, look for an available file descriptor
+ *             in a descending order.
+ *
+ * @return First available file descriptor or a negative error
+ *         code.
+ */
+int vfs_fd_alloc(bool desc)
 {
 	if (!vfs_files_init())
@@ -85,5 +88,10 @@
 	
 	unsigned int i;
-	for (i = 0; i < MAX_OPEN_FILES; i++) {
+	if (desc)
+		i = MAX_OPEN_FILES;
+	else
+		i = 0;
+	
+	while (true) {
 		if (!files[i]) {
 			files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
@@ -96,4 +104,16 @@
 			return (int) i;
 		}
+		
+		if (desc) {
+			if (i == 0)
+				break;
+			
+			i--;
+		} else {
+			if (i == MAX_OPEN_FILES)
+				break;
+			
+			i++;
+		}
 	}
 	
@@ -118,4 +138,27 @@
 	vfs_file_delref(files[fd]);
 	files[fd] = NULL;
+	
+	return EOK;
+}
+
+/** Assign a file to a file descriptor.
+ *
+ * @param file File to assign.
+ * @param fd   File descriptor to assign to.
+ *
+ * @return EOK on success or EINVAL if fd is an invalid or already
+ *         used file descriptor.
+ *
+ */
+int vfs_fd_assign(vfs_file_t *file, int fd)
+{
+	if (!vfs_files_init())
+		return ENOMEM;
+	
+	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (files[fd] != NULL))
+		return EINVAL;
+	
+	files[fd] = file;
+	vfs_file_addref(files[fd]);
 	
 	return EOK;
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision 14ecd6ca692c56647bb3b09f7c3460affaef67e3)
+++ uspace/srv/vfs/vfs_ops.c	(revision 2b88074b388ca2afaa99dfb793b82d8753cc5f24)
@@ -543,5 +543,5 @@
 	 * structure.
 	 */
-	int fd = vfs_fd_alloc();
+	int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
 	if (fd < 0) {
 		vfs_node_put(node);
@@ -620,5 +620,5 @@
 	 * structure.
 	 */
-	int fd = vfs_fd_alloc();
+	int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
 	if (fd < 0) {
 		vfs_node_put(node);
@@ -679,4 +679,39 @@
 }
 
+static int vfs_close_internal(vfs_file_t *file)
+{
+	/*
+	 * Lock the open file structure so that no other thread can manipulate
+	 * the same open file at a time.
+	 */
+	fibril_mutex_lock(&file->lock);
+	
+	if (file->refcnt <= 1) {
+		/* Only close the file on the destination FS server
+		   if there are no more file descriptors (except the
+		   present one) pointing to this file. */
+		
+		int fs_phone = vfs_grab_phone(file->node->fs_handle);
+		
+		/* Make a VFS_OUT_CLOSE request at the destination FS server. */
+		aid_t msg;
+		ipc_call_t answer;
+		msg = async_send_2(fs_phone, VFS_OUT_CLOSE, file->node->dev_handle,
+		    file->node->index, &answer);
+		
+		/* Wait for reply from the FS server. */
+		ipcarg_t rc;
+		async_wait_for(msg, &rc);
+		
+		vfs_release_phone(fs_phone);
+		fibril_mutex_unlock(&file->lock);
+		
+		return IPC_GET_ARG1(answer);
+	}
+	
+	fibril_mutex_unlock(&file->lock);
+	return EOK;
+}
+
 void vfs_close(ipc_callid_t rid, ipc_call_t *request)
 {
@@ -690,30 +725,10 @@
 	}
 	
-	/*
-	 * Lock the open file structure so that no other thread can manipulate
-	 * the same open file at a time.
-	 */
-	fibril_mutex_lock(&file->lock);
-	int fs_phone = vfs_grab_phone(file->node->fs_handle);
-	
-	/* Make a VFS_OUT_CLOSE request at the destination FS server. */
-	aid_t msg;
-	ipc_call_t answer;
-	msg = async_send_2(fs_phone, VFS_OUT_CLOSE, file->node->dev_handle,
-	    file->node->index, &answer);
-
-	/* Wait for reply from the FS server. */
-	ipcarg_t rc;
-	async_wait_for(msg, &rc);
-
-	vfs_release_phone(fs_phone);
-	fibril_mutex_unlock(&file->lock);
-	
-	int retval = IPC_GET_ARG1(answer);
-	if (retval != EOK)
-		ipc_answer_0(rid, retval);
-	
-	retval = vfs_fd_free(fd);
-	ipc_answer_0(rid, retval);
+	int ret = vfs_close_internal(file);
+	if (ret != EOK)
+		ipc_answer_0(rid, ret);
+	
+	ret = vfs_fd_free(fd);
+	ipc_answer_0(rid, ret);
 }
 
@@ -1310,4 +1325,55 @@
 }
 
+void vfs_dup(ipc_callid_t rid, ipc_call_t *request)
+{
+	int oldfd = IPC_GET_ARG1(*request);
+	int newfd = IPC_GET_ARG2(*request);
+	
+	/* Lookup the file structure corresponding to oldfd. */
+	vfs_file_t *oldfile = vfs_file_get(oldfd);
+	if (!oldfile) {
+		ipc_answer_0(rid, EBADF);
+		return;
+	}
+	
+	/* If the file descriptors are the same, do nothing. */
+	if (oldfd == newfd) {
+		ipc_answer_1(rid, EOK, newfd);
+		return;
+	}
+	
+	/*
+	 * Lock the open file structure so that no other thread can manipulate
+	 * the same open file at a time.
+	 */
+	fibril_mutex_lock(&oldfile->lock);
+	
+	/* Lookup an open file structure possibly corresponding to newfd. */
+	vfs_file_t *newfile = vfs_file_get(newfd);
+	if (newfile) {
+		/* Close the originally opened file. */
+		int ret = vfs_close_internal(newfile);
+		if (ret != EOK) {
+			ipc_answer_0(rid, ret);
+			return;
+		}
+		
+		ret = vfs_fd_free(newfd);
+		if (ret != EOK) {
+			ipc_answer_0(rid, ret);
+			return;
+		}
+	}
+	
+	/* Assign the old file to newfd. */
+	int ret = vfs_fd_assign(oldfile, newfd);
+	fibril_mutex_unlock(&oldfile->lock);
+	
+	if (ret != EOK)
+		ipc_answer_0(rid, ret);
+	else
+		ipc_answer_1(rid, EOK, newfd);
+}
+
 /**
  * @}
