Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 04051b8e5d2e6bd22c9e8aa7234a2566ab0c944b)
+++ uspace/Makefile	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -214,5 +214,6 @@
 	lib/c \
 	lib/softint \
-	lib/softfloat
+	lib/softfloat \
+	lib/untar
 
 LIBS = \
Index: uspace/app/untar/Makefile
===================================================================
--- uspace/app/untar/Makefile	(revision 04051b8e5d2e6bd22c9e8aa7234a2566ab0c944b)
+++ uspace/app/untar/Makefile	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -30,7 +30,8 @@
 BINARY = untar
 
+LIBS = untar
+
 SOURCES = \
-	main.c \
-	tar.c
+	main.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/untar/main.c
===================================================================
--- uspace/app/untar/main.c	(revision 04051b8e5d2e6bd22c9e8aa7234a2566ab0c944b)
+++ uspace/app/untar/main.c	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -34,87 +34,43 @@
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <str_error.h>
-#include <vfs/vfs.h>
-#include "tar.h"
+#include <stdarg.h>
+#include <untar.h>
 
-static size_t get_block_count(size_t bytes)
+const char *filename;
+
+static int tar_open(tar_file_t *tar)
 {
-	return (bytes + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE;
-}
+	FILE *file = fopen(filename, "rb");
+	if (file == NULL)
+		return errno;
 
-static errno_t skip_blocks(FILE *tarfile, size_t valid_data_size)
-{
-	size_t blocks_to_read = get_block_count(valid_data_size);
-	while (blocks_to_read > 0) {
-		uint8_t block[TAR_BLOCK_SIZE];
-		size_t actually_read = fread(block, TAR_BLOCK_SIZE, 1, tarfile);
-		if (actually_read != 1) {
-			return errno;
-		}
-		blocks_to_read--;
-	}
+	tar->data = (void *) file;
 	return EOK;
 }
 
-static errno_t handle_normal_file(const tar_header_t *header, FILE *tarfile)
+static void tar_close(tar_file_t *tar)
 {
-	// FIXME: create the directory first
-
-	FILE *file = fopen(header->filename, "wb");
-	if (file == NULL) {
-		fprintf(stderr, "Failed to create %s: %s.\n", header->filename,
-		    str_error(errno));
-		return errno;
-	}
-
-	errno_t rc = EOK;
-	size_t bytes_remaining = header->size;
-	size_t blocks = get_block_count(bytes_remaining);
-	while (blocks > 0) {
-		uint8_t block[TAR_BLOCK_SIZE];
-		size_t actually_read = fread(block, 1, TAR_BLOCK_SIZE, tarfile);
-		if (actually_read != TAR_BLOCK_SIZE) {
-			rc = errno;
-			fprintf(stderr, "Failed to read block for %s: %s.\n",
-			    header->filename, str_error(rc));
-			break;
-		}
-		size_t to_write = TAR_BLOCK_SIZE;
-		if (bytes_remaining < TAR_BLOCK_SIZE) {
-			to_write = bytes_remaining;
-		}
-		size_t actually_written = fwrite(block, 1, to_write, file);
-		if (actually_written != to_write) {
-			rc = errno;
-			fprintf(stderr, "Failed to write to %s: %s.\n",
-			    header->filename, str_error(rc));
-			break;
-		}
-		blocks--;
-		bytes_remaining -= TAR_BLOCK_SIZE;
-	}
-
+	FILE *file = (FILE *) tar->data;
 	fclose(file);
-
-	return rc;
 }
 
-static errno_t handle_directory(const tar_header_t *header, FILE *tarfile)
+static size_t tar_read(tar_file_t *tar, void *data, size_t size)
 {
-	errno_t rc;
+	FILE *file = (FILE *) tar->data;
+	return fread(data, 1, size, file);
+}
 
-	rc = vfs_link_path(header->filename, KIND_DIRECTORY, NULL);
-	if (rc != EOK) {
-		if (rc != EEXIST) {
-			fprintf(stderr, "Failed to create directory %s: %s.\n",
-			    header->filename, str_error(rc));
-			return rc;
-		}
-	}
+static void tar_vreport(tar_file_t *tar, const char *fmt, va_list args)
+{
+	vfprintf(stderr, fmt, args);
+}
 
-	return skip_blocks(tarfile, header->size);
-}
+tar_file_t tar = {
+	.open = tar_open,
+	.close = tar_close,
+
+	.read = tar_read,
+	.vreport = tar_vreport
+};
 
 int main(int argc, char *argv[])
