Adopted Storage support

 -Detects, decrypts, and mounts an adopted SD card if a
  secondary block device is defined (usually mmcblk1)
 -Handles unified storage
 -Displays the adopted storage in MTP along with internal
 -Factory Reset - wiped just like a data media device, we
  retain the keys folder and the storage.xml during a
  factory reset
 -Backup / Restore
 -Disable mass storage when adopted storage is present
 -Read storage nickname from storage.xml and apply it to
  display names in the GUI
 -Read storage.xml and determine what storage location is in
  use for /sdcard and remap accordingly

libgpt_twrp is source code mostly kanged from an efimanager
project. It is GPL v2 or higher, so we will opt for GPL v3.

Change-Id: Ieda0030bec5155ba8d2b9167dc0016cebbf39d55
diff --git a/Android.mk b/Android.mk
index 94084d8..47c30c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -284,7 +284,7 @@
 endif
 ifeq ($(TW_INCLUDE_CRYPTO), true)
     LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
-    LOCAL_SHARED_LIBRARIES += libcryptfslollipop
+    LOCAL_SHARED_LIBRARIES += libcryptfslollipop libgpt_twrp
     LOCAL_C_INCLUDES += external/boringssl/src/include
 endif
 ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
@@ -592,6 +592,7 @@
 ifeq ($(TW_INCLUDE_CRYPTO), true)
     include $(commands_recovery_local_path)/crypto/lollipop/Android.mk
     include $(commands_recovery_local_path)/crypto/scrypt/Android.mk
+    include $(commands_recovery_local_path)/gpt/Android.mk
 endif
 ifeq ($(BUILD_ID), GINGERBREAD)
     TW_NO_EXFAT := true
diff --git a/crypto/lollipop/cryptfs.c b/crypto/lollipop/cryptfs.c
index 1e65a22..fa440ed 100644
--- a/crypto/lollipop/cryptfs.c
+++ b/crypto/lollipop/cryptfs.c
@@ -1060,6 +1060,7 @@
     if (! ioctl(fd, DM_TABLE_LOAD, io)) {
       break;
     }
+    printf("%i\n", errno);
     usleep(500000);
   }
 
@@ -1145,7 +1146,7 @@
 
   ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
   if (ioctl(fd, DM_DEV_CREATE, io)) {
-    printf("Cannot create dm-crypt device\n");
+    printf("Cannot create dm-crypt device %i\n", errno);
     goto errout;
   }
 
@@ -2017,3 +2018,45 @@
 
     return crypt_ftr.crypt_type;
 }
+
+/*
+ * Called by vold when it's asked to mount an encrypted external
+ * storage volume. The incoming partition has no crypto header/footer,
+ * as any metadata is been stored in a separate, small partition.
+ *
+ * out_crypto_blkdev must be MAXPATHLEN.
+ */
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+        const unsigned char* key, int keysize, char* out_crypto_blkdev) {
+    int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
+    if (fd == -1) {
+        printf("Failed to open %s: %s", real_blkdev, strerror(errno));
+        return -1;
+    }
+
+    unsigned long nr_sec = 0;
+    nr_sec = get_blkdev_size(fd);
+    close(fd);
+
+    if (nr_sec == 0) {
+        printf("Failed to get size of %s: %s", real_blkdev, strerror(errno));
+        return -1;
+    }
+
+    struct crypt_mnt_ftr ext_crypt_ftr;
+    memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
+    ext_crypt_ftr.fs_size = nr_sec;
+    ext_crypt_ftr.keysize = keysize;
+    strcpy((char*) ext_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
+
+    return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev,
+            out_crypto_blkdev, label);
+}
+
+/*
+ * Called by vold when it's asked to unmount an encrypted external
+ * storage volume.
+ */
+int cryptfs_revert_ext_volume(const char* label) {
+    return delete_crypto_blk_dev((char*) label);
+}
diff --git a/crypto/lollipop/cryptfs.h b/crypto/lollipop/cryptfs.h
index 67ebbba..cd07e5a 100644
--- a/crypto/lollipop/cryptfs.h
+++ b/crypto/lollipop/cryptfs.h
@@ -221,6 +221,9 @@
   int cryptfs_verify_passwd(char *newpw);
   int cryptfs_get_password_type(void);
   int delete_crypto_blk_dev(char *name);
+  int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+          const unsigned char* key, int keysize, char* out_crypto_blkdev);
+  int cryptfs_revert_ext_volume(const char* label);
 #ifdef __cplusplus
 }
 #endif
diff --git a/data.cpp b/data.cpp
index cda6828..77e8f8d 100644
--- a/data.cpp
+++ b/data.cpp
@@ -744,7 +744,7 @@
 		mConstValues.insert(make_pair(TW_HAS_USB_STORAGE, "0"));
 	} else {
 		LOGINFO("Lun file '%s'\n", Lun_File_str.c_str());
-		mConstValues.insert(make_pair(TW_HAS_USB_STORAGE, "1"));
+		mValues.insert(make_pair(TW_HAS_USB_STORAGE, make_pair("1", 0)));
 	}
 #endif
 #ifdef TW_INCLUDE_INJECTTWRP
@@ -923,6 +923,8 @@
 	mValues.insert(make_pair("tw_language", make_pair(EXPAND(TW_DEFAULT_LANGUAGE), 1)));
 	LOGINFO("LANG: %s\n", EXPAND(TW_DEFAULT_LANGUAGE));
 
+	mValues.insert(make_pair("tw_has_adopted_storage", make_pair("0", 0)));
+
 	pthread_mutex_unlock(&m_valuesLock);
 }
 
