Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 67ddb71c3646dd5015e24c60914c0c032f02672f)
+++ uspace/Makefile	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
@@ -68,4 +68,5 @@
 	app/trace \
 	app/top \
+	app/untar \
 	app/usbinfo \
 	app/vuhid \
Index: uspace/app/untar/Makefile
===================================================================
--- uspace/app/untar/Makefile	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
+++ uspace/app/untar/Makefile	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = untar
+
+SOURCES = \
+	main.c \
+	tar.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/untar/main.c
===================================================================
--- uspace/app/untar/main.c	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
+++ uspace/app/untar/main.c	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
@@ -0,0 +1,177 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <str_error.h>
+#include "tar.h"
+
+static size_t get_block_count(size_t bytes) {
+	return (bytes + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE;
+}
+
+static int 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--;
+	}
+	return EOK;
+}
+
+static int handle_normal_file(const tar_header_t *header, FILE *tarfile)
+{
+	// 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;
+	}
+
+	int 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;
+	}
+
+	fclose(file);
+
+	return rc;
+}
+
+static int handle_directory(const tar_header_t *header, FILE *tarfile)
+{
+	int rc = mkdir(header->filename, 0755);
+	if (rc == EEXISTS) {
+		printf("Note: directory %s already exists.\n", header->filename);
+		rc = EOK;
+	}
+	if (rc != EOK) {
+		fprintf(stderr, "Failed to create directory %s: %s.\n",
+		    header->filename, str_error(rc));
+		return rc;
+	}
+
+	return skip_blocks(tarfile, header->size);
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s tar-file\n", argv[0]);
+		return 1;
+	}
+
+	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;
+		}
+		int 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;
+}
+
+/** @}
+ */
Index: uspace/app/untar/tar.c
===================================================================
--- uspace/app/untar/tar.c	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
+++ uspace/app/untar/tar.c	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
@@ -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 untar
+ * @{
+ */
+/** @file
+ */
+
+#include <str.h>
+#include <stdlib.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 "?";
+	}
+}
+
+int tar_header_parse(tar_header_t *parsed, const tar_header_raw_t *raw)
+{
+	int 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: uspace/app/untar/tar.h
===================================================================
--- uspace/app/untar/tar.h	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
+++ uspace/app/untar/tar.h	(revision ff042c0d0d42ab2ec016699d5ce1da917d557538)
@@ -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 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 int 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
+
+/** @}
+ */