@@ -125,52 +81,6 @@
 	}
 
-	const char *filename = argv[1];
-
-	FILE *tarfile = fopen(filename, "rb");
-	if (tarfile == NULL) {
-		fprintf(stderr, "Failed to open `%s': %s.\n", filename, str_error(errno));
-		return 2;
-	}
-
-	while (true) {
-		size_t header_ok;
-		tar_header_raw_t header_raw;
-		tar_header_t header;
-		header_ok = fread(&header_raw, sizeof(header_raw), 1, tarfile);
-		if (header_ok != 1) {
-			break;
-		}
-		errno_t rc = tar_header_parse(&header, &header_raw);
-		if (rc == EEMPTY) {
-			continue;
-		}
-		if (rc != EOK) {
-			fprintf(stderr, "Failed parsing TAR header: %s.\n", str_error(rc));
-			break;
-		}
-
-		//printf(" ==> %s (%zuB, type %s)\n", header.filename,
-		//    header.size, tar_type_str(header.type));
-
-		switch (header.type) {
-		case TAR_TYPE_DIRECTORY:
-			rc = handle_directory(&header, tarfile);
-			break;
-		case TAR_TYPE_NORMAL:
-			rc = handle_normal_file(&header, tarfile);
-			break;
-		default:
-			rc = skip_blocks(tarfile, header.size);
-			break;
-		}
-		if (rc != EOK) {
-			break;
-		}
-
-	}
-
-	fclose(tarfile);
-
-	return 0;
+	filename = argv[1];
+	return untar(&tar);
 }
 