diff --git a/gpt/Android.mk b/gpt/Android.mk
new file mode 100755
index 0000000..7369339
--- /dev/null
+++ b/gpt/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build libgpt_twrp library
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := false
+LOCAL_MODULE := libgpt_twrp
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES = \
+    gpt.c \
+    gptcrc32.c
+
+LOCAL_SHARED_LIBRARIES := libc
+include $(BUILD_SHARED_LIBRARY)
diff --git a/gpt/gpt.c b/gpt/gpt.c
new file mode 100644
index 0000000..068a244
--- /dev/null
+++ b/gpt/gpt.c
@@ -0,0 +1,675 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+// For TWRP purposes, we'll be opting for version 3 of the GPL
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <asm/byteorder.h>
+#include "gpt.h"
+#include "gptcrc32.h"
+
+#define BLKGETLASTSECT  _IO(0x12,108) /* get last sector of block device */
+#define BLKGETSIZE _IO(0x12,96)	/* return device size */
+#define BLKSSZGET  _IO(0x12,104)	/* get block device sector size */
+#define BLKGETSIZE64 _IOR(0x12,114,uint64_t)	/* return device size in bytes (u64 *arg) */
+
+struct blkdev_ioctl_param {
+        unsigned int block;
+        size_t content_length;
+        char * block_contents;
+};
+
+static inline int
+efi_guidcmp(efi_guid_t left, efi_guid_t right)
+{
+	return memcmp(&left, &right, sizeof (efi_guid_t));
+}
+
+static int
+get_sector_size(int filedes)
+{
+	int rc, sector_size = 512;
+
+	rc = ioctl(filedes, BLKSSZGET, &sector_size);
+	if (rc)
+		sector_size = 512;
+	return sector_size;
+}
+
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ * 
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+	return (gptcrc32(buf, len, ~0L) ^ ~0L);
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ *  1) MSDOS signature is in the last two bytes of the MBR
+ *  2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr)
+{
+	int i, found = 0, signature = 0;
+	if (!mbr)
+		return 0;
+	signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
+	for (i = 0; signature && i < 4; i++) {
+		if (mbr->partition[i].os_type ==
+                    EFI_PMBR_OSTYPE_EFI_GPT) {
+			found = 1;
+			break;
+		}
+	}
+	return (signature && found);
+}
+
+/**
+ * kernel_has_blkgetsize64()
+ *
+ * Returns: 0 on false, 1 on true
+ * True means kernel is 2.4.x, x>=18, or
+ *                   is 2.5.x, x>4, or
+ *                   is > 2.5
+ */
+static int
+kernel_has_blkgetsize64(void)
+{
+        int major=0, minor=0, patch=0, parsed;
+        int rc;
+        struct utsname u;
+
+        memset(&u, 0, sizeof(u));
+        rc = uname(&u);
+        if (rc) return 0;
+
+        parsed = sscanf(u.release, "%d.%d.%d", &major, &minor, &patch);
+        if (parsed < 3) return 0;
+        if (major > 2) return 1;
+        if (major == 2 && minor > 5) return 1;
+        if (major == 2 && minor == 5 && patch >= 4) return 1;
+        if (major == 2 && minor == 4 && patch >= 18) return 1;
+        return 0;
+}
+
+
+/************************************************************
+ * _get_num_sectors
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  Last LBA value on success 
+ *  0 on error
+ *
+ * Try getting BLKGETSIZE64 and BLKSSZGET first,
+ * then BLKGETSIZE if necessary.
+ *  Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64
+ *  which returns the number of 512-byte sectors, not the size of
+ *  the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3.
+ ************************************************************/
+static uint64_t
+_get_num_sectors(int filedes)
+{
+	unsigned long sectors=0;
+        uint64_t bytes=0;
+	int rc;
+        if (kernel_has_blkgetsize64()) {
+                rc = ioctl(filedes, BLKGETSIZE64, &bytes);
+                if (!rc)
+                        return bytes / get_sector_size(filedes);
+        }
+
+        rc = ioctl(filedes, BLKGETSIZE, &sectors);
+        if (rc)
+                return 0;
+        
+	return sectors;
+}
+
+/************************************************************
+ * last_lba(): return number of last logical block of device
+ * 
+ * @fd
+ * 
+ * Description: returns Last LBA value on success, 0 on error.
+ * Notes: The value st_blocks gives the size of the file
+ *        in 512-byte blocks, which is OK if
+ *        EFI_BLOCK_SIZE_SHIFT == 9.
+ ************************************************************/
+
+static uint64_t
+last_lba(int filedes)
+{
+	int rc;
+	uint64_t sectors = 0;
+	struct stat s;
+	memset(&s, 0, sizeof (s));
+	rc = fstat(filedes, &s);
+	if (rc == -1) {
+		fprintf(stderr, "last_lba() could not stat: %s\n",
+			strerror(errno));
+		return 0;
+	}
+
+	if (S_ISBLK(s.st_mode)) {
+		sectors = _get_num_sectors(filedes);
+	} else {
+		fprintf(stderr,
+			"last_lba(): I don't know how to handle files with mode %x\n",
+			s.st_mode);
+		sectors = 1;
+	}
+
+	return sectors - 1;
+}
+
+
+static ssize_t
+read_lastoddsector(int fd, uint64_t lba __unused, void *buffer, size_t count)
+{
+        int rc;
+        struct blkdev_ioctl_param ioctl_param;
+
+        if (!buffer) return 0; 
+
+        ioctl_param.block = 0; /* read the last sector */
+        ioctl_param.content_length = count;
+        ioctl_param.block_contents = buffer;
+
+        rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param);
+        if (rc == -1) perror("read failed");
+
+        return !rc;
+}
+
+static ssize_t
+read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
+{
+	int sector_size = get_sector_size(fd);
+	off_t offset = lba * sector_size;
+        ssize_t bytesread;
+        void *aligned;
+        void *unaligned;
+
+        if (bytes % sector_size)
+                return EINVAL;
+
+	unaligned = malloc(bytes+sector_size-1);
+	aligned = (void *)
+		(((unsigned long)unaligned + sector_size - 1) &
+		 ~(unsigned long)(sector_size-1));
+	memset(aligned, 0, bytes);
+
+
+	lseek(fd, offset, SEEK_SET);
+	bytesread = read(fd, aligned, bytes);
+        memcpy(buffer, aligned, bytesread);
+        free(unaligned);
+
+        /* Kludge.  This is necessary to read/write the last
+           block of an odd-sized disk, until Linux 2.5.x kernel fixes.
+           This is only used by gpt.c, and only to read
+           one sector, so we don't have to be fancy.
+        */
+        if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) {
+                bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+        }
+        return bytesread;
+}
+
+/**
+ * alloc_read_gpt_entries(): reads partition entries from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a buffer into which the GPT will be put  
+ * Description: Returns ptes on success,  NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ */
+static gpt_entry *
+alloc_read_gpt_entries(int fd, gpt_header * gpt)
+{
+	gpt_entry *pte;
+        size_t count = __le32_to_cpu(gpt->num_partition_entries) *
+                __le32_to_cpu(gpt->sizeof_partition_entry);
+
+        if (!count) return NULL;
+
+	pte = (gpt_entry *)malloc(count);
+	if (!pte)
+		return NULL;
+	memset(pte, 0, count);
+
+	if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte,
+                      count)) {
+		free(pte);
+		return NULL;
+	}
+	return pte;
+}
+
+/**
+ * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the Logical Block Address of the partition table
+ * 
+ * Description: returns GPT header on success, NULL on error.   Allocates
+ * and fills a GPT header starting at @ from @bdev.
+ * Note: remember to free gpt when finished with it.
+ */
+static gpt_header *
+alloc_read_gpt_header(int fd, uint64_t lba)
+{
+	gpt_header *gpt;
+	gpt = (gpt_header *)
+	    malloc(sizeof (gpt_header));
+	if (!gpt)
+		return NULL;
+	memset(gpt, 0, sizeof (*gpt));
+	if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+		free(gpt);
+		return NULL;
+	}
+
+	return gpt;
+}
+
+/**
+ * is_gpt_valid() - tests one GPT header and PTEs for validity
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the logical block address of the GPT header to test
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if valid,  0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ */
+static int
+is_gpt_valid(int fd, uint64_t lba,
+             gpt_header ** gpt, gpt_entry ** ptes)
+{
+	int rc = 0;		/* default to not valid */
+	uint32_t crc, origcrc;
+
+	if (!gpt || !ptes)
+                return 0;
+	if (!(*gpt = alloc_read_gpt_header(fd, lba)))
+		return 0;
+
+	/* Check the GUID Partition Table signature */
+	if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
+		/* 
+		   printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n",
+		   __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE);
+		 */
+		free(*gpt);
+		*gpt = NULL;
+		return rc;
+	}
+
+	/* Check the GUID Partition Table Header CRC */
+	origcrc = __le32_to_cpu((*gpt)->header_crc32);
+	(*gpt)->header_crc32 = 0;
+	crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size));
+	if (crc != origcrc) {
+		// printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc);
+		(*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+	(*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+
+	/* Check that the my_lba entry points to the LBA
+	 * that contains the GPT we read */
+	if (__le64_to_cpu((*gpt)->my_lba) != lba) {
+		// printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", __le64_to_cpu((*gpt)->my_lba), lba);
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+
+	if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) {
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+
+	/* Check the GUID Partition Entry Array CRC */
+	crc = efi_crc32(*ptes,
+                        __le32_to_cpu((*gpt)->num_partition_entries) *
+			__le32_to_cpu((*gpt)->sizeof_partition_entry));
+	if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
+		// printf("GUID Partitition Entry Array CRC check failed.\n");
+		free(*gpt);
+		*gpt = NULL;
+		free(*ptes);
+		*ptes = NULL;
+		return 0;
+	}
+
+	/* We're done, all's well */
+	return 1;
+}
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * @pgpt is the primary GPT header
+ * @agpt is the alternate GPT header
+ * @lastlba is the last LBA number
+ * Description: Returns nothing.  Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ * 
+ */
+static void
+compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba)
+{
+	int error_found = 0;
+	if (!pgpt || !agpt)
+		return;
+	if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) {
+		fprintf(stderr, 
+		       "GPT:Primary header LBA != Alt. header alternate_lba\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->my_lba),
+                       __le64_to_cpu(agpt->alternate_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) {
+		fprintf(stderr, 
+		       "GPT:Primary header alternate_lba != Alt. header my_lba\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->alternate_lba),
+                       __le64_to_cpu(agpt->my_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->first_usable_lba) !=
+            __le64_to_cpu(agpt->first_usable_lba)) {
+		fprintf(stderr,  "GPT:first_usable_lbas don't match.\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->first_usable_lba),
+                       __le64_to_cpu(agpt->first_usable_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->last_usable_lba) !=
+            __le64_to_cpu(agpt->last_usable_lba)) {
+		fprintf(stderr,  "GPT:last_usable_lbas don't match.\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->last_usable_lba),
+                       __le64_to_cpu(agpt->last_usable_lba));
+		error_found++;
+	}
+	if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+		fprintf(stderr,  "GPT:disk_guids don't match.\n");
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->num_partition_entries) !=
+            __le32_to_cpu(agpt->num_partition_entries)) {
+		fprintf(stderr,  "GPT:num_partition_entries don't match: "
+		       "0x%x != 0x%x\n",
+		       __le32_to_cpu(pgpt->num_partition_entries),
+		       __le32_to_cpu(agpt->num_partition_entries));
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->sizeof_partition_entry) !=
+            __le32_to_cpu(agpt->sizeof_partition_entry)) {
+		fprintf(stderr, 
+		       "GPT:sizeof_partition_entry values don't match: "
+		       "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->sizeof_partition_entry),
+		       __le32_to_cpu(agpt->sizeof_partition_entry));
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->partition_entry_array_crc32) !=
+            __le32_to_cpu(agpt->partition_entry_array_crc32)) {
+		fprintf(stderr, 
+		       "GPT:partition_entry_array_crc32 values don't match: "
+		       "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->partition_entry_array_crc32),
+		       __le32_to_cpu(agpt->partition_entry_array_crc32));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) {
+		fprintf(stderr, 
+		       "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->alternate_lba), lastlba);
+		error_found++;
+	}
+
+	if (__le64_to_cpu(agpt->my_lba) != lastlba) {
+		fprintf(stderr, 
+		       "GPT:Alternate GPT header not at the end of the disk.\n");
+		fprintf(stderr,  "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n",
+		       __le64_to_cpu(agpt->my_lba), lastlba);
+		error_found++;
+	}
+
+	if (error_found)
+		fprintf(stderr, 
+		       "GPT: Use GNU Parted to correct GPT errors.\n");
+	return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ */
+static int
+find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
+{
+	int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+	gpt_header *pgpt = NULL, *agpt = NULL;
+	gpt_entry *pptes = NULL, *aptes = NULL;
+	legacy_mbr *legacymbr = NULL;
+	uint64_t lastlba;
+	if (!gpt || !ptes)
+		return 0;
+
+	lastlba = last_lba(fd);
+	good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA,
+				 &pgpt, &pptes);
+        if (good_pgpt) {
+		good_agpt = is_gpt_valid(fd,
+                                         __le64_to_cpu(pgpt->alternate_lba),
+					 &agpt, &aptes);
+                if (!good_agpt) {
+                        good_agpt = is_gpt_valid(fd, lastlba,
+                                                 &agpt, &aptes);
+                }
+        }
+        else {
+                good_agpt = is_gpt_valid(fd, lastlba,
+                                         &agpt, &aptes);
+        }
+
+        /* The obviously unsuccessful case */
+        if (!good_pgpt && !good_agpt) {
+                goto fail;
+        }
+
+	/* This will be added to the EFI Spec. per Intel after v1.02. */
+        legacymbr = malloc(sizeof (*legacymbr));
+        if (legacymbr) {
+                memset(legacymbr, 0, sizeof (*legacymbr));
+                read_lba(fd, 0, (uint8_t *) legacymbr,
+                         sizeof (*legacymbr));
+                good_pmbr = is_pmbr_valid(legacymbr);
+                free(legacymbr);
+                legacymbr=NULL;
+        }
+
+        /* Failure due to bad PMBR */
+        if ((good_pgpt || good_agpt) && !good_pmbr) {
+                fprintf(stderr,
+                       "  Warning: Disk has a valid GPT signature "
+                       "but invalid PMBR.\n"
+                       "  Assuming this disk is *not* a GPT disk anymore.\n"
+                       "  Use gpt kernel option to override.  "
+                       "Use GNU Parted to correct disk.\n");
+                goto fail;
+        }
+
+        /* Would fail due to bad PMBR, but force GPT anyhow */
+        if ((good_pgpt || good_agpt) && !good_pmbr) {
+                fprintf(stderr, 
+                       "  Warning: Disk has a valid GPT signature but "
+                       "invalid PMBR.\n"
+                       "  Use GNU Parted to correct disk.\n"
+                       "  gpt option taken, disk treated as GPT.\n");
+        }
+
+        compare_gpts(pgpt, agpt, lastlba);
+
+        /* The good cases */
+        if (good_pgpt && (good_pmbr)) {
+                *gpt  = pgpt;
+                *ptes = pptes;
+                if (agpt)  { free(agpt);   agpt = NULL; }
+                if (aptes) { free(aptes); aptes = NULL; }
+                if (!good_agpt) {
+                        fprintf(stderr, 
+			       "Alternate GPT is invalid, "
+                               "using primary GPT.\n");
+                }
+                return 1;
+        }
+        else if (good_agpt && (good_pmbr)) {
+                *gpt  = agpt;
+                *ptes = aptes;
+                if (pgpt)  { free(pgpt);   pgpt = NULL; }
+                if (pptes) { free(pptes); pptes = NULL; }
+                fprintf(stderr, 
+                       "Primary GPT is invalid, using alternate GPT.\n");
+                return 1;
+        }
+
+ fail:
+        if (pgpt)  { free(pgpt);   pgpt=NULL; }
+        if (agpt)  { free(agpt);   agpt=NULL; }
+        if (pptes) { free(pptes); pptes=NULL; }
+        if (aptes) { free(aptes); aptes=NULL; }
+        *gpt = NULL;
+        *ptes = NULL;
+        return 0;
+}
+
+void guid_to_ascii(const char *guid, char *s)
+{
+	uint32_t p1;
+	uint16_t p2;
+	uint16_t p3;
+	unsigned char p4[8];
+
+	memcpy(&p1, guid + 0, 4);
+	memcpy(&p2, guid + 4, 2);
+	memcpy(&p3, guid + 6, 2);
+	memcpy(p4, guid + 8, 8);
+
+	sprintf(s, "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
+		p1, p2, p3, p4[0], p4[1],
+		p4[2], p4[3], p4[4], p4[5], p4[6], p4[7]);
+}
+
+
+/************************************************************
+ * gpt_disk_get_partition_info()
+ * Requires:
+ *  - open file descriptor fd
+ *  - start, size, signature, mbr_type, signature_type
+ * Modifies: all these
+ * Returns:
+ *  0 on success
+ *  non-zero on failure
+ *
+ ************************************************************/
+int
+gpt_disk_get_partition_info(int fd, uint32_t num,
+			    char *type, char *part)
+{
+	gpt_header *gpt = NULL;
+	gpt_entry *ptes = NULL, *p;
+
+	if (!find_valid_gpt(fd, &gpt, &ptes))
+		return 1;
+
+	if (num > 0 && num <= __le32_to_cpu(gpt->num_partition_entries)) {
+		p = &ptes[num - 1];
+		guid_to_ascii((char*)&p->partition_type_guid, type);
+		guid_to_ascii((char*)&p->unique_partition_guid, part);
+	} else {
+		fprintf (stderr,"partition %d is not valid\n", num);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4 
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpt/gpt.h b/gpt/gpt.h
new file mode 100644
index 0000000..9520c8d
--- /dev/null
+++ b/gpt/gpt.h
@@ -0,0 +1,182 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+// For TWRP purposes, we'll be opting for version 3 of the GPL
+
+#ifndef _GPT_H
+#define _GPT_H
+
+
+#include <inttypes.h>
+//#include "efi.h"
+
+typedef struct {
+	uint8_t  b[16];
+} efi_guid_t;
+typedef uint16_t efi_char16_t;		/* UNICODE character */
+
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+#define MSDOS_MBR_SIGNATURE 0xaa55
+#define GPT_BLOCK_SIZE 512
+
+static const char* TWGptAndroidExpand = "193d1ea4b3ca11e4b07510604b889dcf";
+
+#define GPT_HEADER_SIGNATURE ((uint64_t)(0x5452415020494645LL))
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+#define PARTITION_SYSTEM_GUID \
+    EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \
+              0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) 
+#define LEGACY_MBR_PARTITION_GUID \
+    EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \
+              0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F)
+#define PARTITION_MSFT_RESERVED_GUID \
+    EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \
+              0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE)
+#define PARTITION_BASIC_DATA_GUID \
+    EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \
+              0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
+#define PARTITION_LINUX_RAID_GUID \
+    EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \
+              0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
+#define PARTITION_LINUX_SWAP_GUID \
+    EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \
+              0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f)
+#define PARTITION_LINUX_LVM_GUID \
+    EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+              0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
+
+typedef struct _gpt_header {
+	uint64_t signature;
+	uint32_t revision;
+	uint32_t header_size;
+	uint32_t header_crc32;
+	uint32_t reserved1;
+	uint64_t my_lba;
+	uint64_t alternate_lba;
+	uint64_t first_usable_lba;
+	uint64_t last_usable_lba;
+	efi_guid_t disk_guid;
+	uint64_t partition_entry_lba;
+	uint32_t num_partition_entries;
+	uint32_t sizeof_partition_entry;
+	uint32_t partition_entry_array_crc32;
+	uint8_t reserved2[GPT_BLOCK_SIZE - 92];
+} __attribute__ ((packed)) gpt_header;
+
+typedef struct _gpt_entry_attributes {
+	uint64_t required_to_function:1;
+	uint64_t reserved:47;
+        uint64_t type_guid_specific:16;
+} __attribute__ ((packed)) gpt_entry_attributes;
+
+typedef struct _gpt_entry {
+	efi_guid_t partition_type_guid;
+	efi_guid_t unique_partition_guid;
+	uint64_t starting_lba;
+	uint64_t ending_lba;
+	gpt_entry_attributes attributes;
+	efi_char16_t partition_name[72 / sizeof(efi_char16_t)];
+} __attribute__ ((packed)) gpt_entry;
+
+
+/* 
+   These values are only defaults.  The actual on-disk structures
+   may define different sizes, so use those unless creating a new GPT disk!
+*/
+
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384
+/* 
+   Number of actual partition entries should be calculated
+   as: 
+*/
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \
+        (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \
+         sizeof(gpt_entry))
+
+
+typedef struct _partition_record {
+	uint8_t boot_indicator;	/* Not used by EFI firmware. Set to 0x80 to indicate that this
+				   is the bootable legacy partition. */
+	uint8_t start_head;		/* Start of partition in CHS address, not used by EFI firmware. */
+	uint8_t start_sector;	/* Start of partition in CHS address, not used by EFI firmware. */
+	uint8_t start_track;	/* Start of partition in CHS address, not used by EFI firmware. */
+	uint8_t os_type;		/* OS type. A value of 0xEF defines an EFI system partition.
+				   Other values are reserved for legacy operating systems, and
+				   allocated independently of the EFI specification. */
+	uint8_t end_head;		/* End of partition in CHS address, not used by EFI firmware. */
+	uint8_t end_sector;		/* End of partition in CHS address, not used by EFI firmware. */
+	uint8_t end_track;		/* End of partition in CHS address, not used by EFI firmware. */
+	uint32_t starting_lba;	/* Starting LBA address of the partition on the disk. Used by
+				   EFI firmware to define the start of the partition. */
+	uint32_t size_in_lba;	/* Size of partition in LBA. Used by EFI firmware to determine
+				   the size of the partition. */
+} __attribute__ ((packed)) partition_record;
+
+
+/* Protected Master Boot Record  & Legacy MBR share same structure */
+/* Needs to be packed because the u16s force misalignment. */
+
+typedef struct _legacy_mbr {
+	uint8_t bootcode[440];
+	uint32_t unique_mbr_signature;
+	uint16_t unknown;
+	partition_record partition[4];
+	uint16_t signature;
+} __attribute__ ((packed)) legacy_mbr;
+
+
+
+
+#define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+/* Functions */
+int gpt_disk_get_partition_info (int fd, uint32_t num,
+                                 char *type, char *part);
+
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4 
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpt/gptcrc32.c b/gpt/gptcrc32.c
new file mode 100644
index 0000000..3157e93
--- /dev/null
+++ b/gpt/gptcrc32.c
@@ -0,0 +1,124 @@
+/* 
+ * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Copied crc32.c from the linux/drivers/net/cipe directory.
+ * - Now pass seed as an arg
+ * - changed len to be an unsigned long
+ * - changed crc32val to be a register
+ * - License remains unchanged!  It's still GPL-compatable!
+ */
+
+  /* ============================================================= */
+  /*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or       */
+  /*  code or tables extracted from it, as desired without restriction.     */
+  /*                                                                        */
+  /*  First, the polynomial itself and its table of feedback terms.  The    */
+  /*  polynomial is                                                         */
+  /*  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0   */
+  /*                                                                        */
+  /*  Note that we take it "backwards" and put the highest-order term in    */
+  /*  the lowest-order bit.  The X^32 term is "implied"; the LSB is the     */
+  /*  X^31 term, etc.  The X^0 term (usually shown as "+1") results in      */
+  /*  the MSB being 1.                                                      */
+  /*                                                                        */
+  /*  Note that the usual hardware shift register implementation, which     */
+  /*  is what we're using (we're merely optimizing it by doing eight-bit    */
+  /*  chunks at a time) shifts bits into the lowest-order term.  In our     */
+  /*  implementation, that means shifting towards the right.  Why do we     */
+  /*  do it this way?  Because the calculated CRC must be transmitted in    */
+  /*  order from highest-order term to lowest-order term.  UARTs transmit   */
+  /*  characters in order from LSB to MSB.  By storing the CRC this way,    */
+  /*  we hand it to the UART in the order low-byte to high-byte; the UART   */
+  /*  sends each low-bit to hight-bit; and the result is transmission bit   */
+  /*  by bit from highest- to lowest-order term without requiring any bit   */
+  /*  shuffling on our part.  Reception works similarly.                    */
+  /*                                                                        */
+  /*  The feedback terms table consists of 256, 32-bit entries.  Notes:     */
+  /*                                                                        */
+  /*      The table can be generated at runtime if desired; code to do so   */
+  /*      is shown later.  It might not be obvious, but the feedback        */
+  /*      terms simply represent the results of eight shift/xor opera-      */
+  /*      tions for all combinations of data and CRC register values.       */
+  /*                                                                        */
+  /*      The values must be right-shifted by eight bits by the "updcrc"    */
+  /*      logic; the shift must be unsigned (bring in zeroes).  On some     */
+  /*      hardware you could probably optimize the shift in assembler by    */
+  /*      using byte-swap instructions.                                     */
+  /*      polynomial $edb88320                                              */
+  /*                                                                        */
+  /*  --------------------------------------------------------------------  */
+
+#include <stdint.h>
+
+static uint32_t crc32_tab[] = {
+      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+      0x2d02ef8dL
+   };
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+uint32_t
+gptcrc32(const void *buf, unsigned long len, uint32_t seed)
+{
+  unsigned long i;
+  register uint32_t crc32val;
+  const unsigned char *s = buf;
+
+  crc32val = seed;
+  for (i = 0;  i < len;  i ++)
+    {
+      crc32val =
+	crc32_tab[(crc32val ^ s[i]) & 0xff] ^
+	  (crc32val >> 8);
+    }
+  return crc32val;
+}
diff --git a/gpt/gptcrc32.h b/gpt/gptcrc32.h
new file mode 100644
index 0000000..0631b7f
--- /dev/null
+++ b/gpt/gptcrc32.h
@@ -0,0 +1,36 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 1998-2000 Free Software Foundation, Inc.
+
+    crc32.h
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+// For TWRP purposes, we'll be opting for version 3 of the GPL
+
+#ifndef _GPTCRC32_H
+#define _GPTCRC32_H
+
+#include <stdint.h>
+
+/*
+ * This computes a 32 bit CRC of the data in the buffer, and returns the CRC.
+ * The polynomial used is 0xedb88320.
+ */
+
+extern uint32_t gptcrc32 (const void *buf, unsigned long len, uint32_t seed);
+
+#endif /* _GPTCRC32_H */
diff --git a/gui/action.cpp b/gui/action.cpp
index 711f645..8895e40 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -1466,6 +1466,7 @@
 					LOGINFO("Got default contexts and file mode for storage files.\n");
 				}
 			}
