Index: uspace/lib/libc/generic/io/io.c
===================================================================
--- uspace/lib/libc/generic/io/io.c	(revision c51a7cda3c60e9a8e308d132a04da135bf560a65)
+++ uspace/lib/libc/generic/io/io.c	(revision ef8bcc6df47519f80b0b59ebb86b4c416a742a7b)
@@ -36,4 +36,5 @@
 #include <unistd.h>
 #include <fcntl.h>
+#include <assert.h>
 #include <string.h>
 #include <errno.h>
@@ -45,4 +46,6 @@
 #include <adt/list.h>
 
+static void _fflushbuf(FILE *stream);
+
 static FILE stdin_null = {
 	.fd = -1,
@@ -50,5 +53,9 @@
 	.eof = true,
 	.klog = false,
-	.phone = -1
+	.phone = -1,
+	.btype = _IONBF,
+	.buf = NULL,
+	.buf_size = 0,
+	.buf_head = NULL
 };
 
@@ -58,5 +65,9 @@
 	.eof = false,
 	.klog = true,
-	.phone = -1
+	.phone = -1,
+	.btype = _IOLBF,
+	.buf = NULL,
+	.buf_size = BUFSIZ,
+	.buf_head = NULL
 };
 
@@ -66,5 +77,9 @@
 	.eof = false,
 	.klog = true,
-	.phone = -1
+	.phone = -1,
+	.btype = _IONBF,
+	.buf = NULL,
+	.buf_size = 0,
+	.buf_head = NULL
 };
 
@@ -157,4 +172,28 @@
 }
 
+/** Set stream buffer. */
+void setvbuf(FILE *stream, void *buf, int mode, size_t size)
+{
+	stream->btype = mode;
+	stream->buf = buf;
+	stream->buf_size = size;
+	stream->buf_head = stream->buf;
+}
+
+/** Allocate stream buffer. */
+static int _fallocbuf(FILE *stream)
+{
+	assert(stream->buf == NULL);
+
+	stream->buf = malloc(stream->buf_size);
+	if (stream->buf == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	stream->buf_head = stream->buf;
+	return 0;
+}
+
 /** Open a stream.
  *
@@ -187,4 +226,7 @@
 	stream->klog = false;
 	stream->phone = -1;
+
+	/* FIXME: Should select buffering type based on what was opened. */
+	setvbuf(stream, NULL, _IOFBF, BUFSIZ);
 	
 	list_append(&stream->link, &files);
@@ -207,4 +249,7 @@
 	stream->klog = false;
 	stream->phone = -1;
+
+	/* FIXME: Should select buffering type based on what was opened. */
+	setvbuf(stream, NULL, _IOLBF, BUFSIZ);
 	
 	list_append(&stream->link, &files);
@@ -237,4 +282,7 @@
 	stream->klog = false;
 	stream->phone = -1;
+
+	/* FIXME: Should select buffering type based on what was opened. */
+	setvbuf(stream, NULL, _IOLBF, BUFSIZ);
 	
 	list_append(&stream->link, &files);
@@ -284,5 +332,8 @@
 	size_t left = size * nmemb;
 	size_t done = 0;
-	
+
+	/* Make sure no data is pending write. */
+	_fflushbuf(stream);
+
 	while ((left > 0) && (!stream->error) && (!stream->eof)) {
 		ssize_t rd = read(stream->fd, buf + done, left);
@@ -301,13 +352,5 @@
 }
 
-/** Write to a stream.
- *
- * @param buf    Source buffer.
- * @param size   Size of each record.
- * @param nmemb  Number of records to write.
- * @param stream Pointer to the stream.
- *
- */
-size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
+static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
 {
 	size_t left = size * nmemb;
@@ -333,4 +376,89 @@
 }
 
+/** Drain stream buffer, do not sync stream. */
+static void _fflushbuf(FILE *stream)
+{
+	size_t bytes_used;
+
+	if (!stream->buf || stream->btype == _IONBF || stream->error)
+		return;
+
+	bytes_used = stream->buf_head - stream->buf;
+	if (bytes_used == 0)
+		return;
+
+	(void) _fwrite(stream->buf, 1, bytes_used, stream);
+	stream->buf_head = stream->buf;
+}
+
+/** Write to a stream.
+ *
+ * @param buf    Source buffer.
+ * @param size   Size of each record.
+ * @param nmemb  Number of records to write.
+ * @param stream Pointer to the stream.
+ *
+ */
+size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	uint8_t *data;
+	size_t bytes_left;
+	size_t now;
+	size_t buf_free;
+	size_t total_written;
+	size_t i;
+	uint8_t b;
+	bool need_flush;
+
+	/* If not buffered stream, write out directly. */
+	if (stream->btype == _IONBF)
+		return _fwrite(buf, size, nmemb, stream);
+
+	/* Perform lazy allocation of stream buffer. */
+	if (stream->buf == NULL) {
+		if (_fallocbuf(stream) != 0)
+			return 0; /* Errno set by _fallocbuf(). */
+	}
+
+	data = (uint8_t *) buf;
+	bytes_left = size * nmemb;
+	total_written = 0;
+	need_flush = false;
+
+	while (!stream->error && bytes_left > 0) {
+
+		buf_free = stream->buf_size - (stream->buf_head - stream->buf);
+		if (bytes_left > buf_free)
+			now = buf_free;
+		else
+			now = bytes_left;
+
+		for (i = 0; i < now; i++) {
+			b = data[i];
+			stream->buf_head[i] = b;
+
+			if (b == '\n' && stream->btype == _IOLBF)
+				need_flush = true;
+		}
+
+		buf += now;
+		stream->buf_head += now;
+		buf_free -= now;
+		bytes_left -= now;
+		total_written += now;
+
+		if (buf_free == 0) {
+			/* Only need to drain buffer. */
+			_fflushbuf(stream);
+			need_flush = false;
+		}
+	}
+
+	if (need_flush)
+		fflush(stream);
+
+	return (total_written / size);
+}
+
 int fputc(wchar_t c, FILE *stream)
 {
@@ -406,4 +534,6 @@
 int fflush(FILE *stream)
 {
+	_fflushbuf(stream);
+
 	if (stream->klog) {
 		klog_update();
