Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision ed18e148046bfdc424427e6fcc350b1718b2628b)
+++ uspace/lib/c/Makefile	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -132,4 +132,5 @@
 	generic/malloc.c \
 	generic/stdio/scanf.c \
+	generic/stdio/sstream.c \
 	generic/sysinfo.c \
 	generic/ipc.c \
Index: uspace/lib/c/generic/io/io.c
===================================================================
--- uspace/lib/c/generic/io/io.c	(revision ed18e148046bfdc424427e6fcc350b1718b2628b)
+++ uspace/lib/c/generic/io/io.c	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -53,4 +53,27 @@
 static void _fflushbuf(FILE *stream);
 
+static size_t stdio_kio_read(void *, size_t, size_t, FILE *);
+static size_t stdio_kio_write(const void *, size_t, size_t, FILE *);
+static int stdio_kio_flush(FILE *);
+
+static size_t stdio_vfs_read(void *, size_t, size_t, FILE *);
+static size_t stdio_vfs_write(const void *, size_t, size_t, FILE *);
+
+static int stdio_vfs_flush(FILE *);
+
+/** KIO stream ops */
+static __stream_ops_t stdio_kio_ops = {
+	.read = stdio_kio_read,
+	.write = stdio_kio_write,
+	.flush = stdio_kio_flush
+};
+
+/** VFS stream ops */
+static __stream_ops_t stdio_vfs_ops = {
+	.read = stdio_vfs_read,
+	.write = stdio_vfs_write,
+	.flush = stdio_vfs_flush
+};
+
 static FILE stdin_null = {
 	.fd = -1,
@@ -58,5 +81,6 @@
 	.error = true,
 	.eof = true,
-	.kio = false,
+	.ops = &stdio_vfs_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IONBF,
@@ -73,5 +97,6 @@
 	.error = false,
 	.eof = false,
-	.kio = true,
+	.ops = &stdio_kio_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IOLBF,
@@ -88,5 +113,6 @@
 	.error = false,
 	.eof = false,
-	.kio = true,
+	.ops = &stdio_kio_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IONBF,
@@ -328,5 +354,6 @@
 	stream->error = false;
 	stream->eof = false;
-	stream->kio = false;
+	stream->ops = &stdio_vfs_ops;
+	stream->arg = NULL;
 	stream->sess = NULL;
 	stream->need_sync = false;
@@ -352,5 +379,6 @@
 	stream->error = false;
 	stream->eof = false;
-	stream->kio = false;
+	stream->ops = &stdio_vfs_ops;
+	stream->arg = NULL;
 	stream->sess = NULL;
 	stream->need_sync = false;
@@ -435,19 +463,5 @@
 static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
 {
-	errno_t rc;
-	size_t nread;
-
-	if (size == 0 || nmemb == 0)
-		return 0;
-
-	rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
-	if (rc != EOK) {
-		errno = rc;
-		stream->error = true;
-	} else if (nread == 0) {
-		stream->eof = true;
-	}
-
-	return (nread / size);
+	return stream->ops->read(buf, size, nmemb, stream);
 }
 
@@ -464,5 +478,4 @@
 static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
 {
-	errno_t rc;
 	size_t nwritten;
 
@@ -470,18 +483,5 @@
 		return 0;
 
-	if (stream->kio) {
-		rc = kio_write(buf, size * nmemb, &nwritten);
-		if (rc != EOK) {
-			stream->error = true;
-			nwritten = 0;
-		}
-	} else {
-		rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb,
-		    &nwritten);
-		if (rc != EOK) {
-			errno = rc;
-			stream->error = true;
-		}
-	}
+	nwritten = stream->ops->write(buf, size, nmemb, stream);
 
 	if (nwritten > 0)
@@ -905,24 +905,13 @@
 	}
 