+			PartitionManager.Decrypt_Adopted();
 		}
 	}
 
diff --git a/gui/theme/common/landscape.xml b/gui/theme/common/landscape.xml
index ff6babe..a6b5dca 100755
--- a/gui/theme/common/landscape.xml
+++ b/gui/theme/common/landscape.xml
@@ -1120,6 +1120,12 @@
 				<text>{@format_data_lcp2=removes encryption on internal storage.}</text>
 			</text>
 
+			<text style="text_m_fail">
+				<condition var1="tw_has_adopted_storage" var2="1"/>
+				<placement x="%center_x%" y="%row4_y%" placement="5"/>
+				<text>{@format_data_adopted=Including Adopted Storage}</text>
+			</text>
+
 			<text style="text_m">
 				<placement x="%center_x%" y="%row5_y%" placement="5"/>
 				<text>{@format_data_undo=This cannot be undone.}</text>
diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml
index 5d7a5ac..8b36ccf 100755
--- a/gui/theme/common/languages/en.xml
+++ b/gui/theme/common/languages/en.xml
@@ -143,6 +143,7 @@
 		<string name="format_data_ptr1">Format Data will wipe all of your apps,</string>
 		<string name="format_data_ptr2">backups, pictures, videos, media, and</string>
 		<string name="format_data_ptr3">removes encryption on internal storage.</string>
