Add libtar to TWRP instead of using busybox tar
Add proper mkdosfs tool
Add fuse to TWRP
Add experimental exfat-fuse to TWRP
Convert all system() functions to use new Exec_Cmd function
diff --git a/libtar/block.c b/libtar/block.c
new file mode 100644
index 0000000..89e5e3d
--- /dev/null
+++ b/libtar/block.c
@@ -0,0 +1,383 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  block.c - libtar code to handle tar archive header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
+
+
+/* read a header block */
+int
+th_read_internal(TAR *t)
+{
+	int i;
+	int num_zero_blocks = 0;
+
+#ifdef DEBUG
+	printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
+#endif
+
+	while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
+	{
+		/* two all-zero blocks mark EOF */
+		if (t->th_buf.name[0] == '\0')
+		{
+			num_zero_blocks++;
+			if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
+			    && num_zero_blocks >= 2)
+				return 0;	/* EOF */
+			else
+				continue;
+		}
+
+		/* verify magic and version */
+		if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
+		    && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown magic value in tar header");
+#endif
+			return -2;
+		}
+
+		if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
+		    && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown version value in tar header");
+#endif
+			return -2;
+		}
+
+		/* check chksum */
+		if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
+		    && !th_crc_ok(t))
+		{
+#ifdef DEBUG
+			puts("!!! tar header checksum error");
+#endif
+			return -2;
+		}
+
+		break;
+	}
+
+#ifdef DEBUG
+	printf("<== th_read_internal(): returning %d\n", i);
+#endif
+	return i;
+}
+
+
+/* wrapper function for th_read_internal() to handle GNU extensions */
+int
+th_read(TAR *t)
+{
+	int i, j;
+	size_t sz;
+	char *ptr;
+
+#ifdef DEBUG
+	printf("==> th_read(t=0x%lx)\n", t);
+#endif
+
+	if (t->th_buf.gnu_longname != NULL)
+		free(t->th_buf.gnu_longname);
+	if (t->th_buf.gnu_longlink != NULL)
+		free(t->th_buf.gnu_longlink);
+	memset(&(t->th_buf), 0, sizeof(struct tar_header));
+
+	i = th_read_internal(t);
+	if (i == 0)
+		return 1;
+	else if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+	/* check for GNU long link extention */
+	if (TH_ISLONGLINK(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long linkname detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longlink == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longlink; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long linkname "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
+		       t->th_buf.gnu_longlink);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	/* check for GNU long name extention */
+	if (TH_ISLONGNAME(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long filename detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longname == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longname; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long filename "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longname == \"%s\"\n",
+		       t->th_buf.gnu_longname);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+#if 0
+	/*
+	** work-around for old archive files with broken typeflag fields
+	** NOTE: I fixed this in the TH_IS*() macros instead
+	*/
+
+	/*
+	** (directories are signified with a trailing '/')
+	*/
+	if (t->th_buf.typeflag == AREGTYPE
+	    && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+		t->th_buf.typeflag = DIRTYPE;
+
+	/*
+	** fallback to using mode bits
+	*/
+	if (t->th_buf.typeflag == AREGTYPE)
+	{
+		mode = (mode_t)oct_to_int(t->th_buf.mode);
+
+		if (S_ISREG(mode))
+			t->th_buf.typeflag = REGTYPE;
+		else if (S_ISDIR(mode))
+			t->th_buf.typeflag = DIRTYPE;
+		else if (S_ISFIFO(mode))
+			t->th_buf.typeflag = FIFOTYPE;
+		else if (S_ISCHR(mode))
+			t->th_buf.typeflag = CHRTYPE;
+		else if (S_ISBLK(mode))
+			t->th_buf.typeflag = BLKTYPE;
+		else if (S_ISLNK(mode))
+			t->th_buf.typeflag = SYMTYPE;
+	}
+#endif
+
+	return 0;
+}
+
+
+/* write a header block */
+int
+th_write(TAR *t)
+{
+	int i, j;
+	char type2;
+	size_t sz, sz2;
+	char *ptr;
+	char buf[T_BLOCKSIZE];
+
+#ifdef DEBUG
+	printf("==> th_write(TAR=\"%s\")\n", t->pathname);
+	th_print(t);
+#endif
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longlink (\"%s\")\n",
+		       t->th_buf.gnu_longlink);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGLINK_TYPE;
+		sz = strlen(t->th_buf.gnu_longlink);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longlink; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longname (\"%s\")\n",
+		       t->th_buf.gnu_longname);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGNAME_TYPE;
+		sz = strlen(t->th_buf.gnu_longname);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longname; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+	th_finish(t);
+
+#ifdef DEBUG
+	/* print tar header */
+	th_print(t);
+#endif
+
+	i = tar_block_write(t, &(t->th_buf));
+	if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+#ifdef DEBUG
+	puts("th_write(): returning 0");
+#endif
+	return 0;
+}
+
+