-	if (stream->kio) {
-		kio_update();
-		return 0;
-	}
-
-	if ((stream->fd >= 0) && (stream->need_sync)) {
-		errno_t rc;
-
+	if (stream->need_sync) {
 		/**
 		 * Better than syncing always, but probably still not the
 		 * right thing to do.
 		 */
+		if (stream->ops->flush(stream) == EOF)
+			return EOF;
+
 		stream->need_sync = false;
-		rc = vfs_sync(stream->fd);
-		if (rc != EOK) {
-			errno = rc;
-			return EOF;
-		}
-
-		return 0;
 	}
 
@@ -948,5 +937,5 @@
 int fileno(FILE *stream)
 {
-	if (stream->kio) {
+	if (stream->ops != &stdio_vfs_ops) {
 		errno = EBADF;
 		return EOF;
@@ -978,4 +967,84 @@
 }
 
+/** Read from KIO stream. */
+static size_t stdio_kio_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	stream->eof = true;
+	return 0;
+}
+
+/** Write to KIO stream. */
+static size_t stdio_kio_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	errno_t rc;
+	size_t nwritten;
+
+	rc = kio_write(buf, size * nmemb, &nwritten);
+	if (rc != EOK) {
+		stream->error = true;
+		nwritten = 0;
+	}
+
+	return nwritten / size;
+}
+
+/** Flush KIO stream. */
+static int stdio_kio_flush(FILE *stream)
+{
+	kio_update();
+	return 0;
+}
+
+/** Read from VFS stream. */
+static size_t stdio_vfs_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	errno_t rc;
+	size_t nread;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
+	if (rc != EOK) {
+		errno = rc;
+		stream->error = true;
+	} else if (nread == 0) {
+		stream->eof = true;
+	}
+
+	return (nread / size);
+}
+
+/** Write to VFS stream. */
+static size_t stdio_vfs_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	errno_t rc;
+	size_t nwritten;
+
+	rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb, &nwritten);
+	if (rc != EOK) {
+		errno = rc;
+		stream->error = true;
+	}
+
+	return nwritten / size;
+}
+
+/** Flush VFS stream. */
+static int stdio_vfs_flush(FILE *stream)
+{
+	errno_t rc;
+
+	rc = vfs_sync(stream->fd);
+	if (rc != EOK) {
+		errno = rc;
+		return EOF;
+	}
+
+	return 0;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/private/sstream.h
===================================================================
--- uspace/lib/c/generic/private/sstream.h	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
+++ uspace/lib/c/generic/private/sstream.h	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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_PRIVATE_SSTREAM_H_
+#define LIBC_PRIVATE_SSTREAM_H_
+
+#include <stdio.h>
+
+extern void __sstream_init(const char *, FILE *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/generic/private/stdio.h
===================================================================
--- uspace/lib/c/generic/private/stdio.h	(revision ed18e148046bfdc424427e6fcc350b1718b2628b)
+++ uspace/lib/c/generic/private/stdio.h	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -39,7 +39,19 @@
 #include <stdio.h>
 #include <async.h>
+#include <stddef.h>
 
 /** Maximum characters that can be pushed back by ungetc() */
 #define UNGETC_MAX 1
+
+/** Stream operations */
+typedef struct {
+	/** Read from stream */
+	size_t (*read)(void *buf, size_t size, size_t nmemb, FILE *stream);
+	/** Write to stream */
+	size_t (*write)(const void *buf, size_t size, size_t nmemb,
+	    FILE *stream);
+	/** Flush stream */
+	int (*flush)(FILE *stream);
+} __stream_ops_t;
 
 struct _IO_FILE {
@@ -47,6 +59,12 @@
 	link_t link;
 
+	/** Stream operations */
+	__stream_ops_t *ops;
+
 	/** Underlying file descriptor. */
 	int fd;
+
+	/** Instance argument */
+	void *arg;
 
 	/** File position. */
@@ -58,7 +76,4 @@
 	/** End-of-file indicator. */
 	int eof;
-
-	/** KIO indicator */
-	int kio;
 
 	/** Session to the file provider */
Index: uspace/lib/c/generic/stdio/scanf.c
===================================================================
--- uspace/lib/c/generic/stdio/scanf.c	(revision ed18e148046bfdc424427e6fcc350b1718b2628b)
+++ uspace/lib/c/generic/stdio/scanf.c	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -46,4 +46,6 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include "../private/stdio.h"
+#include "../private/sstream.h"
 
 typedef enum {
@@ -1338,29 +1340,13 @@
 {
 	va_list args;
-	FILE *f;
+	FILE f;
 	int rc;
 
-	f = fopen("/tmp/test.tmp", "wt");
-	if (f == NULL)
-		return EOF;
-
-	if (fputs(s, f) == EOF)
-		return EOF;
-
-	if (fclose(f) == EOF)
-		return EOF;
-
-	f = fopen("/tmp/test.tmp", "rt");
-	if (f == NULL) {
-		printf("failed to open for reading\n");
-		return EOF;
-	}
+	__sstream_init(s, &f);
 
 	va_start(args, fmt);
-	rc = xxvfscanf(f, fmt, args);
+	rc = xxvfscanf(&f, fmt, args);
 	va_end(args);
 
-	fclose(f);
-
 	return rc;
 }
Index: uspace/lib/c/generic/stdio/sstream.c
===================================================================
--- uspace/lib/c/generic/stdio/sstream.c	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
+++ uspace/lib/c/generic/stdio/sstream.c	(revision 01cc7b454be965748c3e3a48118e2cb58ac9a69e)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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 <stdio.h>
+#include <str.h>
+#include <errno.h>
+#include <adt/list.h>
+#include <wchar.h>
+#include "../private/stdio.h"
+#include "../private/sstream.h"
+
+static size_t stdio_str_read(void *, size_t, size_t, FILE *);
+static size_t stdio_str_write(const void *, size_t, size_t, FILE *);
+static int stdio_str_flush(FILE *);
+
+static __stream_ops_t stdio_str_ops = {
+	.read = stdio_str_read,
+	.write = stdio_str_write,
+	.flush = stdio_str_flush
+};
+
+/** Read from string stream. */
+static size_t stdio_str_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	size_t nread;
+	char *cp = (char *)stream->arg;
+	char *bp = (char *)buf;
+
+	nread = 0;
+	while (nread < size * nmemb) {
+		if (*cp == '\0') {
+			stream->eof = true;
+			break;
+		}
+
+		bp[nread] = *cp;
+		++nread;
+		++cp;
+		stream->arg = (void *)cp;
+	}
+
+	return (nread / size);
+}
+
+/** Write to string stream. */
+static size_t stdio_str_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	return 0;
+}
+
+/** Flush string stream. */
+static int stdio_str_flush(FILE *stream)
+{
+	return EOF;
+}
+
+/** Initialize string stream.
+ *
+ * @param str String used as backend for reading
+ * @param stream Stream to initialize
+ */
+void __sstream_init(const char *str, FILE *stream)
+{
+	memset(stream, 0, sizeof(FILE));
+	stream->ops = &stdio_str_ops;
+	stream->arg = (void *)str;
+}
+
+/** @}
+ */