+		<string name="format_data_adopted">Including Adopted Storage</string>
 		<string name="format_data_lcp1">Format Data will wipe all of your apps, backups, pictures, videos, media, and</string>
 		<string name="format_data_lcp2">removes encryption on internal storage.</string>
 		<string name="format_data_wtc1">Format Data will wipe all of your apps,</string>
diff --git a/gui/theme/common/portrait.xml b/gui/theme/common/portrait.xml
index 03004ae..0904c72 100755
--- a/gui/theme/common/portrait.xml
+++ b/gui/theme/common/portrait.xml
@@ -1077,6 +1077,12 @@
 				<text>{@format_data_ptr3=removes encryption on internal storage.}</text>
 			</text>
 
+			<text style="text_m_fail">
+				<condition var1="tw_has_adopted_storage" var2="1"/>
+				<placement x="%center_x%" y="%row5_y%" placement="5"/>
+				<text>{@format_data_adopted=Including Adopted Storage}</text>
+			</text>
+
 			<text style="text_m">
 				<placement x="%center_x%" y="%row6_y%" placement="5"/>
 				<text>{@format_data_undo=This cannot be undone.}</text>
diff --git a/mtp/Android.mk b/mtp/Android.mk
index 57f0ae3..34514b8 100755
--- a/mtp/Android.mk
+++ b/mtp/Android.mk
@@ -3,7 +3,6 @@
 # Build libtwrpmtp library
 
 include $(CLEAR_VARS)