Index: pace/app/untar/tar.c
===================================================================
--- uspace/app/untar/tar.c	(revision 04051b8e5d2e6bd22c9e8aa7234a2566ab0c944b)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/*
- * Copyright (c) 2013 Vojtech Horky
- * 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 untar
- * @{
- */
-/** @file
- */
-
-#include <str.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <errno.h>
-#include <assert.h>
-
-#include "tar.h"
-
-tar_type_t tar_type_parse(const char type)
-{
-	switch (type) {
-	case '0':
-	case 0:
-		return TAR_TYPE_NORMAL;
-	case '5':
-		return TAR_TYPE_DIRECTORY;
-	default:
-		return TAR_TYPE_UNKNOWN;
-	}
-}
-
-const char *tar_type_str(tar_type_t type)
-{
-	switch (type) {
-	case TAR_TYPE_UNKNOWN:
-		return "unknown";
-	case TAR_TYPE_NORMAL:
-		return "normal";
-	case TAR_TYPE_DIRECTORY:
-		return "directory";
-	default:
-		assert(false && "unexpected tar_type_t enum value");
-		return "?";
-	}
-}
-
-errno_t tar_header_parse(tar_header_t *parsed, const tar_header_raw_t *raw)
-{
-	errno_t rc;
-
-	if (str_length(raw->filename) == 0) {
-		return EEMPTY;
-	}
-
-	size_t size;
-	rc = str_size_t(raw->size, NULL, 8, true, &size);
-	if (rc != EOK) {
-		return rc;
-	}
-	parsed->size = size;
-
-	str_cpy(parsed->filename, 100, raw->filename);
-
-	parsed->type = tar_type_parse(raw->type);
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/app/untar/tar.h
===================================================================
--- uspace/app/untar/tar.h	(revision 04051b8e5d2e6bd22c9e8aa7234a2566ab0c944b)
+++ 	(revision )
@@ -1,79 +1,0 @@
-/*
- * Copyright (c) 2013 Vojtech Horky
- * 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 untar
- * @{
- */
-/** @file
- */
-#ifndef TAR_H_GUARD
-#define TAR_H_GUARD
-
-#define TAR_BLOCK_SIZE 512
-
-typedef struct tar_header_raw {
-	char filename[100];
-	char permissions[8];
-	char owner[8];
-	char group[8];
-	char size[12];
-	char modification_time[12];
-	char checksum[8];
-	char type;
-	char name[100];
-	char ustar_magic[6];
-	char ustar_version[2];
-	char ustar_owner_name[32];
-	char ustar_group_name[32];
-	char ustar_device_major[8];
-	char ustar_device_minor[8];
-	char ustar_prefix[155];
-	char ignored[12];
-} tar_header_raw_t;
-
-typedef enum tar_type {
-	TAR_TYPE_UNKNOWN,
-	TAR_TYPE_NORMAL,
-	TAR_TYPE_DIRECTORY
-} tar_type_t;
-
-typedef struct tar_header {
-	char filename[100];
-	size_t size;
-	tar_type_t type;
-} tar_header_t;
-
-
-extern errno_t tar_header_parse(tar_header_t *, const tar_header_raw_t *);
-extern tar_type_t tar_type_parse(const char);
-extern const char *tar_type_str(tar_type_t);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/untar/Makefile
===================================================================
--- uspace/lib/untar/Makefile	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
+++ uspace/lib/untar/Makefile	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2018 Martin Decky
+# 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.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libuntar
+
+SOURCES = \
+	tar.c \
+	untar.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/untar/private/tar.h
===================================================================
--- uspace/lib/untar/private/tar.h	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
+++ uspace/lib/untar/private/tar.h	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#ifndef TAR_H_
+#define TAR_H_
+
+#define TAR_BLOCK_SIZE 512
+
+typedef struct tar_header_raw {
+	char filename[100];
+	char permissions[8];
+	char owner[8];
+	char group[8];
+	char size[12];
+	char modification_time[12];
+	char checksum[8];
+	char type;
+	char name[100];
+	char ustar_magic[6];
+	char ustar_version[2];
+	char ustar_owner_name[32];
+	char ustar_group_name[32];
+	char ustar_device_major[8];
+	char ustar_device_minor[8];
+	char ustar_prefix[155];
+	char ignored[12];
+} tar_header_raw_t;
+
+typedef enum tar_type {
+	TAR_TYPE_UNKNOWN,
+	TAR_TYPE_NORMAL,
+	TAR_TYPE_DIRECTORY
+} tar_type_t;
+
+typedef struct tar_header {
+	char filename[100];
+	size_t size;
+	tar_type_t type;
+} tar_header_t;
+
+extern errno_t tar_header_parse(tar_header_t *, const tar_header_raw_t *);
+extern tar_type_t tar_type_parse(const char);
+extern const char *tar_type_str(tar_type_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/untar/tar.c
===================================================================
--- uspace/lib/untar/tar.c	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
+++ uspace/lib/untar/tar.c	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#include <str.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include "private/tar.h"
+
+tar_type_t tar_type_parse(const char type)
+{
+	switch (type) {
+	case '0':
+	case 0:
+		return TAR_TYPE_NORMAL;
+	case '5':
+		return TAR_TYPE_DIRECTORY;
+	default:
+		return TAR_TYPE_UNKNOWN;
+	}
+}
+
+const char *tar_type_str(tar_type_t type)
+{
+	switch (type) {
+	case TAR_TYPE_UNKNOWN:
+		return "unknown";
+	case TAR_TYPE_NORMAL:
+		return "normal";
+	case TAR_TYPE_DIRECTORY:
+		return "directory";
+	default:
+		assert(false && "unexpected tar_type_t enum value");
+		return "?";
+	}
+}
+
+errno_t tar_header_parse(tar_header_t *parsed, const tar_header_raw_t *raw)
+{
+	if (str_length(raw->filename) == 0) {
+		return EEMPTY;
+	}
+
+	size_t size;
+	errno_t rc = str_size_t(raw->size, NULL, 8, true, &size);
+	if (rc != EOK) {
+		return rc;
+	}
+	parsed->size = size;
+
+	str_cpy(parsed->filename, 100, raw->filename);
+
+	parsed->type = tar_type_parse(raw->type);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/untar/untar.c
===================================================================
--- uspace/lib/untar/untar.c	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
+++ uspace/lib/untar/untar.c	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <str_error.h>
+#include <vfs/vfs.h>
+#include "private/tar.h"
+#include "untar.h"
+
+static size_t get_block_count(size_t bytes)
+{
+	return (bytes + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE;
+}
+
+static int tar_open(tar_file_t *tar)
+{
+	return tar->open(tar);
+}
+
+static void tar_close(tar_file_t *tar)
+{
+	tar->close(tar);
+}
+
+static size_t tar_read(tar_file_t *tar, void *data, size_t size)
+{
+	return tar->read(tar, data, size);
+}
+
+static void tar_report(tar_file_t *tar, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+
+	tar->vreport(tar, fmt, args);
+
+	va_end(args);
+}
+
+static errno_t tar_skip_blocks(tar_file_t *tar, size_t valid_data_size)
+{
+	size_t blocks_to_read = get_block_count(valid_data_size);
+
+	while (blocks_to_read > 0) {
+		uint8_t block[TAR_BLOCK_SIZE];
+		size_t actually_read = tar_read(tar, block, TAR_BLOCK_SIZE);
+		if (actually_read != TAR_BLOCK_SIZE)
+			return errno;
+
+		blocks_to_read--;
+	}
+
+	return EOK;
+}
+
+static errno_t tar_handle_normal_file(tar_file_t *tar,
+    const tar_header_t *header)
+{
+	// FIXME: create the directory first
+
+	FILE *file = fopen(header->filename, "wb");
+	if (file == NULL) {
+		tar_report(tar, "Failed to create %s: %s.\n", header->filename,
+		    str_error(errno));
+		return errno;
+	}
+
+	errno_t rc = EOK;
+	size_t bytes_remaining = header->size;
+	size_t blocks = get_block_count(bytes_remaining);
+
+	while (blocks > 0) {
+		uint8_t block[TAR_BLOCK_SIZE];
+		size_t actually_read = tar_read(tar, block, TAR_BLOCK_SIZE);
+		if (actually_read != TAR_BLOCK_SIZE) {
+			rc = errno;
+			tar_report(tar, "Failed to read block for %s: %s.\n",
+			    header->filename, str_error(rc));
+			break;
+		}
+
+		size_t to_write = TAR_BLOCK_SIZE;
+		if (bytes_remaining < TAR_BLOCK_SIZE)
+			to_write = bytes_remaining;
+
+		size_t actually_written = fwrite(block, 1, to_write, file);
+		if (actually_written != to_write) {
+			rc = errno;
+			tar_report(tar, "Failed to write to %s: %s.\n",
+			    header->filename, str_error(rc));
+			break;
+		}
+
+		blocks--;
+		bytes_remaining -= TAR_BLOCK_SIZE;
+	}
+
+	fclose(file);
+	return rc;
+}
+
+static errno_t tar_handle_directory(tar_file_t *tar, const tar_header_t *header)
+{
+	errno_t rc = vfs_link_path(header->filename, KIND_DIRECTORY, NULL);
+	if (rc != EOK) {
+		if (rc != EEXIST) {
+			tar_report(tar, "Failed to create directory %s: %s.\n",
+			    header->filename, str_error(rc));
+			return rc;
+		}
+	}
+
+	return tar_skip_blocks(tar, header->size);
+}
+
+int untar(tar_file_t *tar)
+{
+	int rc = tar_open(tar);
+	if (rc != EOK) {
+		tar_report(tar, "Failed to open: %s.\n", str_error(rc));
+		return rc;
+	}
+	
+	while (true) {
+		tar_header_raw_t header_raw;
+		size_t header_ok = tar_read(tar, &header_raw, sizeof(header_raw));
+		if (header_ok != sizeof(header_raw))
+			break;
+
+		tar_header_t header;
+		errno_t rc = tar_header_parse(&header, &header_raw);
+		if (rc == EEMPTY)
+			continue;
+
+		if (rc != EOK) {
+			tar_report(tar, "Failed parsing TAR header: %s.\n", str_error(rc));
+			break;
+		}
+
+		switch (header.type) {
+		case TAR_TYPE_DIRECTORY:
+			rc = tar_handle_directory(tar, &header);
+			break;
+		case TAR_TYPE_NORMAL:
+			rc = tar_handle_normal_file(tar, &header);
+			break;
+		default:
+			rc = tar_skip_blocks(tar, header.size);
+			break;
+		}
+
+		if (rc != EOK)
+			break;
+	}
+
+	tar_close(tar);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/untar/untar.h
===================================================================
--- uspace/lib/untar/untar.h	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
+++ uspace/lib/untar/untar.h	(revision 24fda5ed4fb3077000933b68f72dfc153ba3db02)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 Martin Decky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#ifndef UNTAR_H_
+#define UNTAR_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+typedef struct tar_file {
+	void *data;
+
+	int (*open)(struct tar_file *);
+	void (*close)(struct tar_file *);
+
+	size_t (*read)(struct tar_file *, void *, size_t);
+	void (*vreport)(struct tar_file *, const char *, va_list);
+} tar_file_t;
+
+extern int untar(tar_file_t *);
+
+#endif
+
+/** @}
+ */