-
 LOCAL_MODULE := libtwrpmtp
 LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing
diff --git a/partition.cpp b/partition.cpp
index 71d6f22..54bf5e1 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -26,6 +26,7 @@
 #include <dirent.h>
 #include <iostream>
 #include <sstream>
+#include <sys/param.h>
 
 #ifdef TW_INCLUDE_CRYPTO
 	#include "cutils/properties.h"
@@ -53,6 +54,7 @@
 
 #ifdef TW_INCLUDE_CRYPTO
 	#include "crypto/lollipop/cryptfs.h"
+	#include "gpt/gpt.h"
 #else
 	#define CRYPT_FOOTER_OFFSET 0x4000
 #endif
@@ -159,6 +161,8 @@
 	MTP_Storage_ID = 0;
 	Can_Flash_Img = false;
 	Mount_Read_Only = false;
+	Is_Adopted_Storage = false;
+	Adopted_GUID = "";
 }
 
 TWPartition::~TWPartition(void) {
@@ -703,28 +707,40 @@
 
 void TWPartition::Setup_Data_Media() {
 	LOGINFO("Setting up '%s' as data/media emulated storage.\n", Mount_Point.c_str());
-	Storage_Name = "Internal Storage";
+	if (Storage_Name.empty() || Storage_Name == "Data")
+		Storage_Name = "Internal Storage";
 	Has_Data_Media = true;
 	Is_Storage = true;
-	Is_Settings_Storage = true;
-	Storage_Path = "/data/media";
+	Storage_Path = Mount_Point + "/media";
 	Symlink_Path = Storage_Path;
-	if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
-		Make_Dir("/emmc", false);
-		Symlink_Mount_Point = "/emmc";
+	if (Mount_Point == "/data") {
+		Is_Settings_Storage = true;
+		if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
+			Make_Dir("/emmc", false);
+			Symlink_Mount_Point = "/emmc";
+		} else {
+			Make_Dir("/sdcard", false);
+			Symlink_Mount_Point = "/sdcard";
+		}
+		if (Mount(false) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
+			Storage_Path = Mount_Point + "/media/0";
+			Symlink_Path = Storage_Path;
+			DataManager::SetValue(TW_INTERNAL_PATH, Mount_Point + "/media/0");
+			UnMount(true);
+		}
+		DataManager::SetValue("tw_has_internal", 1);
+		DataManager::SetValue("tw_has_data_media", 1);
+		du.add_absolute_dir(Mount_Point + "/misc/vold");
+		du.add_absolute_dir(Mount_Point + "/.layout_version");
+		du.add_absolute_dir(Mount_Point + "/system/storage.xml");
 	} else {
-		Make_Dir("/sdcard", false);
-		Symlink_Mount_Point = "/sdcard";
+		if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
+			Storage_Path = Mount_Point + "/media/0";
+			Symlink_Path = Storage_Path;
+			UnMount(true);
+		}
 	}
-	if (Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
-		Storage_Path = "/data/media/0";
-		Symlink_Path = Storage_Path;
-		DataManager::SetValue(TW_INTERNAL_PATH, "/data/media/0");
-		UnMount(true);
-	}
-	DataManager::SetValue("tw_has_internal", 1);
-	DataManager::SetValue("tw_has_data_media", 1);
-	du.add_absolute_dir("/data/media");
+	du.add_absolute_dir(Mount_Point + "/media");
 }
 
 void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) {
@@ -1204,7 +1220,7 @@
 			}
 		}
 
-		if (Mount_Point == "/data" && Has_Data_Media && recreate_media) {
+		if (Has_Data_Media && recreate_media) {
 			Recreate_Media_Folder();
 		}
 	}
@@ -1907,30 +1923,42 @@
 	// In an OEM Build we want to do a full format
 	return Wipe_Encryption();
 #else
-	string dir;
+	bool ret = false;
 
-	// This handles wiping data on devices with "sdcard" in /data/media
 	if (!Mount(true))
 		return false;
 
 	gui_msg("wiping_data=Wiping data without wiping /data/media ...");
+	ret = Wipe_Data_Without_Wiping_Media_Func(Mount_Point + "/");
+	if (ret)
+		gui_msg("done=Done.");
+	return ret;
+#endif // ifdef TW_OEM_BUILD
+}
+
+bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unused) {
+	string dir;
 
 	DIR* d;
-	d = opendir("/data");
+	d = opendir(parent.c_str());
 	if (d != NULL) {
 		struct dirent* de;
 		while ((de = readdir(d)) != NULL) {
 			if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)	 continue;
-			// The media folder is the "internal sdcard"
-			// The .layout_version file is responsible for determining whether 4.2 decides up upgrade
-			// the media folder for multi-user.
-			//TODO: convert this to use twrpDU.cpp
-			if (strcmp(de->d_name, "media") == 0 || strcmp(de->d_name, ".layout_version") == 0)   continue;
 
-			dir = "/data/";
+			dir = parent;
 			dir.append(de->d_name);
+			if (du.check_skip_dirs(dir)) {
+				LOGINFO("skipped '%s'\n", dir.c_str());
+				continue;
+			}
 			if (de->d_type == DT_DIR) {
-				TWFunc::removeDir(dir, false);
+				dir.append("/");
+				if (!Wipe_Data_Without_Wiping_Media_Func(dir)) {
+					closedir(d);
+					return false;
+				}
+				rmdir(dir.c_str());
 			} else if (de->d_type == DT_REG || de->d_type == DT_LNK || de->d_type == DT_FIFO || de->d_type == DT_SOCK) {
 				if (!unlink(dir.c_str()))
 					LOGINFO("Unable to unlink '%s'\n", dir.c_str());
@@ -1938,12 +1966,10 @@
 		}
 		closedir(d);
 
-		gui_msg("done=Done.");
 		return true;
 	}
 	gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Mount_Point)(strerror(errno)));
 	return false;
-#endif // ifdef TW_OEM_BUILD
 }
 
 bool TWPartition::Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid) {
@@ -2200,7 +2226,7 @@
 	if (Has_Data_Media) {
 		if (Mount(Display_Error)) {
 			unsigned long long data_media_used, actual_data;
-			Used = du.Get_Folder_Size("/data");
+			Used = du.Get_Folder_Size(Mount_Point);
 			Backup_Size = Used;
 			int bak = (int)(Used / 1048576LLU);
 			int fre = (int)(Free / 1048576LLU);
@@ -2245,13 +2271,14 @@
 
 void TWPartition::Recreate_Media_Folder(void) {
 	string Command;
+	string Media_Path = Mount_Point + "/media";
 
 	if (!Mount(true)) {
-		gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")("/data/media"));
-	} else if (!TWFunc::Path_Exists("/data/media")) {
+		gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")(Media_Path));
+	} else if (!TWFunc::Path_Exists(Media_Path)) {
 		PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
-		LOGINFO("Recreating /data/media folder.\n");
-		mkdir("/data/media", 0770);
+		LOGINFO("Recreating %s folder.\n", Media_Path.c_str());
+		mkdir(Media_Path.c_str(), 0770);
 		string Internal_path = DataManager::GetStrValue("tw_internal_path");
 		if (!Internal_path.empty()) {
 			LOGINFO("Recreating %s folder.\n", Internal_path.c_str());
@@ -2264,7 +2291,7 @@
 		// Afterwards, we will try to set the
 		// default metadata that we were hopefully able to get during
 		// early boot.
-		tw_set_default_metadata("/data/media");
+		tw_set_default_metadata(Media_Path.c_str());
 		if (!Internal_path.empty())
 			tw_set_default_metadata(Internal_path.c_str());
 #endif
@@ -2401,3 +2428,141 @@
 	Mount_Read_Only = original_read_only;
 	return ret;
 }
+
+int TWPartition::Decrypt_Adopted() {
+#ifdef TW_INCLUDE_CRYPTO
+	int ret = 1;
+	Is_Adopted_Storage = false;
+	string Adopted_Key_File = "";
+
+	if (!Removable)
+		return ret;
+
+	int fd = open(Alternate_Block_Device.c_str(), O_RDONLY);
+	if (fd < 0) {
+		LOGINFO("failed to open '%s'\n", Alternate_Block_Device.c_str());
+		return ret;
+	}
+	char type_guid[80];
+	char part_guid[80];
+
+	if (gpt_disk_get_partition_info(fd, 2, type_guid, part_guid) == 0) {
+		LOGINFO("type: '%s'\n", type_guid);
+		LOGINFO("part: '%s'\n", part_guid);
+		Adopted_GUID = part_guid;
+		LOGINFO("Adopted_GUID '%s'\n", Adopted_GUID.c_str());
+		if (strcmp(type_guid, TWGptAndroidExpand) == 0) {
+			LOGINFO("android_expand found\n");
+			Adopted_Key_File = "/data/misc/vold/expand_";
+			Adopted_Key_File += part_guid;
+			Adopted_Key_File += ".key";
+			if (TWFunc::Path_Exists(Adopted_Key_File)) {
+				Is_Adopted_Storage = true;
+				/* Until we find a use case for this, I think it is safe
+				 * to disable USB Mass Storage whenever adopted storage
+				 * is present.
+				 */
+				LOGINFO("Detected adopted storage, disabling USB mass storage mode\n");
+				DataManager::SetValue("tw_has_usb_storage", 0);
+			}
+		}
+	}
+
+	if (Is_Adopted_Storage) {
+		string Adopted_Block_Device = Alternate_Block_Device + "p2";
+		if (!TWFunc::Path_Exists(Adopted_Block_Device)) {
+			Adopted_Block_Device = Alternate_Block_Device + "2";
+			if (!TWFunc::Path_Exists(Adopted_Block_Device)) {
+				LOGINFO("Adopted block device does not exist\n");
+				goto exit;
+			}
+		}
+		LOGINFO("key file is '%s', block device '%s'\n", Adopted_Key_File.c_str(), Adopted_Block_Device.c_str());
+		char crypto_blkdev[MAXPATHLEN];
+		std::string thekey;
+		int fdkey = open(Adopted_Key_File.c_str(), O_RDONLY);
+		if (fdkey < 0) {
+			LOGINFO("failed to open key file\n");
+			goto exit;
+		}
+		char buf[512];
+		ssize_t n;
+		while ((n = read(fdkey, &buf[0], sizeof(buf))) > 0) {
+			thekey.append(buf, n);
+		}
+		close(fdkey);
+		unsigned char* key = (unsigned char*) thekey.data();
+		cryptfs_revert_ext_volume(part_guid);
+
+		ret = cryptfs_setup_ext_volume(part_guid, Adopted_Block_Device.c_str(), key, thekey.size(), crypto_blkdev);
+		if (ret == 0) {
+			LOGINFO("adopted storage new block device: '%s'\n", crypto_blkdev);
+			Decrypted_Block_Device = crypto_blkdev;
+			Is_Decrypted = true;
+			Is_Encrypted = true;
+			Find_Actual_Block_Device();
+			if (!Mount(false)) {
+				LOGERR("Failed to mount decrypted adopted storage device\n");
+				Is_Decrypted = false;
+				Is_Encrypted = false;
+				cryptfs_revert_ext_volume(part_guid);
+				ret = 1;
+			} else {
+				Setup_Data_Media();
+				Recreate_Media_Folder();
+				Wipe_Available_in_GUI = true;
+				Wipe_During_Factory_Reset = true;
+				Can_Be_Backed_Up = true;
+				Can_Encrypt_Backup = true;
+				Use_Userdata_Encryption = true;
+				Is_Storage = true;
+				Storage_Name = "Adopted Storage";
+				Is_SubPartition = true;
+				SubPartition_Of = "/data";
+				PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
+				DataManager::SetValue("tw_has_adopted_storage", 1);
+			}
+		} else {
+			LOGERR("Failed to setup adopted storage decryption\n");
+		}
+	}
+exit:
+	return ret;
+#else
+	LOGINFO("Decrypt_Adopted: no crypto support\n");
+	return 1;
+#endif
+}
+
+void TWPartition::Revert_Adopted() {
+#ifdef TW_INCLUDE_CRYPTO
+	if (!Adopted_GUID.empty()) {
+		PartitionManager.Remove_MTP_Storage(Mount_Point);
+		UnMount(false);
+		cryptfs_revert_ext_volume(Adopted_GUID.c_str());
+		Is_Adopted_Storage = false;
+		Is_Encrypted = false;
+		Is_Decrypted = false;
+		Decrypted_Block_Device = "";
+		Find_Actual_Block_Device();
+		Wipe_During_Factory_Reset = false;
+		Can_Be_Backed_Up = false;
+		Can_Encrypt_Backup = false;
+		Use_Userdata_Encryption = false;
+		Is_SubPartition = false;
+		SubPartition_Of = "";
+		Has_Data_Media = false;
+		Storage_Path = Mount_Point;
+		if (!Symlink_Mount_Point.empty()) {
+			TWPartition* Dat = PartitionManager.Find_Partition_By_Path("/data");
+			if (Dat) {
+				Dat->UnMount(false);
+				Dat->Symlink_Mount_Point = Symlink_Mount_Point;
+			}
+			Symlink_Mount_Point = "";
+		}
+	}
+#else
+	LOGINFO("Revert_Adopted: no crypto support\n");
+#endif
+}
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 00c4f8f..81d448d 100644
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -57,6 +57,8 @@
 
 #ifdef TW_INCLUDE_CRYPTO
 	#include "crypto/lollipop/cryptfs.h"
+	#include "gui/rapidxml.hpp"
+	#include "gui/pages.hpp"
 #endif
 
 extern bool datamedia;
@@ -167,6 +169,9 @@
 			DataManager::SetValue("TW_CRYPTO_TYPE", password_type);
 		}
 	}
+	if (Decrypt_Data && (!Decrypt_Data->Is_Encrypted || Decrypt_Data->Is_Decrypted) && Decrypt_Data->Mount(false)) {
+		Decrypt_Adopted();
+	}
 #endif
 	Update_System_Details();
 	UnMount_Main_Partitions();
@@ -276,6 +281,8 @@
 		printf("Mount_To_Decrypt ");
 	if (Part->Can_Flash_Img)
 		printf("Can_Flash_Img ");
+	if (Part->Is_Adopted_Storage)
+		printf("Is_Adopted_Storage ");
 	printf("\n");
 	if (!Part->SubPartition_Of.empty())
 		printf("   SubPartition_Of: %s\n", Part->SubPartition_Of.c_str());
@@ -1504,13 +1511,13 @@
 	return result;
 }
 
-TWPartition* TWPartitionManager::Find_Next_Storage(string Path, string Exclude) {
+TWPartition* TWPartitionManager::Find_Next_Storage(string Path, bool Exclude_Data_Media) {
 	std::vector<TWPartition*>::iterator iter = Partitions.begin();
 
 	if (!Path.empty()) {
 		string Search_Path = TWFunc::Get_Root_Path(Path);
 		for (; iter != Partitions.end(); iter++) {
-			if ((*iter)->Mount_Point == Search_Path) {
+			if (Exclude_Data_Media && (*iter)->Has_Data_Media) {
 				iter++;
 				break;
 			}
@@ -1518,7 +1525,9 @@
 	}
 
 	for (; iter != Partitions.end(); iter++) {
-		if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount_Point != Exclude) {
+		if (Exclude_Data_Media && (*iter)->Has_Data_Media) {
+			// do nothing, do not return this type of partition
+		} else if ((*iter)->Is_Storage && (*iter)->Is_Present) {
 			return (*iter);
 		}
 	}
@@ -1563,7 +1572,7 @@
 		LOGINFO("Device doesn't have multiple lun files, mount current storage\n");
 		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
 		if (TWFunc::Get_Root_Path(DataManager::GetCurrentStoragePath()) == "/data") {
-			TWPartition* Mount = Find_Next_Storage("", "/data");
+			TWPartition* Mount = Find_Next_Storage("", true);
 			if (Mount) {
 				if (!Open_Lun_File(Mount->Mount_Point, lun_file)) {
 					goto error_handle;
@@ -1580,13 +1589,13 @@
 		TWPartition* Mount1;
 		TWPartition* Mount2;
 		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
-		Mount1 = Find_Next_Storage("", "/data");
+		Mount1 = Find_Next_Storage("", true);
 		if (Mount1) {
 			if (!Open_Lun_File(Mount1->Mount_Point, lun_file)) {
 				goto error_handle;
 			}
 			sprintf(lun_file, CUSTOM_LUN_FILE, 1);
-			Mount2 = Find_Next_Storage(Mount1->Mount_Point, "/data");
+			Mount2 = Find_Next_Storage(Mount1->Mount_Point, true);
 			if (Mount2) {
 				Open_Lun_File(Mount2->Mount_Point, lun_file);
 			}
@@ -1668,6 +1677,9 @@
 	// Locate and validate device to partition
 	TWPartition* SDCard = Find_Partition_By_Path(DataManager::GetCurrentStoragePath());
 
+	if (SDCard->Is_Adopted_Storage)
+		SDCard->Revert_Adopted();
+
 	if (SDCard == NULL || !SDCard->Removable || SDCard->Has_Data_Media) {
 		gui_err("partition_sd_locate=Unable to locate device to partition.");
 		return false;
@@ -2263,22 +2275,35 @@
 void TWPartitionManager::Translate_Partition(const char* path, const char* resource_name, const char* default_value) {
 	TWPartition* part = PartitionManager.Find_Partition_By_Path(path);
 	if (part) {
-		part->Display_Name = gui_lookup(resource_name, default_value);
-		part->Backup_Display_Name = part->Display_Name;
+		if (part->Is_Adopted_Storage) {
+			part->Display_Name = part->Display_Name + " - " + gui_lookup("data", "Data");
+			part->Backup_Display_Name = part->Display_Name;
+			part->Storage_Name = part->Storage_Name + " - " + gui_lookup("adopted_storage", "Adopted Storage");
+		} else {
+			part->Display_Name = gui_lookup(resource_name, default_value);
+			part->Backup_Display_Name = part->Display_Name;
+		}
 	}
 }
 
 void TWPartitionManager::Translate_Partition(const char* path, const char* resource_name, const char* default_value, const char* storage_resource_name, const char* storage_default_value) {
 	TWPartition* part = PartitionManager.Find_Partition_By_Path(path);
 	if (part) {
-		part->Display_Name = gui_lookup(resource_name, default_value);
-		part->Backup_Display_Name = part->Display_Name;
-		if (part->Is_Storage)
-			part->Storage_Name = gui_lookup(storage_resource_name, storage_default_value);
+		if (part->Is_Adopted_Storage) {
+			part->Display_Name = part->Display_Name + " - " + gui_lookup("data", "Data");
+			part->Backup_Display_Name = part->Display_Name;
+			part->Storage_Name = part->Storage_Name + " - " + gui_lookup("adopted_storage", "Adopted Storage");
+		} else {
+			part->Display_Name = gui_lookup(resource_name, default_value);
+			part->Backup_Display_Name = part->Display_Name;
+			if (part->Is_Storage)
+				part->Storage_Name = gui_lookup(storage_resource_name, storage_default_value);
+		}
 	}
 }
 
 void TWPartitionManager::Translate_Partition_Display_Names() {
+	LOGINFO("Translating partition display names\n");
 	Translate_Partition("/system", "system", "System");
 	Translate_Partition("/system_image", "system_image", "System Image");
 	Translate_Partition("/vendor", "vendor", "Vendor");
@@ -2306,3 +2331,86 @@
 	// This updates the text on all of the storage selection buttons in the GUI
 	DataManager::SetBackupFolder();
 }
+
+void TWPartitionManager::Decrypt_Adopted() {
+#ifdef TW_INCLUDE_CRYPTO
+	if (!Mount_By_Path("/data", false)) {
+		LOGERR("Cannot decrypt adopted storage because /data will not mount\n");
+		return;
+	}
+	LOGINFO("Decrypt adopted storage starting\n");
+	char* xmlFile = PageManager::LoadFileToBuffer("/data/system/storage.xml", NULL);
+	xml_document<> *doc = NULL;
+	xml_node<>* volumes = NULL;
+	xml_node<>* volume = NULL;
+	string Primary_Storage_UUID = "";
+	if (xmlFile != NULL) {
+		LOGINFO("successfully loaded storage.xml\n");
+		doc = new xml_document<>();
+		doc->parse<0>(xmlFile);
+		volumes = doc->first_node("volumes");
+		if (volumes) {
+			xml_attribute<>* psuuid = volumes->first_attribute("primaryStorageUuid");
+			if (psuuid) {
+				Primary_Storage_UUID = psuuid->value();
+			}
+		}
+	}
+	std::vector<TWPartition*>::iterator adopt;
+	for (adopt = Partitions.begin(); adopt != Partitions.end(); adopt++) {
+		if ((*adopt)->Removable && (*adopt)->Is_Present) {
+			if ((*adopt)->Decrypt_Adopted() == 0) {
+				if (volumes) {
+					xml_node<>* volume = volumes->first_node("volume");
+					while (volume) {
+						xml_attribute<>* guid = volume->first_attribute("partGuid");
+						if (guid) {
+							string GUID = (*adopt)->Adopted_GUID.c_str();
+							GUID.insert(8, "-");
+							GUID.insert(13, "-");
+							GUID.insert(18, "-");
+							GUID.insert(23, "-");
+
+							if (strcasecmp(GUID.c_str(), guid->value()) == 0) {
+								xml_attribute<>* attr = volume->first_attribute("nickname");
+								if (attr) {
+									(*adopt)->Storage_Name = attr->value();
+									(*adopt)->Display_Name = (*adopt)->Storage_Name;
+									(*adopt)->Backup_Display_Name = (*adopt)->Storage_Name;
+									LOGINFO("storage name from storage.xml is '%s'\n", attr->value());
+								}
+								attr = volume->first_attribute("fsUuid");
+								if (attr && !Primary_Storage_UUID.empty() && strcmp(Primary_Storage_UUID.c_str(), attr->value()) == 0) {
+									TWPartition* Dat = Find_Partition_By_Path("/data");
+									if (Dat) {
+										LOGINFO("Internal storage is found on adopted storage '%s'\n", (*adopt)->Display_Name.c_str());
+										LOGINFO("Changing '%s' to point to '%s'\n", Dat->Symlink_Mount_Point.c_str(), (*adopt)->Storage_Path.c_str());
+										(*adopt)->Symlink_Mount_Point = Dat->Symlink_Mount_Point;
+										Dat->Symlink_Mount_Point = "";
+										// Toggle mounts to ensure that the symlink mount point (probably /sdcard) is mounted to the right location
+										Dat->UnMount(false);
+										Dat->Mount(false);
+										(*adopt)->UnMount(false);
+										(*adopt)->Mount(false);
+										Output_Partition((*adopt));
+									}
+								}
+								break;
+							}
+						}
+						volume = volume->next_sibling("volume");
+					}
+				}
+			}
+		}
+	}
+	if (xmlFile) {
+		doc->clear();
+		delete doc;
+		free(xmlFile);
+	}
+#else
+	LOGINFO("Decrypt_Adopted: no crypto support\n");
+	return;
+#endif
+}
diff --git a/partitions.hpp b/partitions.hpp
index e802f34..bc44384 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -74,6 +74,8 @@
 	bool Flash_Image(string Filename);                                        // Flashes an image to the partition
 	void Change_Mount_Read_Only(bool new_value);                              // Changes Mount_Read_Only to new_value
 	int Check_Lifetime_Writes();
+	int Decrypt_Adopted();
+	void Revert_Adopted();
 
 public:
 	string Current_File_System;                                               // Current file system
@@ -82,6 +84,7 @@
 	bool Is_Present;                                                          // Indicates if the partition is currently present as a block device
 	string Crypto_Key_Location;                                               // Location of the crypto key used for decrypting encrypted data partitions
 	unsigned int MTP_Storage_ID;
+	string Adopted_GUID;
 
 protected:
 	bool Has_Data_Media;                                                      // Indicates presence of /data/media, may affect wiping and backup methods
@@ -111,6 +114,7 @@
 	bool Wipe_F2FS();                                                         // Uses mkfs.f2fs to wipe
 	bool Wipe_NTFS();                                                         // Uses mkntfs to wipe
 	bool Wipe_Data_Without_Wiping_Media();                                    // Uses rm -rf to wipe but does not wipe /data/media
+	bool Wipe_Data_Without_Wiping_Media_Func(const string& parent);           // Uses rm -rf to wipe but does not wipe /data/media
 	bool Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid); // Backs up using tar for file systems
 	bool Backup_DD(string backup_folder);                                     // Backs up using dd for emmc memory types
 	bool Backup_Dump_Image(string backup_folder);                             // Backs up using dump_image for MTD memory types
@@ -174,6 +178,7 @@
 	bool Retain_Layout_Version;                                               // Retains the .layout_version file during a wipe (needed on devices like Sony Xperia T where /data and /data/media are separate partitions)
 	bool Can_Flash_Img;                                                       // Indicates if this partition can have images flashed to it via the GUI
 	bool Mount_Read_Only;                                                     // Only mount this partition as read-only
+	bool Is_Adopted_Storage;                                                  // Indicates that this partition is for adopted storage (android_expand)
 
 friend class TWPartitionManager;
 friend class DataManager;
@@ -238,6 +243,7 @@
 	void Translate_Partition(const char* path, const char* resource_name, const char* default_value);
 	void Translate_Partition(const char* path, const char* resource_name, const char* default_value, const char* storage_resource_name, const char* storage_default_value);
 	void Translate_Partition_Display_Names();                                 // Updates display names based on translations
+	void Decrypt_Adopted();
 
 	TWAtomicInt stop_backup;
 
@@ -250,7 +256,7 @@
 	void Output_Partition(TWPartition* Part);
 	TWPartition* Find_Partition_By_MTP_Storage_ID(unsigned int Storage_ID);   // Returns a pointer to a partition based on MTP Storage ID
 	bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type);         // Adds or removes an MTP Storage partition
-	TWPartition* Find_Next_Storage(string Path, string Exclude);
+	TWPartition* Find_Next_Storage(string Path, bool Exclude_Data_Media);
 	int Open_Lun_File(string Partition_Path, string Lun_File);
 	pid_t mtppid;
 	bool mtp_was_enabled;
diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk
index ea83286..825b990 100644
--- a/prebuilt/Android.mk
+++ b/prebuilt/Android.mk
@@ -155,6 +155,7 @@
     RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfslollipop.so
     RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
     RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libhardware.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgpt_twrp.so
     ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
         RELINK_SOURCE_FILES += $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/libcryptfs_hw.so
     endif