Merge tag 'android-12.0.0_r7' into android-11

Android 12.0.0 release 7

Change-Id: Idbc61c74c1f983c7e8c7c9269059997eefefdc4d
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..540e149
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,23 @@
+- [ ] I am running an official build of TWRP, downloaded from https://twrp.me/Devices/
+- [ ] I am running the latest version of TWRP
+- [ ] I have read the FAQ (https://twrp.me/FAQ/)
+- [ ] I have searched for my issue and it does not already exist
+
+**Device codename**: <!-- Device codename -->
+**TWRP version**: <!-- TWRP version installed -->
+
+#### WHAT STEPS WILL REPRODUCE THE PROBLEM?
+<!-- Explain the steps necessary to reproduce the problem, as completely as possible -->
+
+#### WHAT IS THE EXPECTED RESULT?
+<!-- Explain what the expected result is, as completely as possible -->
+
+#### WHAT HAPPENS INSTEAD?
+<!-- Explain what happens instead, as completely as possible -->
+
+#### ADDITIONAL INFORMATION
+<!-- Add any additional information you know about the issue, such as possible causes and solutions -->
+
+<!-- Use https://paste.omnirom.org/ and upload `/tmp/recovery.log` and the output of `dmesg` -->
+`/tmp/recovery.log`: <!-- Link here -->
+`dmesg`: <!-- Link here -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..d04c212
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+# WE DO NOT MERGE PULL REQUESTS SUBMITTED HERE
+
+You will need to submit it through [OmniRom Gerrit](https://gerrit.omnirom.org/#/admin/projects/android_bootable_recovery/)
+
+For changes to device trees, use [TWRP Gerrit](https://gerrit.twrp.me/)
+
+This guide explani how to use [Gerrit code review](https://forum.xda-developers.com/general/xda-university/guide-using-gerrit-code-review-t3720802)
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..480f547
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.*.swp
+*~
+tags
+.vscode
diff --git a/Android.bp b/Android.bp
old mode 100644
new mode 100755
index 52de770..87915f2
--- a/Android.bp
+++ b/Android.bp
@@ -1,16 +1,6 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+subdirs = [
+    "bootloader_message",
+]
 
 // *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS.  PLEASE
 //     CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
@@ -48,90 +38,95 @@
 
 cc_defaults {
     name: "recovery_defaults",
-
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
-
         // Must be the same as RECOVERY_API_VERSION.
         "-DRECOVERY_API_VERSION=3",
-
         "-Wall",
         "-Werror",
     ],
+    cpp_std: "c++17",
 }
 
-cc_library_static {
-    name: "librecovery_fastboot",
-    recovery_available: true,
-    defaults: [
-        "recovery_defaults",
+bootstrap_go_package {
+    name: "soong-libaosprecovery_defaults",
+    pkgPath: "bootable/recovery/libaosprecovery",
+    deps: [
+        "soong",
+        "soong-android",
+        "soong-cc"
     ],
-
     srcs: [
-        "fastboot/fastboot.cpp",
+        "libaosprecovery_defaults.go",
+        "soong/makevars.go"
     ],
-
-    shared_libs: [
-        "libbase",
-        "libbootloader_message",
-        "libcutils",
-        "liblog",
-        "librecovery_ui",
-    ],
-
-    static_libs: [
-        "librecovery_ui_default",
-    ],
+    pluginFor: ["soong_build"]
 }
 
-cc_defaults {
-    name: "librecovery_defaults",
+libaosprecovery_defaults {
+    name: "libaosprecovery_defaults"
+}
 
-    defaults: [
-        "recovery_defaults",
+cc_library_shared {
+    name: "libaosprecovery",
+    defaults: ["libaosprecovery_defaults"],
+    cflags: [
+        "-std=gnu++2a",
+        "-DRECOVERY_API_VERSION=3"
     ],
-
+    include_dirs: [
+        "bootable/recovery/install/include",
+        "bootable/recovery/recovery_ui/include",
+        "bootable/recovery/recovery_utils/include",
+        "bootable/recovery/otautil/include",
+        "bootable/recovery/minadbd",
+        "bootable/recovery/minadbd/include",
+        "bootable/recovery/minzip",
+        "bootable/recovery/twrpinstall/include",
+        "system/libvintf/include"
+    ],
+    srcs: [
+        "install/adb_install.cpp",
+        "install/asn1_decoder.cpp",
+        "install/get_args.cpp", 
+        "install/install.cpp",
+        "install/package.cpp",
+        "install/verifier.cpp",
+        "install/wipe_data.cpp",
+        "install/set_metadata.cpp",
+        "install/ZipUtil.cpp"
+    ],
     shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libbase",
         "libbootloader_message",
         "libcrypto",
-        "libcutils",
-        "libfs_mgr",
-        "liblp",
+        "libext4_utils",
+        "libfs_mgr", 
+        "libfusesideload", 
+        "libhidl-gen-utils",
+        "libhidlbase",
         "liblog",
-        "libprotobuf-cpp-lite",
+        "libselinux",
+        "libtinyxml2",
+        "libutils",
+        "libz",
         "libziparchive",
+        "libcutils",
+        "libc++"
     ],
-
     static_libs: [
-        "libc++fs",
-        "libinstall",
-        "librecovery_fastboot",
-        "libminui",
-        "librecovery_utils",
         "libotautil",
-        "libsnapshot_nobinder",
-        "update_metadata-protos",
+        "libvintf", 
+        "libhidl-gen-utils",
+        "librecovery_utils",
+        "libc++fs"
     ],
-}
-
-cc_library_static {
-    name: "librecovery",
-    recovery_available: true,
-
-    defaults: [
-        "librecovery_defaults",
-    ],
-
-    srcs: [
-        "recovery.cpp",
-    ],
-
-    shared_libs: [
-        "librecovery_ui",
-    ],
+    required: [
+        "init_recovery.rc",
+        "ueventd.rc.recovery",
+        "libdl_android.bootstrap",
+        "ziptool.recovery"
+    ]
 }
 
 prebuilt_etc {
@@ -141,98 +136,3 @@
     sub_dir: "init/hw",
     recovery: true,
 }
-
-cc_binary {
-    name: "recovery",
-    recovery: true,
-
-    defaults: [
-        "libinstall_defaults",
-        "librecovery_defaults",
-        "librecovery_utils_defaults",
-    ],
-
-    srcs: [
-        "recovery_main.cpp",
-    ],
-
-    shared_libs: [
-        "librecovery_ui",
-    ],
-
-    static_libs: [
-        "librecovery",
-        "librecovery_ui_default",
-    ],
-
-    required: [
-        "e2fsdroid.recovery",
-        "init_recovery.rc",
-        "librecovery_ui_ext",
-        "minadbd",
-        "mke2fs.conf.recovery",
-        "mke2fs.recovery",
-        "recovery_deps",
-        "ueventd.rc.recovery",
-    ],
-}
-
-// The dynamic executable that runs after /data mounts.
-cc_binary {
-    name: "recovery-persist",
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "recovery-persist.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
-        "librecovery_utils",
-    ],
-
-    init_rc: [
-        "recovery-persist.rc",
-    ],
-}
-
-// The dynamic executable that runs at init.
-cc_binary {
-    name: "recovery-refresh",
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "recovery-refresh.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
-        "librecovery_utils",
-    ],
-
-    init_rc: [
-        "recovery-refresh.rc",
-    ],
-}
-
-filegroup {
-    name: "res-testdata",
-
-    srcs: [
-        "res-*/images/*_text.png",
-    ],
-}
diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
index 96af417..302b143
--- a/Android.mk
+++ b/Android.mk
@@ -13,70 +13,687 @@
 # limitations under the License.
 
 LOCAL_PATH := $(call my-dir)
+commands_TWRP_local_path := $(LOCAL_PATH)
 
-# Needed by build/make/core/Makefile. Must be consistent with the value in Android.bp.
+ifneq ($(project-path-for),)
+    ifeq ($(LOCAL_PATH),$(call project-path-for,recovery))
+        PROJECT_PATH_AGREES := true
+        BOARD_SEPOLICY_DIRS += $(call project-path-for,recovery)/sepolicy
+    endif
+else
+    ifeq ($(LOCAL_PATH),bootable/recovery)
+        PROJECT_PATH_AGREES := true
+        BOARD_SEPOLICY_DIRS += bootable/recovery/sepolicy
+    else
+        ifeq ($(LOCAL_PATH),bootable/recovery-twrp)
+            ifeq ($(RECOVERY_VARIANT),twrp)
+                PROJECT_PATH_AGREES := true
+                BOARD_SEPOLICY_DIRS += bootable/recovery-twrp/sepolicy
+            endif
+        endif
+    endif
+endif
+
+ifeq ($(PROJECT_PATH_AGREES),true)
+
+ifeq ($(CM_PLATFORM_SDK_VERSION),)
+    CM_PLATFORM_SDK_VERSION := 0
+endif
+
+include $(CLEAR_VARS)
+
+TARGET_RECOVERY_GUI := true
+
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+
+ifneq ($(TW_DEVICE_VERSION),)
+    LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-$(TW_DEVICE_VERSION)"'
+else
+    LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-0"'
+endif
+LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+
+LOCAL_SRC_FILES := \
+    twrp.cpp \
+    fixContexts.cpp \
+    twrpTar.cpp \
+    exclude.cpp \
+    find_file.cpp \
+    infomanager.cpp \
+    data.cpp \
+    kernel_module_loader.cpp \
+    partition.cpp \
+    partitionmanager.cpp \
+    progresstracking.cpp \
+    startupArgs.cpp \
+    twrp-functions.cpp \
+    twrpDigestDriver.cpp \
+    openrecoveryscript.cpp \
+    tarWrite.c \
+    twrpAdbBuFifo.cpp \
+    twrpRepacker.cpp
+
+ifeq ($(TW_EXCLUDE_APEX),)
+    LOCAL_SRC_FILES += twrpApex.cpp
+else
+    LOCAL_CFLAGS += -DTW_EXCLUDE_APEX
+endif
+
+LOCAL_STATIC_LIBRARIES += libavb libtwrpinstall libminadbd_services libinit libsnapshot_nobinder
+LOCAL_SHARED_LIBRARIES += libfs_mgr libhardware android.hardware.boot@1.0 android.hardware.boot@1.1 libprotobuf-cpp-lite liblp libutils libhidlbase
+LOCAL_C_INCLUDES += \
+    system/core/fs_mgr/libfs_avb/include/ \
+    system/core/fs_mgr/include_fstab/ \
+    system/core/fs_mgr/include/ \
+    system/core/fs_mgr/libdm/include/ \
+    system/core/fs_mgr/liblp/include/ \
+    system/gsid/include/ \
+    system/core/init/ \
+    system/extras/ext4_utils/include \
+
+ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
+  LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
+endif
+
+LOCAL_MODULE := recovery
+
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+LOCAL_CLANG := true
 
-# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a
-# device-specific module that defines make_device() and the exact RecoveryUI class for the
-# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI.
-TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
+LOCAL_C_INCLUDES += \
+    bionic \
+    system/extras \
+    system/core/adb \
+    system/core/libmodprobe/include \
+    system/core/libsparse \
+    external/zlib \
+    system/core/libziparchive/include \
+    external/freetype/include \
+    external/boringssl/include \
+    external/libcxx/include \
+    external/libselinux/include \
+    external/libpng \
+    $(LOCAL_PATH)/gui/include \
+    $(LOCAL_PATH)/recovery_ui/include \
+    $(LOCAL_PATH)/otautil/include \
+    $(LOCAL_PATH)/install/include \
+    $(LOCAL_PATH)/fuse_sideload/include \
+    $(LOCAL_PATH)/install/include \
+    $(LOCAL_PATH)/twrpinstall/include \
+    $(LOCAL_PATH)/recovery_utils/include \
+    $(LOCAL_PATH)/libpixelflinger/include \
+    $(LOCAL_PATH)/minuitwrp/include \
+    $(LOCAL_PATH)/twinstall/include
 
-# librecovery_ui_ext (shared library)
-# ===================================
-include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES += libguitwrp libmodprobe
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libmtdutils libtwadbbu 
+LOCAL_SHARED_LIBRARIES += libbootloader_message libcrecovery libtwrpdigest libc++ libaosprecovery libcrypto libbase 
+LOCAL_SHARED_LIBRARIES += libziparchive libselinux libdl_android.bootstrap
 
-LOCAL_MODULE := librecovery_ui_ext
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL
-LOCAL_LICENSE_CONDITIONS := by_exception_only notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+ifneq ($(wildcard system/core/libsparse/Android.mk),)
+LOCAL_SHARED_LIBRARIES += libsparse
+endif
 
-# LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds.
-LOCAL_MULTILIB := first
+ifeq ($(TW_OEM_BUILD),true)
+    LOCAL_CFLAGS += -DTW_OEM_BUILD
+    BOARD_HAS_NO_REAL_SDCARD := true
+    TW_USE_TOOLBOX := true
+    TW_EXCLUDE_MTP := true
+    TW_EXCLUDE_TZDATA := true
+    TW_EXCLUDE_NANO := true
+    TW_EXCLUDE_BASH := true
+endif
 
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
+ifeq ($(AB_OTA_UPDATER),true)
+    LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+    TWRP_REQUIRED_MODULES += libhardware android.hardware.boot@1.0-service android.hardware.boot@1.0-service.rc \
+    android.hardware.boot@1.1-service android.hardware.boot@1.1-service.rc android.hardware.boot@1.1.xml
+endif
+
+ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
+    LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1
+    TWRP_REQUIRED_MODULES += android.hardware.health@2.1-service android.hardware.health@2.1-impl.recovery android.hardware.health@2.1-service.rc android.hardware.health@2.1.xml
+    TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-impl.recovery android.hardware.health@2.0-service.rc
+    ifeq ($(TW_EXCLUDE_LPDUMP),)
+        TWRP_REQUIRED_MODULES += lpdump lpdumpd.rc
+    endif
+endif
+
+ifeq ($(TW_USES_VENDOR_LIBS),true)
+    LOCAL_CFLAGS += -DUSE_VENDOR_LIBS=1
+endif
+
+ifeq ($(TW_NO_BIND_SYSTEM),true)
+    LOCAL_CFLAGS += -DTW_NO_BIND_SYSTEM
+endif
+
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+
+ifeq ($(TARGET_RECOVERY_TWRP_LIB),)
+    LOCAL_SRC_FILES += BasePartition.cpp
 else
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
+    LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_TWRP_LIB)
 endif
 
-LOCAL_WHOLE_STATIC_LIBRARIES := \
-    $(TARGET_RECOVERY_UI_LIB)
+LOCAL_C_INCLUDES += system/extras/ext4_utils
 
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblog \
-    librecovery_ui.recovery
+tw_git_revision := $(shell git -C $(LOCAL_PATH) rev-parse --short=8 HEAD 2>/dev/null)
+ifeq ($(shell git -C $(LOCAL_PATH) diff --quiet; echo $$?),1)
+    tw_git_revision := $(tw_git_revision)-dirty
+endif
+LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"'
 
-include $(BUILD_SHARED_LIBRARY)
+ifeq ($(TW_FORCE_USE_BUSYBOX), true)
+    TW_USE_TOOLBOX := false
+else
+    TW_USE_TOOLBOX := true
+endif
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs
+endif
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true)
+    LOCAL_CFLAGS += -DBOARD_USES_RECOVERY_AS_BOOT
+endif
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE), true)
+    LOCAL_CFLAGS += -DBOARD_BUILD_SYSTEM_ROOT_IMAGE
+endif
 
-# recovery_deps: A phony target that's depended on by `recovery`, which
-# builds additional modules conditionally based on Makefile variables.
-# ======================================================================
+#TWRP Build Flags
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_CFLAGS += -DTW_HAS_MTP
+endif
+ifneq ($(TW_NO_SCREEN_TIMEOUT),)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD), true)
+    LOCAL_CFLAGS += -DBOARD_HAS_NO_REAL_SDCARD
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+	LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifneq ($(TW_INCLUDE_DUMLOCK),)
+	LOCAL_CFLAGS += -DTW_INCLUDE_DUMLOCK
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifeq ($(TW_HAS_NO_BOOT_PARTITION), true)
+    LOCAL_CFLAGS += -DTW_HAS_NO_BOOT_PARTITION
+endif
+ifeq ($(TW_NO_REBOOT_BOOTLOADER), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_BOOTLOADER
+endif
+ifeq ($(TW_NO_REBOOT_RECOVERY), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_RECOVERY
+endif
+ifeq ($(TW_NO_BATT_PERCENT), true)
+    LOCAL_CFLAGS += -DTW_NO_BATT_PERCENT
+endif
+ifeq ($(TW_NO_CPU_TEMP), true)
+    LOCAL_CFLAGS += -DTW_NO_CPU_TEMP
+endif
+ifneq ($(TW_CUSTOM_POWER_BUTTON),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_POWER_BUTTON=$(TW_CUSTOM_POWER_BUTTON)
+endif
+ifeq ($(TW_ALWAYS_RMRF), true)
+    LOCAL_CFLAGS += -DTW_ALWAYS_RMRF
+endif
+ifeq ($(TW_NEVER_UNMOUNT_SYSTEM), true)
+    LOCAL_CFLAGS += -DTW_NEVER_UNMOUNT_SYSTEM
+endif
+ifeq ($(TW_NO_USB_STORAGE), true)
+    LOCAL_CFLAGS += -DTW_NO_USB_STORAGE
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_INJECTTWRP
+endif
+ifeq ($(TW_INCLUDE_BLOBPACK), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_BLOBPACK
+endif
+ifneq ($(TARGET_USE_CUSTOM_LUN_FILE_PATH),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(TARGET_USE_CUSTOM_LUN_FILE_PATH)\"
+endif
+ifneq ($(BOARD_UMS_LUNFILE),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(BOARD_UMS_LUNFILE)\"
+endif
+ifeq ($(TW_HAS_DOWNLOAD_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_DOWNLOAD_MODE
+endif
+ifeq ($(TW_HAS_EDL_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_EDL_MODE
+endif
+ifeq ($(TW_NO_SCREEN_BLANK), true)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+ifeq ($(TW_SDEXT_NO_EXT4), true)
+    LOCAL_CFLAGS += -DTW_SDEXT_NO_EXT4
+endif
+ifeq ($(TW_FORCE_CPUINFO_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_FORCE_CPUINFO_FOR_DEVICE_ID
+endif
+ifeq ($(TW_NO_EXFAT_FUSE), true)
+    LOCAL_CFLAGS += -DTW_NO_EXFAT_FUSE
+endif
+ifeq ($(TW_NO_HAPTICS), true)
+    LOCAL_CFLAGS += -DTW_NO_HAPTICS
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_L_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifneq ($(TW_ADDITIONAL_APEX_FILES),)
+    LOCAL_CFLAGS += -DTW_ADDITIONAL_APEX_FILES=$(TW_ADDITIONAL_APEX_FILES)
+endif
+ifneq ($(TW_LOAD_VENDOR_MODULES),)
+    LOCAL_CFLAGS += -DTW_LOAD_VENDOR_MODULES=$(TW_LOAD_VENDOR_MODULES)
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined
+    LOCAL_SHARED_LIBRARIES += libcryptfsfde
+    LOCAL_SHARED_LIBRARIES += libgpt_twrp libstatssocket.recovery
+    LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto
+    LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
+    TW_INCLUDE_CRYPTO_FBE := true
+    LOCAL_CFLAGS += -DTW_INCLUDE_FBE
+    LOCAL_SHARED_LIBRARIES += libtwrpfscrypt android.frameworks.stats@1.0 android.hardware.authsecret@1.0 \
+        android.hardware.oemlock@1.0
+    LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+		TW_INCLUDE_LIBRESETPROP := true
+        LOCAL_CFLAGS += -DTW_CRYPTO_USE_SYSTEM_VOLD
+        LOCAL_STATIC_LIBRARIES += libvolddecrypt
+    endif
+    endif
+endif
+WITH_CRYPTO_UTILS := \
+    $(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
+ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+endif
+ifeq ($(TW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID
+endif
+ifneq ($(TW_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=$(TW_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_SECONDARY_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_SECONDARY_BRIGHTNESS_PATH=$(TW_SECONDARY_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+	LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+endif
+ifneq ($(TW_DEFAULT_BRIGHTNESS),)
+	LOCAL_CFLAGS += -DTW_DEFAULT_BRIGHTNESS=$(TW_DEFAULT_BRIGHTNESS)
+endif
+ifneq ($(TW_CUSTOM_BATTERY_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_BATTERY_PATH=$(TW_CUSTOM_BATTERY_PATH)
+endif
+ifneq ($(TW_CUSTOM_CPU_TEMP_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_CPU_TEMP_PATH=$(TW_CUSTOM_CPU_TEMP_PATH)
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS),)
+    LOCAL_SHARED_LIBRARIES += libopenaes
+else
+    LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+endif
+ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),)
+  ifneq ($(filter msm8226 msm8x26 msm8610 msm8974 msm8x74 msm8084 msm8x84 apq8084 msm8909 msm8916 msm8992 msm8994 msm8952 msm8996 msm8937 msm8953 msm8998,$(TARGET_BOARD_PLATFORM)),)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+  else ifeq ($(TARGET_CPU_VARIANT),krait)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+  endif
+else ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),true)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+endif
+ifneq ($(TW_NO_LEGACY_PROPS),)
+	LOCAL_CFLAGS += -DTW_NO_LEGACY_PROPS
+endif
+ifneq ($(wildcard bionic/libc/include/sys/capability.h),)
+    LOCAL_CFLAGS += -DHAVE_CAPABILITIES
+endif
+ifneq ($(TARGET_RECOVERY_INITRC),)
+    TW_EXCLUDE_DEFAULT_USB_INIT := true
+endif
+LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD
+ifneq ($(TW_DEFAULT_LANGUAGE),)
+    LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=$(TW_DEFAULT_LANGUAGE)
+else
+    LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=en
+endif
+ifneq ($(TW_CLOCK_OFFSET),)
+	LOCAL_CFLAGS += -DTW_CLOCK_OFFSET=$(TW_CLOCK_OFFSET)
+endif
+ifneq ($(TW_OVERRIDE_SYSTEM_PROPS),)
+    TW_INCLUDE_LIBRESETPROP := true
+    LOCAL_CFLAGS += -DTW_OVERRIDE_SYSTEM_PROPS=$(TW_OVERRIDE_SYSTEM_PROPS)
+endif
+ifneq ($(TW_INCLUDE_LIBRESETPROP),)
+    LOCAL_SHARED_LIBRARIES += libresetprop
+    LOCAL_C_INCLUDES += external/magisk-prebuilt/include
+    LOCAL_CFLAGS += -DTW_INCLUDE_LIBRESETPROP
+endif
+ifeq ($(TW_EXCLUDE_NANO), true)
+    LOCAL_CFLAGS += -DTW_EXCLUDE_NANO
+endif
+
+LOCAL_C_INCLUDES += system/vold \
+
+TWRP_REQUIRED_MODULES += \
+    relink_libraries \
+    relink_binaries \
+    twrp_ramdisk \
+    bc \
+    dump_image \
+    erase_image \
+    flash_image \
+    mke2fs.conf \
+    pigz \
+    teamwin \
+    twrp \
+    fsck.fat \
+    fatlabel \
+    mkfs.fat \
+    permissive.sh \
+    simg2img_twrp \
+    libbootloader_message \
+    init.recovery.hlthchrg.rc \
+    init.recovery.service.rc \
+    init.recovery.ldconfig.rc \
+    awk \
+    toybox \
+    toolbox \
+    mkshrc_twrp \
+    plat_hwservice_contexts \
+    vendor_hwservice_contexts \
+    minadbd \
+    twrpbu \
+    adbd_system_api_recovery \
+    me.twrp.twrpapp.apk \
+    privapp-permissions-twrpapp.xml \
+    adbd_system_api_recovery \
+    libsync.recovery \
+    libmodprobe
+
+ifneq ($(TW_EXCLUDE_TZDATA), true)
+TWRP_REQUIRED_MODULES += \
+    tzdata_twrp
+endif
+
+ifneq ($(TW_EXCLUDE_NANO), true)
+TWRP_REQUIRED_MODULES += \
+    nano_twrp \
+    nano.rc
+endif
+
+ifneq ($(TW_EXCLUDE_BASH), true)
+    ifneq ($(wildcard external/bash/.),)
+    TWRP_REQUIRED_MODULES += \
+        bash_twrp
+    endif
+endif
+
+ifeq ($(TW_INCLUDE_REPACKTOOLS), true)
+TWRP_REQUIRED_MODULES += \
+    magiskboot
+endif
+
+ifeq ($(TW_INCLUDE_RESETPROP), true)
+TWRP_REQUIRED_MODULES += \
+    resetprop
+endif
+
+TWRP_REQUIRED_MODULES += \
+    hwservicemanager \
+    hwservicemanager.rc \
+    vndservicemanager \
+    vndservicemanager.rc
+
+ifneq ($(TW_INCLUDE_CRYPTO),)
+TWRP_REQUIRED_MODULES += \
+    vold_prepare_subdirs \
+    task_recovery_profiles.json \
+    fscryptpolicyget
+    ifneq ($(TW_INCLUDE_CRYPTO_FBE),)
+    TWRP_REQUIRED_MODULES += \
+        plat_service_contexts \
+        servicemanager \
+        servicemanager.rc
+    endif
+endif
+
+ifneq ($(wildcard external/zip/Android.mk),)
+    TWRP_REQUIRED_MODULES += zip
+endif
+ifneq ($(wildcard external/unzip/Android.mk),)
+    TWRP_REQUIRED_MODULES += unzip
+endif
+
+ifneq ($(TW_NO_EXFAT), true)
+    TWRP_REQUIRED_MODULES += mkexfatfs fsckexfat
+    ifneq ($(TW_NO_EXFAT_FUSE), true)
+        TWRP_REQUIRED_MODULES += exfat-fuse
+    endif
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD),)
+    TWRP_REQUIRED_MODULES += sgdisk
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    TWRP_REQUIRED_MODULES += openaes openaes_license
+endif
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+    TWRP_REQUIRED_MODULES += \
+        htcdumlock htcdumlocksys flash_imagesys dump_imagesys libbmlutils.so \
+        libflashutils.so libmmcutils.so libmtdutils.so HTCDumlock.apk
+endif
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    TWRP_REQUIRED_MODULES += fb2png
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    TWRP_REQUIRED_MODULES += orscmd
+endif
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+    TWRP_REQUIRED_MODULES += bml_over_mtd
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    TWRP_REQUIRED_MODULES += injecttwrp
+endif
+ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true)
+    TWRP_REQUIRED_MODULES += init.recovery.usb.rc
+endif
+ifeq ($(TWRP_INCLUDE_LOGCAT), true)
+    TWRP_REQUIRED_MODULES += logcat event-log-tags
+    ifeq ($(TARGET_USES_LOGD), true)
+        TWRP_REQUIRED_MODULES += logd libsysutils libnl init.recovery.logd.rc
+    endif
+endif
+# Allow devices to specify device-specific recovery dependencies
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+    TWRP_REQUIRED_MODULES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
+ifeq ($(TW_INCLUDE_NTFS_3G),true)
+    TWRP_REQUIRED_MODULES += \
+        mount.ntfs \
+        fsck.ntfs \
+        mkfs.ntfs
+endif
+ifeq ($(TARGET_USERIMAGES_USE_F2FS), true)
+    TWRP_REQUIRED_MODULES += sload.f2fs \
+        libfs_mgr \
+        fs_mgr \
+        libinit
+endif
+
+TWRP_REQUIRED_MODULES += file_contexts_text
+
+ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
+    TWRP_REQUIRED_MODULES += recovery-persist recovery-refresh
+endif
+
+LOCAL_REQUIRED_MODULES += $(TWRP_REQUIRED_MODULES)
+
+include $(BUILD_EXECUTABLE)
+
+# Symlink for file_contexts
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := recovery_deps
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL
-LOCAL_LICENSE_CONDITIONS := by_exception_only notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_MODULE := file_contexts_text
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := file_contexts.bin
 
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-LOCAL_REQUIRED_MODULES += \
-    make_f2fs.recovery \
-    sload_f2fs.recovery
-endif
-
-# On A/B devices recovery-persist reads the recovery related file from the persist storage and
-# copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist
-# parses the last_install file and reports the embedded update metrics. Also, the last_install file
-# will be deteleted after the report.
-LOCAL_REQUIRED_MODULES += recovery-persist
-ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
-LOCAL_REQUIRED_MODULES += recovery-refresh
-endif
+LOCAL_POST_INSTALL_CMD := \
+    $(hide) cp -f $(PRODUCT_OUT)/obj/ETC/file_contexts.bin_intermediates/file_contexts.concat.tmp $(TARGET_RECOVERY_ROOT_OUT)/file_contexts
 
 include $(BUILD_PHONY_PACKAGE)
 
+# recovery-persist (system partition dynamic executable run after /data mounts)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    recovery-persist.cpp 
+LOCAL_MODULE := recovery-persist
+LOCAL_SHARED_LIBRARIES := liblog libbase 
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += system/core/libstats/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-persist.rc
+include $(BUILD_EXECUTABLE)
+
+# recovery-refresh (system partition dynamic executable run at init)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    recovery-refresh.cpp
+LOCAL_MODULE := recovery-refresh
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-refresh.rc
+include $(BUILD_EXECUTABLE)
+
+# libmounts (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := mounts.cpp
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror
+LOCAL_MODULE := libmounts
+LOCAL_STATIC_LIBRARIES := libbase
+include $(BUILD_STATIC_LIBRARY)
+
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    install.cpp
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+
+ifeq ($(AB_OTA_UPDATER),true)
+    LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+    libminui \
+    libotautil \
+    libvintf \
+    libcrypto_utils \
+    libcrypto \
+    libbase \
+    libziparchive \
+
+include $(BUILD_STATIC_LIBRARY)
+
+commands_recovery_local_path := $(LOCAL_PATH)
+
 include \
-    $(LOCAL_PATH)/updater/Android.mk \
+    $(commands_TWRP_local_path)/updater/Android.mk
+
+include $(commands_TWRP_local_path)/mtp/ffs/Android.mk \
+    $(commands_TWRP_local_path)/minui/Android.mk
+
+#includes for TWRP
+include $(commands_TWRP_local_path)/injecttwrp/Android.mk \
+    $(commands_TWRP_local_path)/htcdumlock/Android.mk \
+    $(commands_TWRP_local_path)/mmcutils/Android.mk \
+    $(commands_TWRP_local_path)/bmlutils/Android.mk \
+    $(commands_TWRP_local_path)/prebuilt/Android.mk \
+    $(commands_TWRP_local_path)/mtdutils/Android.mk \
+    $(commands_TWRP_local_path)/flashutils/Android.mk \
+    $(commands_TWRP_local_path)/pigz/Android.mk \
+    $(commands_TWRP_local_path)/libtar/Android.mk \
+    $(commands_TWRP_local_path)/libcrecovery/Android.mk \
+    $(commands_TWRP_local_path)/libblkid/Android.mk \
+    $(commands_TWRP_local_path)/openaes/Android.mk \
+    $(commands_TWRP_local_path)/twrpTarMain/Android.mk \
+    $(commands_TWRP_local_path)/minzip/Android.mk \
+    $(commands_TWRP_local_path)/dosfstools/Android.mk \
+    $(commands_TWRP_local_path)/etc/Android.mk \
+    $(commands_TWRP_local_path)/simg2img/Android.mk \
+    $(commands_TWRP_local_path)/adbbu/Android.mk \
+    $(commands_TWRP_local_path)/twrpDigest/Android.mk \
+    $(commands_TWRP_local_path)/attr/Android.mk
+
+ifneq ($(TW_OZIP_DECRYPT_KEY),)
+    TWRP_REQUIRED_MODULES += ozip_decrypt
+    include $(commands_TWRP_local_path)/ozip_decrypt/Android.mk
+endif
+
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    include $(commands_TWRP_local_path)/crypto/fde/Android.mk
+    include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk
+    ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
+        include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk
+    endif
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+        include $(commands_TWRP_local_path)/crypto/vold_decrypt/Android.mk
+    endif
+    endif
+    include $(commands_TWRP_local_path)/gpt/Android.mk
+endif
+ifeq ($(BUILD_ID), GINGERBREAD)
+    TW_NO_EXFAT := true
+endif
+ifneq ($(TW_NO_EXFAT), true)
+    include $(commands_TWRP_local_path)/exfat/mkfs/Android.mk \
+            $(commands_TWRP_local_path)/exfat/fsck/Android.mk \
+            $(commands_TWRP_local_path)/fuse/Android.mk \
+            $(commands_TWRP_local_path)/exfat/libexfat/Android.mk
+    ifneq ($(TW_NO_EXFAT_FUSE), true)
+        include $(commands_TWRP_local_path)/exfat/fuse/Android.mk
+    endif
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    include $(commands_TWRP_local_path)/orscmd/Android.mk
+endif
+
+# FB2PNG
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    include $(commands_TWRP_local_path)/fb2png/Android.mk
+endif
+
+endif
+
+commands_TWRP_local_path :=
diff --git a/BasePartition.cpp b/BasePartition.cpp
new file mode 100644
index 0000000..3b34d75
--- /dev/null
+++ b/BasePartition.cpp
@@ -0,0 +1,28 @@
+/*
+	Copyright 2018 TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP 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 3 of the License, or
+	(at your option) any later version.
+
+	TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "partitions.hpp"
+
+// On devices that need it, this is supposed to be overridden using
+// TARGET_RECOVERY_TWRP_LIB to allow device-specific pre and post
+// wipe encryption calls.
+
+BasePartition* make_partition() {
+  return new BasePartition();
+}
diff --git a/NOTICE b/NOTICE
index c5b1efa..346e71a 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,190 +1,634 @@
 
-   Copyright (c) 2005-2008, The Android Open Source Project
+    Copyright (c) 2011-2016, Dees_Troy, bigbiff, Team Win
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
+    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 3 of the License, or
+    (at your option) any later version.
 
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
+    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.
 
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
 
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+                            Preamble
 
-   1. Definitions.
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
 
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
 
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
 
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
 
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
 
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
 
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
 
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
 
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
 
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
+  The precise terms and conditions for copying, distribution and
+modification follow.
 
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
+                       TERMS AND CONDITIONS
 
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
+  0. Definitions.
 
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
+  "This License" refers to version 3 of the GNU General Public License.
 
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
 
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
 
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
 
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
 
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
 
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
 
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
 
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
+  1. Source Code.
 
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
 
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
 
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
 
-   END OF TERMS AND CONDITIONS
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
 
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index 5ab19d1..820a8b7
--- a/README.md
+++ b/README.md
@@ -1,159 +1,3 @@
-The Recovery Image
-==================
+**Team Win Recovery Project (TWRP)**
 
-Quick turn-around testing
--------------------------
-
-* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT)
-
-      # After setting up environment and lunch.
-      m -j bootimage
-      adb reboot bootloader
-
-      # Pixel devices don't support booting into recovery mode with `fastboot boot`.
-      fastboot flash boot
-
-      # Manually choose `Recovery mode` from bootloader menu.
-
-* Devices with a separate recovery image (e.g. Nexus)
-
-      # After setting up environment and lunch.
-      mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
-      adb reboot bootloader
-
-      # To boot into the new recovery image without flashing the recovery partition:
-      fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
-
-Running the tests
------------------
-
-    # After setting up environment and lunch.
-    mmma -j bootable/recovery
-
-    # Running the tests on device (under normal boot).
-    adb root
-    adb sync data
-
-    # 32-bit device
-    adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
-
-    # Or 64-bit device
-    adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
-
-Running the manual tests
-------------------------
-
-`recovery-refresh` and `recovery-persist` executables exist only on systems without
-/cache partition. And we need to follow special steps to run tests for them.
-
-- Execute the test on an A/B device first. The test should fail but it will log
-  some contents to pmsg.
-
-- Reboot the device immediately and run the test again. The test should save the
-  contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
-  this file has expected contents.
-
-Using `adb` under recovery
---------------------------
-
-When running recovery image from debuggable builds (i.e. `-eng` or `-userdebug` build variants, or
-`ro.debuggable=1` in `/prop.default`), `adbd` service is enabled and started by default, which
-allows `adb` communication. A device should be listed under `adb devices`, either in `recovery` or
-`sideload` state.
-
-    $ adb devices
-    List of devices attached
-    1234567890abcdef    recovery
-
-Although `/system/bin/adbd` is built from the same code base as the one in the normal boot, only a
-subset of `adb` commands are meaningful under recovery, such as `adb root`, `adb shell`, `adb push`,
-`adb pull` etc. Since Android Q, `adb shell` no longer requires manually mounting `/system` from
-recovery menu.
-
-## Troubleshooting
-
-### `adb devices` doesn't show the device.
-
-    $ adb devices
-    List of devices attached
-
- * Ensure `adbd` is built and running.
-
-By default, `adbd` is always included into recovery image, as `/system/bin/adbd`. `init` starts
-`adbd` service automatically only in debuggable builds. This behavior is controlled by the recovery
-specific `/init.rc`, whose source code is at `bootable/recovery/etc/init.rc`.
-
-The best way to confirm a running `adbd` is by checking the serial output, which shows a service
-start log as below.
-
-    [   18.961986] c1      1 init: starting service 'adbd'...
-
- * Ensure USB gadget has been enabled.
-
-If `adbd` service has been started but device not shown under `adb devices`, use `lsusb(8)` (on
-host) to check if the device is visible to the host.
-
-`bootable/recovery/etc/init.rc` disables Android USB gadget (via sysfs) as part of the `fs` action
-trigger, and will only re-enable it in debuggable builds (the `on property` rule will always run
-_after_ `on fs`).
-
-    on fs
-        write /sys/class/android_usb/android0/enable 0
-
-    # Always start adbd on userdebug and eng builds
-    on property:ro.debuggable=1
-        write /sys/class/android_usb/android0/enable 1
-        start adbd
-
-If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt),
-check if configfs has been properly set up in init rc scripts. See the [example
-configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc)
-for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using
-configfs.
-
-### `adb devices` shows the device, but in `unauthorized` state.
-
-    $ adb devices
-    List of devices attached
-    1234567890abcdef    unauthorized
-
-recovery image doesn't honor the USB debugging toggle and the authorizations added under normal boot
-(because such authorization data stays in /data, which recovery doesn't mount), nor does it support
-authorizing a host device under recovery. We can use one of the following options instead.
-
- * **Option 1 (Recommended):** Authorize a host device with adb vendor keys.
-
-For debuggable builds, an RSA keypair can be used to authorize a host device that has the private
-key. The public key, defined via `PRODUCT_ADB_KEYS`, will be copied to `/adb_keys`. When starting
-the host-side `adbd`, make sure the filename (or the directory) of the matching private key has been
-added to `$ADB_VENDOR_KEYS`.
-
-    $ export ADB_VENDOR_KEYS=/path/to/adb/private/key
-    $ adb kill-server
-    $ adb devices
-
-`-user` builds filter out `PRODUCT_ADB_KEYS`, so no `/adb_keys` will be included there.
-
-Note that this mechanism applies to both of normal boot and recovery modes.
-
- * **Option 2:** Allow `adbd` to connect without authentication.
-   * `adbd` is compiled with `ALLOW_ADBD_NO_AUTH` (only on debuggable builds).
-   * `ro.adb.secure` has a value of `0`.
-
-Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its
-value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and
-`0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`.
-
-Localization of the background texts
-------------------------------------
-
-The recovery image supports localization of several background texts, e.g. installing, error,
-factory reset warnings, etc. For devices using `xxhdpi` and `xxxhdpi`, the build system generates
-these localization images dynamically since android-10 when building the recovery image. While
-the static images under res-*dpi/images/ is used for other display resolutions and as a
-backup.
-
-Check the invocation of the image_generator tool in the [makefile]. And the detailed usage of the
-image_generator is documented [here](./tools/image_generator/README.md).
-
-[makefile]: https://android.googlesource.com/platform/build/+/refs/heads/master/core/Makefile#1800
+You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
diff --git a/adbbu/Android.mk b/adbbu/Android.mk
new file mode 100755
index 0000000..c05f265
--- /dev/null
+++ b/adbbu/Android.mk
@@ -0,0 +1,47 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtwadbbu
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -fno-strict-aliasing -D_LARGFILE_SOURCE #-D_DEBUG_ADB_BACKUP
+LOCAL_C_INCLUDES += bionic external/zlib
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+endif
+
+LOCAL_SRC_FILES = \
+        libtwadbbu.cpp \
+        twrpback.cpp
+
+LOCAL_SHARED_LIBRARIES += libz libc libstdc++ libtwrpdigest
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        adbbumain.cpp
+
+LOCAL_SHARED_LIBRARIES += libstdc++ libz libtwadbbu
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+LOCAL_C_INCLUDES += bionic external/zlib
+LOCAL_CFLAGS:= -c -W
+LOCAL_MODULE:= twrpbu
+LOCAL_MODULE_STEM := bu
+LOCAL_MODULE_TAGS:= optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+include $(BUILD_EXECUTABLE)
diff --git a/adbbu/adbbumain.cpp b/adbbu/adbbumain.cpp
new file mode 100644
index 0000000..bd96b20
--- /dev/null
+++ b/adbbu/adbbumain.cpp
@@ -0,0 +1,95 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP 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 3 of the License, or
+		(at your option) any later version.
+
+		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+#include "twrpback.hpp"
+#include "twadbstream.h"
+
+
+int main(int argc, char **argv) {
+	int index;
+	size_t pos = 0;
+	bool ret = false;
+	size_t maxpos = strlen(TWRPARG) + 2;
+	std::string command;
+	twrpback tw;
+
+	tw.adblogwrite("Starting adb backup and restore\n");
+	command = argv[1];
+	for (index = 2; index < argc; index++) {
+		command = command + " " + argv[index];
+	}
+
+	pos = command.find(TWRP_BACKUP_ARG);
+	if (pos == std::string::npos || pos > (maxpos + strlen(TWRP_BACKUP_ARG) + 1)) {
+		pos = command.find(TWRP_RESTORE_ARG);
+	}
+	if (pos == std::string::npos || pos > maxpos + strlen(TWRP_STREAM_ARG) + 1) {
+		pos = command.find(TWRP_STREAM_ARG);
+	}
+
+	tw.adblogwrite("command: " + command + "\n");
+	command.erase(0, pos);
+	command.erase(std::remove(command.begin(), command.end(), '\''), command.end());
+
+	if (command.substr(0, sizeof(TWRP_BACKUP_ARG) - 1) == TWRP_BACKUP_ARG) {
+		tw.adblogwrite("Starting adb backup\n");
+		if (isdigit(*argv[1]))
+			tw.adbd_fd = atoi(argv[1]);
+		else
+			tw.adbd_fd = 1;
+		ret = tw.backup(command);
+	}
+	else if (command.substr(0, sizeof(TWRP_RESTORE_ARG) - 1) == TWRP_RESTORE_ARG) {
+		tw.adblogwrite("Starting adb restore\n");
+		if (isdigit(*argv[1]))
+			tw.adbd_fd = atoi(argv[1]);
+		else
+			tw.adbd_fd = 0;
+		ret = tw.restore();
+	}
+	else if (command.substr(0, sizeof(TWRP_STREAM_ARG) - 1) == TWRP_STREAM_ARG) {
+		tw.setStreamFileName(argv[3]);
+		tw.threadStream();
+		ret = true;
+	}
+	if (ret)
+		tw.adblogwrite("Adb backup/restore completed\n");
+	else
+		tw.adblogwrite("Adb backup/restore failed\n");
+
+	if (unlink(TW_ADB_BU_CONTROL) < 0) {
+		std::stringstream str;
+		str << strerror(errno);
+		tw.adblogwrite("Unable to remove TW_ADB_BU_CONTROL: " + str.str());
+	}
+	unlink(TW_ADB_TWRP_CONTROL);
+	if (ret)
+		return 0;
+	else
+		return -1;
+}
diff --git a/adbbu/libtwadbbu.cpp b/adbbu/libtwadbbu.cpp
new file mode 100644
index 0000000..39803b0
--- /dev/null
+++ b/adbbu/libtwadbbu.cpp
@@ -0,0 +1,307 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP 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 3 of the License, or
+		(at your option) any later version.
+
+		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+
+#include "twadbstream.h"
+#include "libtwadbbu.hpp"
+#include "twrpback.hpp"
+
+bool twadbbu::Check_ADB_Backup_File(std::string fname) {
+	struct AdbBackupStreamHeader adbbuhdr;
+	uint32_t crc, adbbuhdrcrc;
+	unsigned char buf[MAX_ADB_READ];
+	int bytes;
+
+	int fd = open(fname.c_str(), O_RDONLY);
+	if (fd < 0) {
+		printf("Unable to open %s for reading: %s.\n", fname.c_str(), strerror(errno));
+		close(fd);
+		return false;
+	}
+	bytes = read(fd, &buf, sizeof(buf));
+	close(fd);
+
+	if (memcpy(&adbbuhdr, buf, sizeof(adbbuhdr)) == NULL) {
+		printf("Unable to memcpy: %s (%s).\n", fname.c_str(), strerror(errno));
+		return false;
+	}
+	adbbuhdrcrc = adbbuhdr.crc;
+	memset(&adbbuhdr.crc, 0, sizeof(adbbuhdr.crc));
+	crc = crc32(0L, Z_NULL, 0);
+	crc = crc32(crc, (const unsigned char*) &adbbuhdr, sizeof(adbbuhdr));
+
+	return (crc == adbbuhdrcrc);
+}
+
+std::vector<std::string> twadbbu::Get_ADB_Backup_Files(std::string fname) {
+	unsigned char buf[MAX_ADB_READ];
+	struct AdbBackupControlType structcmd;
+	std::vector<std::string> adb_partitions;
+
+	int fd = open(fname.c_str(), O_RDONLY);
+	if (fd < 0) {
+		printf("Unable to open %s for reading: %s\n", fname.c_str(), strerror(errno));
+		close(fd);
+		return std::vector<std::string>();
+	}
+
+	while (true) {
+		std::string cmdstr;
+		int readbytes;
+		if ((readbytes = read(fd, &buf, sizeof(buf))) > 0) {
+			memcpy(&structcmd, buf, sizeof(structcmd));
+			assert(structcmd.type == TWENDADB || structcmd.type == TWIMG || structcmd.type == TWFN);
+			cmdstr = structcmd.type;
+			std::string cmdtype = cmdstr.substr(0, sizeof(structcmd.type) - 1);
+			if (cmdtype == TWENDADB) {
+				struct AdbBackupControlType endadb;
+				uint32_t crc, endadbcrc;
+
+				memcpy(&endadb, buf, sizeof(endadb));
+				endadbcrc = endadb.crc;
+				memset(&endadb.crc, 0, sizeof(endadb.crc));
+				crc = crc32(0L, Z_NULL, 0);
+				crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+				if (crc == endadbcrc) {
+					break;
+				}
+				else {
+					printf("ADB TWENDADB crc header doesn't match\n");
+					close(fd);
+					return std::vector<std::string>();
+				}
+			}
+			else if (cmdtype == TWIMG || cmdtype == TWFN) {
+				struct twfilehdr twfilehdr;
+				uint32_t crc, twfilehdrcrc;
+
+				memcpy(&twfilehdr, buf, sizeof(twfilehdr));
+				twfilehdrcrc = twfilehdr.crc;
+				memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+				crc = crc32(0L, Z_NULL, 0);
+				crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+				if (crc == twfilehdrcrc) {
+					std::string adbfile = twfilehdr.name;
+					int pos = adbfile.find_last_of("/") + 1;
+					adbfile = adbfile.substr(pos, adbfile.size());
+					adb_partitions.push_back(adbfile);
+				}
+				else {
+					printf("ADB crc header doesn't match\n");
+					close(fd);
+					return std::vector<std::string>();
+				}
+			}
+		}
+	}
+	close(fd);
+	return adb_partitions;
+}
+
+bool twadbbu::Write_ADB_Stream_Header(uint64_t partition_count) {
+	struct AdbBackupStreamHeader twhdr;
+	int adb_control_bu_fd;
+
+	memset(&twhdr, 0, sizeof(twhdr));
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		printf("Cannot write to TW_ADB_BU_CONTROL: %s\n", strerror(errno));
+		return false;
+	}
+
+	strncpy(twhdr.start_of_header, TWRP, sizeof(twhdr.start_of_header));
+	strncpy(twhdr.type, TWSTREAMHDR, sizeof(twhdr.type));
+	twhdr.partition_count = partition_count;
+	twhdr.version = ADB_BACKUP_VERSION;
+	memset(twhdr.space, 0, sizeof(twhdr.space));
+	twhdr.crc = crc32(0L, Z_NULL, 0);
+	twhdr.crc = crc32(twhdr.crc, (const unsigned char*) &twhdr, sizeof(twhdr));
+	if (write(adb_control_bu_fd, &twhdr, sizeof(twhdr)) < 0) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	return true;
+}
+
+bool twadbbu::Write_ADB_Stream_Trailer() {
+	int adb_control_bu_fd;
+	struct AdbBackupControlType endadb;
+
+	memset(&endadb, 0, sizeof(endadb));
+
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY);
+	if (adb_control_bu_fd < 0) {
+		printf("Error opening adb_control_bu_fd: %s\n", strerror(errno));
+		return false;
+	}
+	strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+	strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+	endadb.crc = crc32(0L, Z_NULL, 0);
+	endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+	if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 0) {
+		printf("Cannot write to ADB control: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression) {
+	int adb_control_bu_fd;
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	struct twfilehdr twfilehdr;
+	strncpy(twfilehdr.start_of_header, TWRP, sizeof(twfilehdr.start_of_header));
+	strncpy(twfilehdr.type, TWFN, sizeof(twfilehdr.type));
+	strncpy(twfilehdr.name, Backup_FileName.c_str(), sizeof(twfilehdr.name));
+	twfilehdr.size = (file_size == 0 ? 1024 : file_size);
+	twfilehdr.compressed = use_compression;
+	twfilehdr.crc = crc32(0L, Z_NULL, 0);
+	twfilehdr.crc = crc32(twfilehdr.crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+	printf("Sending TWFN to adb\n");
+	if (write(adb_control_bu_fd, &twfilehdr, sizeof(twfilehdr)) < 1) {
+		printf("Cannot that write to adb_control_bu_fd: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	fsync(adb_control_bu_fd);
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWIMG(std::string Backup_FileName, uint64_t file_size) {
+	int adb_control_bu_fd;
+	struct twfilehdr twimghdr;
+
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	strncpy(twimghdr.start_of_header, TWRP, sizeof(twimghdr.start_of_header));
+	strncpy(twimghdr.type, TWIMG, sizeof(twimghdr.type));
+	twimghdr.size = file_size;
+	strncpy(twimghdr.name, Backup_FileName.c_str(), sizeof(twimghdr.name));
+	twimghdr.crc = crc32(0L, Z_NULL, 0);
+	twimghdr.crc = crc32(twimghdr.crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+	printf("Sending TWIMG to adb\n");
+	if (write(adb_control_bu_fd, &twimghdr, sizeof(twimghdr)) < 1) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		return false;
+	}
+
+	return true;
+}
+
+bool twadbbu::Write_TWEOF() {
+	struct AdbBackupControlType tweof;
+	int adb_control_bu_fd;
+	int errctr = 0;
+
+	printf("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	while (adb_control_bu_fd < 0) {
+		printf("failed to open TW_ADB_BU_CONTROL. Retrying: %s\n", strerror(errno));
+		adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+		usleep(10000);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+			close(adb_control_bu_fd);
+			return false;
+		}
+	}
+	memset(&tweof, 0, sizeof(tweof));
+	strncpy(tweof.start_of_header, TWRP, sizeof(tweof.start_of_header));
+	strncpy(tweof.type, TWEOF, sizeof(tweof.type));
+	tweof.crc = crc32(0L, Z_NULL, 0);
+	tweof.crc = crc32(tweof.crc, (const unsigned char*) &tweof, sizeof(tweof));
+	printf("Sending TWEOF to adb backup\n");
+	if (write(adb_control_bu_fd, &tweof, sizeof(tweof)) < 0) {
+		printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWERROR() {
+	struct AdbBackupControlType twerror;
+	int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+	strncpy(twerror.start_of_header, TWRP, sizeof(twerror.start_of_header));
+	strncpy(twerror.type, TWERROR, sizeof(twerror.type));
+	memset(twerror.space, 0, sizeof(twerror.space));
+	twerror.crc = crc32(0L, Z_NULL, 0);
+	twerror.crc = crc32(twerror.crc, (const unsigned char*) &twerror, sizeof(twerror));
+	if (write(adb_control_bu_fd, &twerror, sizeof(twerror)) < 0) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWENDADB() {
+	struct AdbBackupControlType endadb;
+	int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+	memset(&endadb, 0, sizeof(endadb));
+	strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+	strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+	endadb.crc = crc32(0L, Z_NULL, 0);
+	endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+	printf("Sending TWENDADB to ADB Backup\n");
+	if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 1) {
+		printf("Cannot write to ADB_CONTROL_BU_FD: %s\n", strerror(errno));
+		return false;
+	}
+
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWDATA(FILE* adbd_fp) {
+	struct AdbBackupControlType data_block;
+	memset(&data_block, 0, sizeof(data_block));
+	strncpy(data_block.start_of_header, TWRP, sizeof(data_block.start_of_header));
+	strncpy(data_block.type, TWDATA, sizeof(data_block.type));
+	data_block.crc = crc32(0L, Z_NULL, 0);
+	data_block.crc = crc32(data_block.crc, (const unsigned char*) &data_block, sizeof(data_block));
+	if (fwrite(&data_block, 1, sizeof(data_block), adbd_fp) != sizeof(data_block))  {
+		return false;
+	}
+	return true;
+}
diff --git a/adbbu/libtwadbbu.hpp b/adbbu/libtwadbbu.hpp
new file mode 100644
index 0000000..9244bb5
--- /dev/null
+++ b/adbbu/libtwadbbu.hpp
@@ -0,0 +1,51 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP 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 3 of the License, or
+		(at your option) any later version.
+
+		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _LIBTWADBBU_HPP
+#define _LIBTWADBBU_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+
+#include "twadbstream.h"
+
+class twadbbu {
+public:
+	static bool Check_ADB_Backup_File(std::string fname);                                          //Check if file is ADB Backup file
+	static std::vector<std::string> Get_ADB_Backup_Files(std::string fname);                       //List ADB Files in String Vector
+	static bool Write_ADB_Stream_Header(uint64_t partition_count);                                 //Write ADB Stream Header to stream
+	static bool Write_ADB_Stream_Trailer();                                                        //Write ADB Stream Trailer to stream
+	static bool Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression); //Write a tar image to stream
+	static bool Write_TWIMG(std::string Backup_FileName, uint64_t file_size);                      //Write a partition image to stream
+	static bool Write_TWEOF();                                                                     //Write ADB End-Of-File marker to stream
+	static bool Write_TWERROR();                                                                   //Write error message occurred to stream
+	static bool Write_TWENDADB();                                                                  //Write ADB End-Of-Stream command to stream
+	static bool Write_TWDATA(FILE* adbd_fp);                                                       //Write TWDATA separator
+};
+
+#endif //__LIBTWADBBU_HPP
diff --git a/adbbu/twadbstream.h b/adbbu/twadbstream.h
new file mode 100644
index 0000000..bef463c
--- /dev/null
+++ b/adbbu/twadbstream.h
@@ -0,0 +1,112 @@
+/*
+	TWRP 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 3 of the License, or
+	(at your option) any later version.
+
+	TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TWADBSTREAM_H
+#define __TWADBSTREAM_H
+
+#define TWRPARG "--twrp"
+#define TWRP_BACKUP_ARG "backup"
+#define TWRP_RESTORE_ARG "restore"
+#define TWRP_STREAM_ARG "stream"
+#define TW_ADB_BACKUP "/tmp/twadbbackup"		//FIFO for adb backup
+#define TW_ADB_RESTORE "/tmp/twadbrestore"		//FIFO for adb restore
+#define TW_ADB_BU_CONTROL "/tmp/twadbbucontrol"		//FIFO for sending control from TWRP to ADB Backup
+#define TW_ADB_TWRP_CONTROL "/tmp/twadbtwrpcontrol"	//FIFO for sending control from ADB Backup to TWRP
+#define TWRP "TWRP"					//Magic Value
+#define ADB_BU_MAX_ERROR 20				//Max amount of errors for while loops
+#define ADB_BACKUP_OP "adbbackup"
+#define ADB_RESTORE_OP "adbrestore"
+
+//ADB Backup Control Commands
+#define TWSTREAMHDR "twstreamheader"			//TWRP Parititon Count Control
+#define TWFN "twfilename"				//TWRP Filename Control
+#define TWIMG "twimage"					//TWRP Image name Control
+#define TWEOF "tweof"					//End of File for Image/File
+#define MD5TRAILER "md5trailer"				//Image/File MD5 Trailer
+#define TWDATA "twdatablock"				// twrp adb backup data block header
+#define TWMD5 "twverifymd5"				//This command is compared to the md5trailer by ORS to verify transfer
+#define TWENDADB "twendadb"				//End Protocol
+#define TWERROR "twerror"				//Send error
+#define ADB_BACKUP_VERSION 3				//Backup Version
+#define DATA_MAX_CHUNK_SIZE 1048576			//Maximum size between each data header
+#define MAX_ADB_READ 512				//align with default tar size for amount to read fom adb stream
+
+/*
+structs for adb backup need to align to 512 bytes for reading 512
+bytes at a time
+Each struct contains a crc field so that when we are checking for commands
+and the crc doesn't match we still assume it's data matching the command
+struct but not really a command
+*/
+
+/*  stream format:
+  | TW ADB Backup Header   |
+  | TW File Stream Header  |
+  | File Data              |
+  | File/Image MD5 Trailer |
+  | TW File Stream Header  |
+  | File Data              |
+  | File/Image MD5 Trailer |
+  | etc...                 |
+*/
+
+//determine whether struct is 512 bytes, if not fail compilation
+#define ADBSTRUCT_STATIC_ASSERT(structure) typedef char adb_assertion[( !!(structure) )*2-1 ]
+
+//generic cmd structure to align fields for sending commands to and from adb backup
+struct AdbBackupControlType {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the type of command, TWENDADB, TWCNT, TWEOF, TWMD5, TWDATA and TWERROR
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupControlType struct to allow for making sure we are processing metadata
+	char space[484];				//stores space to align the struct to 512 bytes
+
+	//return a C++ string while not reading outside the type char array
+	std::string get_type() {
+		return std::string(type, strnlen(type, sizeof(type)-1));
+	}
+};
+
+//general info for file metadata stored in adb backup header
+struct twfilehdr {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the type of file header, TWFN or TWIMG
+	uint64_t size;					//stores the size of the file contained after this header in the backup file
+	uint64_t compressed;				//stores whether the file is compressed or not. 1 == compressed and 0 == uncompressed
+	uint32_t crc;					//stores the zlib 32 bit crc of the twfilehdr struct to allow for making sure we are processing metadata
+	char name[468];					//stores the filename of the file
+};
+
+//md5 for files stored as a trailer to files in the adb backup file to check
+//that they are restored correctly
+struct AdbBackupFileTrailer {
+	char start_of_trailer[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the AdbBackupFileTrailer type MD5TRAILER
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupFileTrailer struct to allow for making sure we are processing metadata
+	uint32_t ident;					//stores crc to determine if header is encapsulated in stream as data
+	char md5[40];					//stores the md5 computation of the file
+	char space[440];				//stores space to align the struct to 512 bytes
+};
+
+//info for version and number of partitions backed up
+struct AdbBackupStreamHeader {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the AdbBackupStreamHeader value TWCNT
+	uint64_t partition_count;			//stores the number of partitions to restore in the stream
+	uint64_t version;				//stores the version of adb backup. increment ADB_BACKUP_VERSION each time the metadata is updated
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupStreamHeader struct to allow for making sure we are processing metadata
+	char space[468];				//stores space to align the struct to 512 bytes
+};
+
+#endif //__TWADBSTREAM_H
diff --git a/adbbu/twrpback.cpp b/adbbu/twrpback.cpp
new file mode 100644
index 0000000..b3de76d
--- /dev/null
+++ b/adbbu/twrpback.cpp
@@ -0,0 +1,935 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP 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 3 of the License, or
+		(at your option) any later version.
+
+		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <zlib.h>
+#include <ctype.h>
+#include <semaphore.h>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <algorithm>
+#include <utils/threads.h>
+#include <pthread.h>
+
+#include "twadbstream.h"
+#include "twrpback.hpp"
+#include "libtwadbbu.hpp"
+#include "../twrpDigest/twrpDigest.hpp"
+#include "../twrpDigest/twrpMD5.hpp"
+#include "../twrpAdbBuFifo.hpp"
+
+twrpback::twrpback(void) {
+	adbd_fp = NULL;
+	read_fd = 0;
+	write_fd = 0;
+	adb_control_twrp_fd = 0;
+	adb_control_bu_fd = 0;
+	adb_read_fd = 0;
+	adb_write_fd = 0;
+	ors_fd = 0;
+	debug_adb_fd = 0;
+	firstPart = true;
+	createFifos();
+	adbloginit();
+}
+
+twrpback::~twrpback(void) {
+	adblogfile.close();
+	closeFifos();
+}
+
+void twrpback::printErrMsg(std::string msg, int errNum) {
+	std::stringstream str;
+	str << strerror(errNum);
+	adblogwrite(msg +  " " + str.str() + "\n");
+}
+
+void twrpback::createFifos(void) {
+        if (mkfifo(TW_ADB_BU_CONTROL, 0666) < 0) {
+                std::string msg = "Unable to create TW_ADB_BU_CONTROL fifo: ";
+		printErrMsg(msg, errno);
+        }
+        if (mkfifo(TW_ADB_TWRP_CONTROL, 0666) < 0) {
+                std::string msg = "Unable to create TW_ADB_TWRP_CONTROL fifo: ";
+		printErrMsg(msg, errno);
+                unlink(TW_ADB_BU_CONTROL);
+        }
+}
+
+void twrpback::closeFifos(void) {
+        if (unlink(TW_ADB_BU_CONTROL) < 0) {
+                std::string msg = "Unable to remove TW_ADB_BU_CONTROL: ";
+		printErrMsg(msg, errno);
+        }
+        if (unlink(TW_ADB_TWRP_CONTROL) < 0) {
+                std::string msg = "Unable to remove TW_ADB_TWRP_CONTROL: ";
+		printErrMsg(msg, errno);
+	}
+}
+
+void twrpback::adbloginit(void) {
+	adblogfile.open("/tmp/adb.log", std::fstream::app);
+}
+
+void twrpback::adblogwrite(std::string writemsg) {
+	adblogfile << writemsg << std::flush;
+}
+
+void twrpback::close_backup_fds() {
+	if (ors_fd > 0)
+		close(ors_fd);
+	if (write_fd > 0)
+		close(write_fd);
+	if (adb_read_fd > 0)
+		close(adb_read_fd);
+	if (adb_control_bu_fd > 0)
+		close(adb_control_bu_fd);
+	#ifdef _DEBUG_ADB_BACKUP
+		if (debug_adb_fd > 0)
+			close(debug_adb_fd);
+	#endif
+	if (adbd_fp != NULL)
+		fclose(adbd_fp);
+	if (access(TW_ADB_BACKUP, F_OK) == 0)
+		unlink(TW_ADB_BACKUP);
+}
+
+void twrpback::close_restore_fds() {
+	if (ors_fd > 0)
+		close(ors_fd);
+	if (write_fd > 0)
+		close(write_fd);
+	if (adb_control_bu_fd > 0)
+		close(adb_control_bu_fd);
+	if (adb_control_twrp_fd > 0)
+		close(adb_control_twrp_fd);
+	if (adbd_fp != NULL)
+		fclose(adbd_fp);
+	if (access(TW_ADB_RESTORE, F_OK) == 0)
+		unlink(TW_ADB_RESTORE);
+	#ifdef _DEBUG_ADB_BACKUP
+	if (debug_adb_fd > 0)
+		close(debug_adb_fd);
+	#endif
+}
+
+bool twrpback::backup(std::string command) {
+	twrpMD5 digest;
+	int bytes = 0, errctr = 0;
+	char adbReadStream[MAX_ADB_READ];
+	uint64_t totalbytes = 0, dataChunkBytes = 0, fileBytes = 0;
+	uint64_t md5fnsize = 0;
+	struct AdbBackupControlType endadb;
+
+	//ADBSTRUCT_STATIC_ASSERT(sizeof(endadb) == MAX_ADB_READ);
+
+	bool writedata = true;
+	bool compressed = false;
+	bool firstDataPacket = true;
+
+	adbd_fp = fdopen(adbd_fd, "w");
+	if (adbd_fp == NULL) {
+		adblogwrite("Unable to open adb_fp\n");
+		return false;
+	}
+
+	if (mkfifo(TW_ADB_BACKUP, 0666) < 0) {
+		adblogwrite("Unable to create TW_ADB_BACKUP fifo\n");
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_FIFO\n");
+
+	write_fd = open(TW_ADB_FIFO, O_WRONLY);
+	while (write_fd < 0) {
+		write_fd = open(TW_ADB_FIFO, O_WRONLY);
+		usleep(10000);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			std::string msg = "Unable to open TW_ADB_FIFO";
+			printErrMsg(msg, errno);
+			close_backup_fds();
+			return false;
+		}
+	}
+
+	memset(operation, 0, sizeof(operation));
+	if (snprintf(operation, sizeof(operation), "adbbackup %s", command.c_str()) >= (int)sizeof(operation)) {
+		adblogwrite("Operation too big to write to ORS_INPUT_FILE\n");
+		close_backup_fds();
+		return false;
+	}
+	if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+		adblogwrite("Unable to write to ORS_INPUT_FILE\n");
+		close_backup_fds();
+		return false;
+	}
+
+	memset(&adbReadStream, 0, sizeof(adbReadStream));
+	memset(&cmd, 0, sizeof(cmd));
+
+	adblogwrite("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		adblogwrite("Unable to open TW_ADB_BU_CONTROL for reading.\n");
+		close_backup_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_BACKUP\n");
+	adb_read_fd = open(TW_ADB_BACKUP, O_RDONLY | O_NONBLOCK);
+	if (adb_read_fd < 0) {
+		adblogwrite("Unable to open TW_ADB_BACKUP for reading.\n");
+		close_backup_fds();
+		return false;
+	}
+
+	//loop until TWENDADB sent
+	while (true) {
+		if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+			struct AdbBackupControlType structcmd;
+
+			memcpy(&structcmd, cmd, sizeof(cmd));
+			std::string cmdtype = structcmd.get_type();
+
+			//we received an error, exit and unlink
+			if (cmdtype == TWERROR) {
+				writedata = false;
+				adblogwrite("Error received. Quitting...\n");
+				close_backup_fds();
+				return false;
+			}
+			//we received the end of adb backup stream so we should break the loop
+			else if (cmdtype == TWENDADB) {
+				writedata = false;
+				adblogwrite("Recieved TWENDADB\n");
+				memcpy(&endadb, cmd, sizeof(cmd));
+				std::stringstream str;
+				str << totalbytes;
+				adblogwrite(str.str() + " total bytes written\n");
+				break;
+			}
+			//we recieved the TWSTREAMHDR structure metadata to write to adb
+			else if (cmdtype == TWSTREAMHDR) {
+				writedata = false;
+				adblogwrite("writing TWSTREAMHDR\n");
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					std::string msg = "Error writing TWSTREAMHDR to adbd";
+					printErrMsg(msg, errno);
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+			}
+			//we will be writing an image from TWRP
+			else if (cmdtype == TWIMG) {
+				struct twfilehdr twimghdr;
+
+				adblogwrite("writing TWIMG\n");
+				digest.init();
+				memset(&twimghdr, 0, sizeof(twimghdr));
+				memcpy(&twimghdr, cmd, sizeof(cmd));
+				md5fnsize = twimghdr.size;
+				compressed = false;
+
+				#ifdef _DEBUG_ADB_BACKUP
+				std::string debug_fname = "/data/media/";
+				debug_fname.append(basename(twimghdr.name));
+				debug_fname.append("-backup.img");
+				debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+				adblogwrite("Opening adb debug tar\n");
+				#endif
+
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					adblogwrite("Error writing TWIMG to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = true;
+			}
+			//we will be writing a tar from TWRP
+			else if (cmdtype == TWFN) {
+				struct twfilehdr twfilehdr;
+
+				adblogwrite("writing TWFN\n");
+				digest.init();
+
+				//ADBSTRUCT_STATIC_ASSERT(sizeof(twfilehdr) == MAX_ADB_READ);
+
+				memset(&twfilehdr, 0, sizeof(twfilehdr));
+				memcpy(&twfilehdr, cmd, sizeof(cmd));
+				md5fnsize = twfilehdr.size;
+
+				compressed = twfilehdr.compressed == 1 ? true: false;
+
+				#ifdef _DEBUG_ADB_BACKUP
+				std::string debug_fname = "/data/media/";
+				debug_fname.append(basename(twfilehdr.name));
+				debug_fname.append("-backup.tar");
+				debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+				adblogwrite("Opening adb debug tar\n");
+				#endif
+
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					adblogwrite("Error writing TWFN to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = true;
+			}
+			/*
+			We received the command that we are done with the file stream.
+			We will flush the remaining data stream.
+			Update md5 and write final results to adb stream.
+			If we need padding because the total bytes are not a multiple
+			of 512, we pad the end with 0s to we reach 512.
+			We also write the final md5 to the adb stream.
+			*/
+			else if (cmdtype == TWEOF) {
+				adblogwrite("received TWEOF\n");
+				while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream)) != 0)) {
+					totalbytes += bytes;
+					fileBytes += bytes;
+					dataChunkBytes += bytes;
+
+					char *writeAdbReadStream = new char [bytes];
+					memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+					digest.update((unsigned char *) writeAdbReadStream, bytes);
+					if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+						std::string msg = "Cannot write to adbd stream: ";
+						printErrMsg(msg, errno);
+					}
+					#if defined(_DEBUG_ADB_BACKUP)
+					if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+						std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+						printErrMsg(msg, errno);
+						close_restore_fds();
+						return false;
+					}
+					#endif
+					fflush(adbd_fp);
+					delete [] writeAdbReadStream;
+					memset(adbReadStream, 0, sizeof(adbReadStream));
+				}
+
+				if (fileBytes % DATA_MAX_CHUNK_SIZE != 0) {
+					int64_t count = fileBytes / DATA_MAX_CHUNK_SIZE + 1;
+					uint64_t ceilingBytes = count * DATA_MAX_CHUNK_SIZE;
+					char padding[ceilingBytes - fileBytes];
+					int paddingBytes = sizeof(padding);
+					memset(padding, 0, paddingBytes);
+					std::stringstream paddingStr;
+					paddingStr << paddingBytes;
+					adblogwrite("writing padding to stream: " + paddingStr.str() + " bytes\n");
+					if (fwrite(padding, 1, paddingBytes, adbd_fp) != sizeof(padding)) {
+						adblogwrite("Error writing padding to adbd\n");
+						close_backup_fds();
+						return false;
+					}
+					#if defined(_DEBUG_ADB_BACKUP)
+					if (write(debug_adb_fd, padding, paddingBytes) < 1) {
+						std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+						printErrMsg(msg, errno);
+						close_restore_fds();
+						return false;
+					}
+					#endif
+					totalbytes += paddingBytes;
+					digest.update((unsigned char *) padding, paddingBytes);
+					fflush(adbd_fp);
+				}
+
+				AdbBackupFileTrailer md5trailer;
+
+				memset(&md5trailer, 0, sizeof(md5trailer));
+
+				std::string md5string = digest.return_digest_string();
+
+				strncpy(md5trailer.start_of_trailer, TWRP, sizeof(md5trailer.start_of_trailer));
+				strncpy(md5trailer.type, MD5TRAILER, sizeof(md5trailer.type));
+				strncpy(md5trailer.md5, md5string.c_str(), sizeof(md5trailer.md5));
+
+				md5trailer.crc = crc32(0L, Z_NULL, 0);
+				md5trailer.crc = crc32(md5trailer.crc, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+
+				md5trailer.ident = crc32(0L, Z_NULL, 0);
+				md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+				md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+				if (fwrite(&md5trailer, 1, sizeof(md5trailer), adbd_fp) != sizeof(md5trailer))  {
+					adblogwrite("Error writing md5trailer to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = false;
+				firstDataPacket = true;
+				fileBytes = 0;
+			}
+			memset(&cmd, 0, sizeof(cmd));
+			dataChunkBytes = 0;
+		}
+		//If we are to write data because of a new file stream, lets write all the data.
+		//This will allow us to not write data after a command structure has been written
+		//to the adb stream.
+		//If the stream is compressed, we need to always write the data.
+		if (writedata || compressed) {
+			while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream))) > 0) {
+				if (firstDataPacket) {
+					if (!twadbbu::Write_TWDATA(adbd_fp)) {
+						close_backup_fds();
+						return false;
+					}
+					fileBytes += MAX_ADB_READ;
+					fflush(adbd_fp);
+					firstDataPacket = false;
+					dataChunkBytes += sizeof(adbReadStream);
+				}
+				char *writeAdbReadStream = new char [bytes];
+				memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+				digest.update((unsigned char *) writeAdbReadStream, bytes);
+
+				totalbytes += bytes;
+				fileBytes += bytes;
+				dataChunkBytes += bytes;
+
+				if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) != (unsigned long long)bytes) {
+					adblogwrite("Error writing backup data to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				#ifdef _DEBUG_ADB_BACKUP
+				if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+					std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+					printErrMsg(msg, errno);
+					close_restore_fds();
+					return false;
+				}
+				#endif
+				fflush(adbd_fp);
+				delete [] writeAdbReadStream;
+
+				memset(&adbReadStream, 0, sizeof(adbReadStream));
+
+				if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+					dataChunkBytes = 0;
+					firstDataPacket = true;
+				}
+				else if (dataChunkBytes > (DATA_MAX_CHUNK_SIZE - sizeof(adbReadStream))) {
+					int bytesLeft = DATA_MAX_CHUNK_SIZE - dataChunkBytes;
+					char extraData[bytesLeft];
+
+					memset(&extraData, 0, bytesLeft);
+					while ((bytes = read(adb_read_fd, &extraData, bytesLeft)) != 0) {
+						if (bytes > 0) {
+							totalbytes += bytes;
+							fileBytes += bytes;
+							dataChunkBytes += bytes;
+
+							bytesLeft -= bytes;
+							char *writeAdbReadStream = new char [bytes];
+							memcpy(writeAdbReadStream, extraData, bytes);
+
+							digest.update((unsigned char *) writeAdbReadStream, bytes);
+							if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+								std::string msg = "Cannot write to adbd stream: ";
+								printErrMsg(msg, errno);
+								close_restore_fds();
+								return false;
+							}
+							#ifdef _DEBUG_ADB_BACKUP
+							if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+								std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+								printErrMsg(msg, errno);
+								close_restore_fds();
+								return false;
+							}
+							#endif
+							fflush(adbd_fp);
+							delete [] writeAdbReadStream;
+						}
+						memset(&extraData, 0, bytesLeft);
+						if (bytesLeft == 0) {
+							break;
+						}
+					}
+
+					fflush(adbd_fp);
+					dataChunkBytes = 0;
+					firstDataPacket = true;
+				}
+			}
+		}
+	}
+
+	//Write the final end adb structure to the adb stream
+	if (fwrite(&endadb, 1, sizeof(endadb), adbd_fp) != sizeof(endadb)) {
+		adblogwrite("Error writing endadb to adbd\n");
+		close_backup_fds();
+		return false;
+	}
+	fflush(adbd_fp);
+	close_backup_fds();
+	return true;
+}
+
+bool twrpback::restore(void) {
+	twrpMD5 digest;
+	char cmd[MAX_ADB_READ];
+	char readAdbStream[MAX_ADB_READ];
+	struct AdbBackupControlType structcmd;
+	int errctr = 0;
+	uint64_t totalbytes = 0, dataChunkBytes = 0;
+	uint64_t md5fnsize = 0, fileBytes = 0;
+	bool read_from_adb;
+	bool md5sumdata;
+	bool compressed, tweofrcvd, extraData;
+
+	read_from_adb = true;
+
+	signal(SIGPIPE, SIG_IGN);
+	signal(SIGHUP, SIG_IGN);
+
+	adbd_fp = fdopen(adbd_fd, "r");
+	if (adbd_fp == NULL) {
+		adblogwrite("Unable to open adb_fp\n");
+		close_restore_fds();
+		return false;
+	}
+
+	if(mkfifo(TW_ADB_RESTORE, 0666)) {
+		adblogwrite("Unable to create TW_ADB_RESTORE fifo\n");
+		close_restore_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_FIFO\n");
+	write_fd = open(TW_ADB_FIFO, O_WRONLY);
+
+	while (write_fd < 0) {
+		write_fd = open(TW_ADB_FIFO, O_WRONLY);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			std::string msg = "Unable to open TW_ADB_FIFO.";
+			printErrMsg(msg, errno);
+			close_restore_fds();
+			return false;
+		}
+	}
+
+	memset(operation, 0, sizeof(operation));
+	sprintf(operation, "adbrestore");
+	if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+		adblogwrite("Unable to write to TW_ADB_FIFO\n");
+		close_restore_fds();
+		return false;
+	}
+
+	memset(&readAdbStream, 0, sizeof(readAdbStream));
+	memset(&cmd, 0, sizeof(cmd));
+
+	adblogwrite("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		std::string msg = "Unable to open TW_ADB_BU_CONTROL for writing.";
+		printErrMsg(msg, errno);
+		close_restore_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_TWRP_CONTROL\n");
+	adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+	if (adb_control_twrp_fd < 0) {
+		std::string msg = "Unable to open TW_ADB_TWRP_CONTROL for writing. Retrying...";
+		printErrMsg(msg, errno);
+		while (adb_control_twrp_fd < 0) {
+			adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+			usleep(10000);
+			errctr++;
+			if (errctr > ADB_BU_MAX_ERROR) {
+				adblogwrite("Unable to open TW_ADB_TWRP_CONTROL\n");
+				close_backup_fds();
+				return false;
+			}
+		}
+	}
+
+	//Loop until we receive TWENDADB from TWRP
+	while (true) {
+		memset(&cmd, 0, sizeof(cmd));
+		if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+			struct AdbBackupControlType structcmd;
+			memcpy(&structcmd, cmd, sizeof(cmd));
+			std::string cmdtype = structcmd.get_type();
+
+			//If we receive TWEOF from TWRP close adb data fifo
+			if (cmdtype == TWEOF) {
+				adblogwrite("Received TWEOF\n");
+				read_from_adb = true;
+				tweofrcvd = true;
+				close(adb_write_fd);
+			}
+			//Break when TWRP sends TWENDADB
+			else if (cmdtype == TWENDADB) {
+				adblogwrite("Received TWENDADB\n");
+				break;
+			}
+			//we received an error, exit and unlink
+			else if (cmdtype == TWERROR) {
+				adblogwrite("Error received. Quitting...\n");
+				close_restore_fds();
+				return false;
+			}
+		}
+		//If we should read from the adb stream, write commands and data to TWRP
+		if (read_from_adb) {
+			int readbytes;
+			if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) == sizeof(readAdbStream)) {
+				memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+				std::string cmdtype = structcmd.get_type();
+
+				//Tell TWRP we have read the entire adb stream
+				if (cmdtype == TWENDADB) {
+					struct AdbBackupControlType endadb;
+					uint32_t crc, endadbcrc;
+
+					md5sumdata = false;
+					memset(&endadb, 0, sizeof(endadb));
+					memcpy(&endadb, readAdbStream, sizeof(readAdbStream));
+					endadbcrc = endadb.crc;
+					memset(&endadb.crc, 0, sizeof(endadb.crc));
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+					if (crc == endadbcrc) {
+						adblogwrite("sending TWENDADB\n");
+						if (write(adb_control_twrp_fd, &endadb, sizeof(endadb)) < 1) {
+							std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+						read_from_adb = false;
+					}
+					else {
+						adblogwrite("ADB TWENDADB crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+				}
+				//Send TWRP partition metadata
+				else if (cmdtype == TWSTREAMHDR) {
+					struct AdbBackupStreamHeader cnthdr;
+					uint32_t crc, cnthdrcrc;
+
+					//ADBSTRUCT_STATIC_ASSERT(sizeof(cnthdr) == MAX_ADB_READ);
+
+					md5sumdata = false;
+					memset(&cnthdr, 0, sizeof(cnthdr));
+					memcpy(&cnthdr, readAdbStream, sizeof(readAdbStream));
+					cnthdrcrc = cnthdr.crc;
+					memset(&cnthdr.crc, 0, sizeof(cnthdr.crc));
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &cnthdr, sizeof(cnthdr));
+
+					if (crc == cnthdrcrc) {
+						adblogwrite("Restoring TWSTREAMHDR\n");
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWSTREAMHDR crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+				}
+				//Tell TWRP we are sending a partition image
+				else if (cmdtype == TWIMG) {
+					struct twfilehdr twimghdr;
+					uint32_t crc, twimghdrcrc;
+					md5sumdata = false;
+					fileBytes = 0;
+					read_from_adb = true;
+					dataChunkBytes = 0;
+					extraData = false;
+
+					digest.init();
+					adblogwrite("Restoring TWIMG\n");
+					memset(&twimghdr, 0, sizeof(twimghdr));
+					memcpy(&twimghdr, readAdbStream, sizeof(readAdbStream));
+					md5fnsize = twimghdr.size;
+					twimghdrcrc = twimghdr.crc;
+					memset(&twimghdr.crc, 0, sizeof(twimghdr.crc));
+
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+					if (crc == twimghdrcrc) {
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWIMG crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+
+					#ifdef _DEBUG_ADB_BACKUP
+					std::string debug_fname = "/data/media/";
+					debug_fname.append(basename(twimghdr.name));
+					debug_fname.append("-restore.img");
+					adblogwrite("image: " + debug_fname + "\n");
+					debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+					adblogwrite("Opened restore image\n");
+					#endif
+
+					adblogwrite("opening TW_ADB_RESTORE\n");
+					adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+				}
+				//Tell TWRP we are sending a tar stream
+				else if (cmdtype == TWFN) {
+					struct twfilehdr twfilehdr;
+					uint32_t crc, twfilehdrcrc;
+					fileBytes = 0;
+					md5sumdata = false;
+					read_from_adb = true;
+					dataChunkBytes = 0;
+					extraData = false;
+
+					digest.init();
+					adblogwrite("Restoring TWFN\n");
+					memset(&twfilehdr, 0, sizeof(twfilehdr));
+					memcpy(&twfilehdr, readAdbStream, sizeof(readAdbStream));
+					md5fnsize = twfilehdr.size;
+					twfilehdrcrc = twfilehdr.crc;
+					memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+					if (crc == twfilehdrcrc) {
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWFN crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+
+					#ifdef _DEBUG_ADB_BACKUP
+					std::string debug_fname = "/data/media/";
+					debug_fname.append(basename(twfilehdr.name));
+					debug_fname.append("-restore.tar");
+					adblogwrite("tar: " + debug_fname + "\n");
+					debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+					adblogwrite("Opened restore tar\n");
+					#endif
+
+					compressed = twfilehdr.compressed == 1 ? true: false;
+					adblogwrite("opening TW_ADB_RESTORE\n");
+					adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+				}
+				else if (cmdtype == MD5TRAILER) {
+					if (fileBytes >= md5fnsize)
+						close(adb_write_fd);
+					if (tweofrcvd) {
+						read_from_adb = true;
+						tweofrcvd = false;
+					}
+					else
+						read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+					md5sumdata = false;
+					if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+						close_restore_fds();
+						break;
+					}
+					continue;
+				}
+				//Send the tar or partition image md5 to TWRP
+				else if (cmdtype == TWDATA) {
+					dataChunkBytes += sizeof(readAdbStream);
+					while (true) {
+						if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) != sizeof(readAdbStream)) {
+							close_restore_fds();
+							return false;
+						}
+						memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+						std::string cmdtype = structcmd.get_type();
+
+						dataChunkBytes += readbytes;
+						totalbytes += readbytes;
+						fileBytes += readbytes;
+
+						if (cmdtype == MD5TRAILER) {
+							if (fileBytes >= md5fnsize)
+								close(adb_write_fd);
+							if (tweofrcvd) {
+								tweofrcvd = false;
+								read_from_adb = true;
+							}
+							else
+								read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+							if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+								close_restore_fds();
+								break;
+							}
+							break;
+						}
+
+						digest.update((unsigned char*)readAdbStream, readbytes);
+
+						read_from_adb = true;
+
+						#ifdef _DEBUG_ADB_BACKUP
+						if (write(debug_adb_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+						#endif
+
+						if (write(adb_write_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to TWRP ADB FIFO: ";
+							md5sumdata = true;
+							printErrMsg(msg, errno);
+							adblogwrite("end of stream reached.\n"); 
+							break;
+						}
+
+						if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+							dataChunkBytes = 0;
+							md5sumdata = false;
+							break;
+						}
+					}
+				}
+				else if (md5sumdata) {
+					digest.update((unsigned char*)readAdbStream, sizeof(readAdbStream));
+					md5sumdata = true;
+				}
+			}
+		}
+	}
+	std::stringstream str;
+	str << totalbytes;
+	close_restore_fds();
+	adblogwrite(str.str() + " bytes restored from adbbackup\n");
+	return true;
+}
+
+void twrpback::streamFileForTWRP(void) {
+	adblogwrite("streamFileForTwrp" + streamFn + "\n");
+}
+
+void twrpback::setStreamFileName(std::string fn) {
+	streamFn = fn;
+	adbd_fd = open(fn.c_str(), O_RDONLY);
+	if (adbd_fd < 0) {
+		adblogwrite("Unable to open adb_fd\n");
+		close(adbd_fd);
+		return;
+	}
+	restore();
+}
+
+void twrpback::threadStream(void) {
+	pthread_t thread;
+	ThreadPtr streamPtr = &twrpback::streamFileForTWRP;
+	PThreadPtr p = *(PThreadPtr*)&streamPtr;
+	pthread_create(&thread, NULL, p, this);
+	pthread_join(thread, NULL);
+}
+
+bool twrpback::checkMD5Trailer(char readAdbStream[], uint64_t md5fnsize, twrpMD5 *digest) {
+	struct AdbBackupFileTrailer md5tr;
+	uint32_t crc, md5trcrc, md5ident, md5identmatch;
+
+	//ADBSTRUCT_STATIC_ASSERT(sizeof(md5tr) == MAX_ADB_READ);
+	memcpy(&md5tr, readAdbStream, MAX_ADB_READ);
+	md5ident = md5tr.ident;
+
+	memset(&md5tr.ident, 0, sizeof(md5tr.ident));
+
+	md5identmatch = crc32(0L, Z_NULL, 0);
+	md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5tr, sizeof(md5tr));
+	md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+	if (md5identmatch == md5ident) {
+		adblogwrite("checking MD5TRAILER\n");
+		md5trcrc = md5tr.crc;
+		memset(&md5tr.crc, 0, sizeof(md5tr.crc));
+		crc = crc32(0L, Z_NULL, 0);
+		crc = crc32(crc, (const unsigned char*) &md5tr, sizeof(md5tr));
+		if (crc == md5trcrc) {
+			if (write(adb_control_twrp_fd, &md5tr, sizeof(md5tr)) < 1) {
+				std::string msg = "Cannot write to adb_control_twrp_fd: ";
+				printErrMsg(msg, errno);
+				close_restore_fds();
+				return false;
+			}
+		}
+		else {
+			adblogwrite("ADB MD5TRAILER crc header doesn't match\n");
+			close_restore_fds();
+			return false;
+		}
+
+		AdbBackupFileTrailer md5;
+
+		memset(&md5, 0, sizeof(md5));
+		strncpy(md5.start_of_trailer, TWRP, sizeof(md5.start_of_trailer));
+		strncpy(md5.type, TWMD5, sizeof(md5.type));
+		std::string md5string = digest->return_digest_string();
+		strncpy(md5.md5, md5string.c_str(), sizeof(md5.md5));
+
+		adblogwrite("sending MD5 verification: " + md5string + "\n");
+		if (write(adb_control_twrp_fd, &md5, sizeof(md5)) < 1) {
+			std::string msg = "Cannot write to adb_control_twrp_fd: ";
+			printErrMsg(msg, errno);
+			close_restore_fds();
+			return false;
+		}
+		return true;
+	}
+	return false;
+}
diff --git a/adbbu/twrpback.hpp b/adbbu/twrpback.hpp
new file mode 100644
index 0000000..edc1626
--- /dev/null
+++ b/adbbu/twrpback.hpp
@@ -0,0 +1,61 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP 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 3 of the License, or
+		(at your option) any later version.
+
+		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TWRPBACK_HPP
+#define _TWRPBACK_HPP
+
+#include <fstream>
+#include "../twrpDigest/twrpMD5.hpp"
+
+class twrpback {
+public:
+	int adbd_fd;                                                             // adbd data stream
+	twrpback(void);
+	virtual ~twrpback(void);
+	bool backup(std::string command);                                        // adb backup stream
+	bool restore(void);                                                      // adb restore stream
+	void adblogwrite(std::string writemsg);                                  // adb debugging log function
+	void createFifos(void);                                                  // create fifos needed for adb backup
+	void closeFifos(void);                                                   // close created fifos
+	void streamFileForTWRP(void);                                            // stream file to twrp via bu
+	void setStreamFileName(std::string fn);                                  // tell adb backup what file to load on storage
+	void threadStream(void);                                                 // thread bu for streaming
+
+private:
+	int read_fd;                                                             // ors input fd
+	int write_fd;                                                            // ors operation fd
+	int ors_fd;                                                              // ors output fd
+	int adb_control_twrp_fd;                                                 // fd for bu to twrp communication
+	int adb_control_bu_fd;                                                   // fd for twrp to bu communication
+	int adb_read_fd;                                                         // adb read data stream
+	int adb_write_fd;                                                        // adb write data stream
+	int debug_adb_fd;                                                        // fd to write debug tars
+	bool firstPart;                                                          // first partition in the stream
+	FILE *adbd_fp;                                                           // file pointer for adb stream
+	char cmd[512];                                                           // store result of commands
+	char operation[512];                                                     // operation to send to ors
+	std::ofstream adblogfile;                                                // adb stream log file
+	std::string streamFn;
+	typedef void (twrpback::*ThreadPtr)(void);
+	typedef void* (*PThreadPtr)(void *);
+	void adbloginit(void);                                                   // setup adb log stream file
+	void close_backup_fds();                                                 // close backup resources
+	void close_restore_fds();                                                // close restore resources
+	bool checkMD5Trailer(char adbReadStream[], uint64_t md5fnsize, twrpMD5* digest); // Check MD5 Trailer
+	void printErrMsg(std::string msg, int errNum);                          // print error msg to adb log
+};
+
+#endif // _TWRPBACK_HPP
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
new file mode 100644
index 0000000..44112fd
--- /dev/null
+++ b/applypatch/Android.mk
@@ -0,0 +1,192 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# libapplypatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch.cpp \
+    bspatch.cpp \
+    freecache.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libotafault \
+    libbase \
+    libcrypto \
+    libbspatch \
+    libbz \
+    libz
+LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# libapplypatch_modes (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch \
+    libbase \
+    libedify \
+    libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := applypatch_main.cpp
+LOCAL_MODULE := applypatch
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch_modes \
+    libapplypatch \
+    libbase \
+    libedify \
+    libotafault \
+    libcrypto \
+    libbspatch \
+    libbz
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libz \
+    libcutils
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+libimgdiff_src_files := imgdiff.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+    -Werror \
+    -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+    libbsdiff \
+    libdivsufsort \
+    libdivsufsort64 \
+    libziparchive \
+    libutils \
+    liblog \
+    libbase \
+    libz
+
+# libimgdiff (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# imgdiff (host static executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := imgdiff_main.cpp
+LOCAL_MODULE := imgdiff
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+    libimgdiff \
+    $(libimgdiff_static_libraries) \
+    libbz
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
old mode 100644
new mode 100755
index adda697..206a3a3
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -40,6 +40,9 @@
 #include <android-base/unique_fd.h>
 #include <openssl/sha.h>
 
+#include "bmlutils/bmlutils.h"
+#include "mtdutils/mtdutils.h"
+
 #include "edify/expr.h"
 #include "otautil/paths.h"
 #include "otautil/print_sha1.h"
@@ -186,15 +189,8 @@
       return false;
     }
 
-    unsigned char buffer[4096];
-    start = len;
-    for (size_t p = 0; p < len; p += sizeof(buffer)) {
-      size_t to_read = len - p;
-      if (to_read > sizeof(buffer)) {
-        to_read = sizeof(buffer);
-      }
+    const char* partition = pieces[1].c_str();
 
-      if (!android::base::ReadFully(fd, buffer, to_read)) {
         PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p;
         return false;
       }
diff --git a/attr/Android.mk b/attr/Android.mk
new file mode 100755
index 0000000..121ae02
--- /dev/null
+++ b/attr/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := listxattr.c
+LOCAL_CFLAGS := -c -W
+LOCAL_MODULE := listxattr
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_PACK_MODULE_RELOCATIONS := false
+
+ifneq ($(TARGET_ARCH), arm64)
+    ifneq ($(TARGET_ARCH), x86_64)
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker
+    else
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+    endif
+else
+    LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/attr/listxattr.c b/attr/listxattr.c
new file mode 100644
index 0000000..1aa8e14
--- /dev/null
+++ b/attr/listxattr.c
@@ -0,0 +1,202 @@
+/****************************************************************************
+ |  (C) Copyright 2008 Novell, Inc. All Rights Reserved.
+ |
+ |  GPLv2: This program is free software; you can redistribute it
+ |  and/or modify it under the terms of version 2 of the GNU General
+ |  Public License as published by the Free Software Foundation.
+ |
+ |  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.
+ +-------------------------------------------------------------------------*/
+/*
+ * NOTE from Dees_Troy: modified source to display values along with xattr names
+ * Below code comments about usage are no longer accurate but came from the
+ * original source code from the chromium project and combine features of
+ * listxattr and getfattr:
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/listxattr.c
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/getxattr.c
+ */
+/*
+ * LISTXATTR(2)             Linux Programmer's Manual            LISTXATTR(2)
+ *
+ *
+ *
+ * NAME
+ *        listxattr, llistxattr, flistxattr - list extended attribute names
+ *
+ * SYNOPSIS
+ *        #include <sys/types.h>
+ *        #include <attr/xattr.h>
+ *
+ *        ssize_t listxattr (const char *path,
+ *                             char *list, size_t size);
+ *        ssize_t llistxattr (const char *path,
+ *                             char *list, size_t size);
+ *        ssize_t flistxattr (int filedes,
+ *                             char *list, size_t size);
+ *
+ * DESCRIPTION
+ *        Extended  attributes  are  name:value  pairs associated with inodes
+ *        (files, directories, symlinks, etc).  They are  extensions  to  the
+ *        normal  attributes which are associated with all inodes in the sys-
+ *        tem (i.e. the stat(2)  data).   A  complete  overview  of  extended
+ *        attributes concepts can be found in attr(5).
+ *
+ *        listxattr retrieves the list of extended attribute names associated
+ *        with the given path in the filesystem.  The  list  is  the  set  of
+ *        (NULL-terminated)  names,  one  after the other.  Names of extended
+ *        attributes to which the calling process does not have access may be
+ *        omitted  from  the  list.  The length of the attribute name list is
+ *        returned.
+ *
+ *        llistxattr is identical to listxattr, except in the case of a  sym-
+ *        bolic  link, where the list of names of extended attributes associ-
+ *        ated with the link itself is retrieved, not the file that it refers
+ *        to.
+ *
+ *        flistxattr is identical to listxattr, only the open file pointed to
+ *        by filedes (as returned by open(2)) is  interrogated  in  place  of
+ *        path.
+ *
+ *        A  single  extended  attribute  name  is  a  simple NULL-terminated
+ *        string.  The name includes a namespace prefix; there  may  be  sev-
+ *        eral, disjoint namespaces associated with an individual inode.
+ *
+ *        An  empty  buffer  of  size  zero can be passed into these calls to
+ *        return the current size of the list of  extended  attribute  names,
+ *        which  can be used to estimate the size of a buffer which is suffi-
+ *        ciently large to hold the list of names.
+ *
+ * EXAMPLES
+ *        The list of names is returned as an unordered array of  NULL-termi-
+ *        nated  character  strings  (attribute  names  are separated by NULL
+ *        characters), like this:
+ *               user.name1\0system.name1\0user.name2\0
+ *
+ *        Filesystems like ext2, ext3 and  XFS  which  implement  POSIX  ACLs
+ *        using extended attributes, might return a list like this:
+ *               system.posix_acl_access\0system.posix_acl_default\0
+ *
+ * RETURN VALUE
+ *        On  success,  a  positive number is returned indicating the size of
+ *        the extended attribute name list.  On failure, -1 is  returned  and
+ *        errno is set appropriately.
+ *
+ *        If  the  size  of  the list buffer is too small to hold the result,
+ *        errno is set to ERANGE.
+ *
+ *        If extended attributes are not supported by the filesystem, or  are
+ *        disabled, errno is set to ENOTSUP.
+ *
+ *        The errors documented for the stat(2) system call are also applica-
+ *        ble here.
+ *
+ * AUTHORS
+ *        Andreas Gruenbacher, <a.gruenbacher@computer.org> and the  SGI  XFS
+ *        development  team,  <linux-xfs@oss.sgi.com>.   Please  send any bug
+ *        reports or comments to these addresses.
+ *
+ * SEE ALSO
+ *        getfattr(1),  setfattr(1),  getxattr(2),  open(2),  removexattr(2),
+ *        setxattr(2), stat(2), attr(5)
+ *
+ *
+ *
+ * Dec 2001                    Extended Attributes               LISTXATTR(2)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+//#include <eprintf.h>
+//#include <puny.h>
+
+/* dumpmem: dumps an n byte area of memory to screen */
+void dumpmem (const void *mem, unsigned int n)
+{
+	const char *c = mem;
+	unsigned i;
+	int all_text = 1;
+	if (n == 0) {
+		printf("<empty>");
+		return;
+	}
+	for (i = 0; i < n - 1; i++, c++) {
+		if (!isprint(*c)) {
+			all_text = 0;
+			break;
+		}
+	}
+	c = mem;
+	if (all_text) {
+		for (i = 0; i < n - 1; i++, c++) {
+			putchar(*c);
+		}
+		return;
+	} else {
+		char hex[(n * 2) + 1];
+		for(i = 0; i < n; i++, c++)
+			sprintf(hex + (i * 2), "%02X", *c);
+		hex[n] = 0;
+		printf("0x%s", hex);
+		return;
+	}
+}
+
+void dump_list (char *file, char *list, ssize_t size)
+{
+	int	c;
+	int	i;
+	int	first = 1;
+	int j = 0;
+	char xattr[1024];
+	char value[1024];
+	ssize_t	size2;
+	for (i = 0; i < size; i++) {
+		c = list[i];
+		if (c) {
+			if (first) {
+				putchar('\t');
+				first = 0;
+				j = 0;
+			}
+			putchar(c);
+			xattr[j++] = list[i];
+		} else {
+			xattr[j] = '\0';
+			size2 = getxattr(file, xattr, value, sizeof(value));
+			if (size2 < 0) {
+				printf("file=%s xattr=%s returned:", file, xattr);
+			} else {
+				putchar('=');
+				dumpmem(value, size2);
+			}
+			putchar('\n');
+			first = 1;
+		}
+	}
+}
+void usage (void)
+{
+	printf("listxattr <file>");
+}
+char	List[1<<17];
+int main (int argc, char *argv[])
+{
+	ssize_t	size;
+	if (argc < 2) {
+		usage();
+		exit(2);
+	}
+	size = listxattr(argv[1], List, sizeof(List));
+	if (size == -1) {
+		perror(argv[1]);
+		exit(2);
+	}
+	printf("xattrs for %s:\n", argv[1]);
+	dump_list(argv[1], List, size);
+	return 0;
+}
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..9cbada0
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_STATIC_LIBRARY := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+#Added for building TWRP dynamic:
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_SHARED_LIBRARIES := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c
new file mode 100755
index 0000000..382c0a5
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,197 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bmlutils.h>
+
+#include "../libcrecovery/common.h"
+
+static int restore_internal(const char* bml, const char* filename)
+{
+    char buf[4096];
+    int dstfd, srcfd, bytes_read, total_read = 0;
+    if (filename == NULL)
+        srcfd = 0;
+    else {
+        srcfd = open(filename, O_RDONLY | O_LARGEFILE);
+        if (srcfd < 0)
+            return 2;
+    }
+    dstfd = open(bml, O_RDWR | O_LARGEFILE);
+    if (dstfd < 0)
+        return 3;
+    if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
+        return 4;
+    do {
+        total_read += bytes_read = read(srcfd, buf, 4096);
+        if (!bytes_read)
+            break;
+        if (bytes_read < 4096)
+            memset(&buf[bytes_read], 0, 4096 - bytes_read);
+        if (write(dstfd, buf, 4096) < 4096)
+            return 5;
+    } while(bytes_read == 4096);
+    
+    close(dstfd);
+    close(srcfd);
+    
+    return 0;
+}
+
+int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
+        return 6;
+
+    int ret = -1;
+    if (strcmp(partition, "recoveryonly") != 0) {
+        // always restore boot, regardless of whether recovery or boot is flashed.
+        // this is because boot and recovery are the same on some samsung phones.
+        // unless of course, recoveryonly is explictly chosen (bml8)
+        ret = restore_internal(BOARD_BML_BOOT, filename);
+        if (ret != 0)
+            return ret;
+    }
+
+    if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
+        ret = restore_internal(BOARD_BML_RECOVERY, filename);
+
+    // support explicitly provided device paths
+    if (partition[0] == '/')
+        ret = restore_internal(partition, filename);
+    return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
+{
+    const char* bml;
+    if (strcmp("boot", partition) == 0)
+        bml = BOARD_BML_BOOT;
+    else if (strcmp("recovery", partition) == 0)
+        bml = BOARD_BML_RECOVERY;
+    else if (partition[0] == '/') {
+        // support explicitly provided device paths
+        bml = partition;
+    }
+    else {
+        printf("Invalid partition.\n");
+        return -1;
+    }
+
+    int ch;
+    FILE *in;
+    FILE *out;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    const char *in_file = bml;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(fileno(out));
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+}
+
+int cmd_bml_erase_raw_partition(const char *partition __unused)
+{
+    // TODO: implement raw wipe
+    return 0;
+}
+
+int cmd_bml_erase_partition(const char *partition __unused, const char *filesystem __unused)
+{
+    return -1;
+}
+
+int cmd_bml_mount_partition(const char *partition __unused, const char *mount_point __unused, const char *filesystem __unused, int read_only __unused)
+{
+    return -1;
+}
+
+int cmd_bml_get_partition_device(const char *partition __unused, char *device __unused)
+{
+    return -1;
+}
+
+int format_rfs_device (const char *device, const char *path) {
+    const char *fatsize = "32";
+    const char *sectorsize = "1";
+
+    if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
+        fatsize = "16";
+    }
+
+    // Just in case /data sector size needs to be altered
+    else if (strcmp(path, "/data") == 0 ) {
+        sectorsize = "1";
+    } 
+
+    // dump 10KB of zeros to partition before format due to fat.format bug
+    char cmd[PATH_MAX];
+
+    sprintf(cmd, "/system/bin/dd if=/dev/zero of=%s bs=4096 count=10", device);
+    if(__system(cmd)) {
+        printf("failure while zeroing rfs partition.\n");
+        return -1;
+    }
+
+    // Run fat.format
+    sprintf(cmd, "/system/bin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
+    if(__system(cmd)) {
+        printf("failure while running fat.format\n");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/bmlutils/bmlutils.h b/bmlutils/bmlutils.h
new file mode 100644
index 0000000..1e85f08
--- /dev/null
+++ b/bmlutils/bmlutils.h
@@ -0,0 +1,16 @@
+#ifndef BMLUTILS_H_
+#define BMLUTILS_H_
+
+int format_rfs_device (const char *device, const char *path);
+
+#define BML_UNLOCK_ALL				0x8A29		///< unlock all partition RO -> RW
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+#endif // BMLUTILS_H_
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
old mode 100644
new mode 100755
diff --git a/crypto/fde/Android.mk b/crypto/fde/Android.mk
new file mode 100755
index 0000000..faf7da8
--- /dev/null
+++ b/crypto/fde/Android.mk
@@ -0,0 +1,126 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfsfde
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libstdc++
+LOCAL_STATIC_LIBRARIES := libscrypttwrp_static
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += bionic external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+    LOCAL_CPPFLAGS := -std=c++11
+endif
+
+LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    #8.0 or higher
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+        LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+    else
+        LOCAL_SHARED_LIBRARIES += libe4crypt
+    endif
+    LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+        android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        #9.0 rules
+        LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+        LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+    else
+        #8.x rules
+        ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_SHARED_LIBRARIES += libkeyutils
+        endif
+        LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+    endif
+else
+    # <= 7.x rules
+    ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+        LOCAL_C_INCLUDES +=  external/boringssl/src/include
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+    else
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+    endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+        LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    else
+        LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+    endif
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpdec
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := main.cpp cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc libstdc++
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += bionic external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+    LOCAL_CPPFLAGS := -std=c++11
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    #8.0 or higher
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+        LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+    else
+        LOCAL_SHARED_LIBRARIES += libe4crypt
+    endif
+    LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+    android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        #9.0 rules
+        LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+        LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+    else
+        #8.x rules
+        ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_SHARED_LIBRARIES += libkeyutils
+        endif
+        LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+    endif
+else
+    # <= 7.x rules
+    ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+        LOCAL_C_INCLUDES +=  external/boringssl/src/include
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+    else
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+    endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+        LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    else
+        LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+    endif
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fde/cryptfs.cpp b/crypto/fde/cryptfs.cpp
new file mode 100644
index 0000000..fa0dc78
--- /dev/null
+++ b/crypto/fde/cryptfs.cpp
@@ -0,0 +1,1848 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TO DO:
+ *   1.  Perhaps keep several copies of the encrypted key, in case something
+ *       goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <errno.h>
+//#include <ext4_utils/ext4_crypt.h>
+//#include <ext4_utils/ext4_utils.h>
+#include <linux/kdev_t.h>
+//#include <fs_mgr.h>
+#include <time.h>
+#include <math.h>
+//#include <selinux/selinux.h>
+#include "cryptfs.h"
+//#include "secontext.h"
+#define LOG_TAG "Cryptfs"
+//#include "cutils/log.h"
+#include "cutils/properties.h"
+//#include "cutils/android_reboot.h"
+//#include "hardware_legacy/power.h"
+//#include <logwrap/logwrap.h>
+//#include "ScryptParameters.h"
+//#include "VolumeManager.h"
+//#include "VoldUtil.h"
+//#include "Ext4Crypt.h"
+//#include "f2fs_sparseblock.h"
+//#include "EncryptInplace.h"
+//#include "Process.h"
+#include "Keymaster.h"
+#if TW_KEYMASTER_MAX_API == 0
+#include <hardware/keymaster.h>
+#else // so far, all trees that have keymaster >= 1 have keymaster 1 support
+#include <stdbool.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster2.h>
+#endif
+//#include "android-base/properties.h"
+//#include <bootloader_message/bootloader_message.h>
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+#include <cryptfs_hw.h>
+#endif
+extern "C" {
+#include <crypto_scrypt.h>
+}
+#include <string>
+#include <vector>
+
+#define ALOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
+#define SLOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
+#define SLOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
+
+#define UNUSED __attribute__((unused))
+
+#define DM_CRYPT_BUF_SIZE 4096
+
+#define HASH_COUNT 2000
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+constexpr size_t INTERMEDIATE_KEY_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_IV_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_BUF_SIZE =
+    (INTERMEDIATE_KEY_LEN_BYTES + INTERMEDIATE_IV_LEN_BYTES);
+
+// SCRYPT_LEN is used by struct crypt_mnt_ftr for its intermediate key.
+static_assert(INTERMEDIATE_BUF_SIZE == SCRYPT_LEN,
+              "Mismatch of intermediate key sizes");
+
+#define KEY_IN_FOOTER  "footer"
+
+#define DEFAULT_HEX_PASSWORD "64656661756c745f70617373776f7264"
+#define DEFAULT_PASSWORD "default_password"
+
+#define CRYPTO_BLOCK_DEVICE "userdata"
+
+#define TABLE_LOAD_RETRIES 10
+
+#define RSA_KEY_SIZE 2048
+#define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8)
+#define RSA_EXPONENT 0x10001
+#define KEYMASTER_CRYPTFS_RATE_LIMIT 1  // Maximum one try per second
+#define KEY_LEN_BYTES 16
+
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+#define CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE (1)
+
+static unsigned char saved_master_key[MAX_KEY_LEN];
+static char *saved_mount_point;
+static int  master_key_saved = 0;
+static struct crypt_persist_data *persist_data = NULL;
+
+static int previous_type;
+
+static char key_fname[PROPERTY_VALUE_MAX] = "";
+static char real_blkdev[PROPERTY_VALUE_MAX] = "";
+static char file_system[PROPERTY_VALUE_MAX] = "";
+
+static void get_blkdev_size(int fd, unsigned long *nr_sec)
+{
+  if ( (ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
+    *nr_sec = 0;
+  }
+}
+
+#if TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto out;
+    }
+
+    rc = keymaster_open(mod, keymaster_dev);
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+            KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto out;
+    }
+
+    return 0;
+
+out:
+    *keymaster_dev = NULL;
+    return rc;
+}
+#else //TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster0_device_t **keymaster0_dev,
+                          keymaster1_device_t **keymaster1_dev,
+			  keymaster2_device_t **keymaster2_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto err;
+    }
+
+    printf("keymaster module name is %s\n", mod->name);
+    printf("keymaster version is %d\n", mod->module_api_version);
+
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    *keymaster2_dev = NULL;
+    if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
+        printf("Found keymaster2 module, using keymaster2 API.\n");
+        rc = keymaster2_open(mod, keymaster2_dev);
+    } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+        printf("Found keymaster1 module, using keymaster1 API.\n");
+        rc = keymaster1_open(mod, keymaster1_dev);
+    } else {
+        printf("Found keymaster0 module, using keymaster0 API.\n");
+        rc = keymaster0_open(mod, keymaster0_dev);
+    }
+
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+              KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto err;
+    }
+
+    return 0;
+
+err:
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    *keymaster2_dev = NULL;
+    return rc;
+}
+#endif //TW_KEYMASTER_MAX_API == 0
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+                            unsigned char *ikey, void *params);
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii);
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                const char *passwd, const char *mount_point, const char *label);
+int cryptfs_check_passwd_hw(char *passwd);
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+                                   unsigned char* master_key);
+
+static void convert_key_to_hex_ascii_for_upgrade(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii)
+{
+    unsigned int i, a;
+    unsigned char nibble;
+
+    for (i = 0, a = 0; i < keysize; i++, a += 2) {
+        /* For each byte, write out two ascii hex digits */
+        nibble = (master_key[i] >> 4) & 0xf;
+        master_key_ascii[a] = nibble + (nibble > 9 ? 0x57 : 0x30);
+
+        nibble = master_key[i] & 0xf;
+        master_key_ascii[a + 1] = nibble + (nibble > 9 ? 0x57 : 0x30);
+    }
+
+    /* Add the null termination */
+    master_key_ascii[a] = '\0';
+}
+
+static int get_keymaster_hw_fde_passwd(const char* passwd, unsigned char* newpw,
+                                  unsigned char* salt,
+                                  const struct crypt_mnt_ftr *ftr)
+{
+    /* if newpw updated, return 0
+     * if newpw not updated return -1
+     */
+    int rc = -1;
+
+    if (should_use_keymaster()) {
+        if (scrypt_keymaster(passwd, salt, newpw, (void*)ftr)) {
+            SLOGE("scrypt failed");
+        } else {
+            rc = 0;
+        }
+    }
+
+    return rc;
+}
+
+static int verify_hw_fde_passwd(const char *passwd, struct crypt_mnt_ftr* crypt_ftr)
+{
+    unsigned char newpw[32] = {0};
+    int key_index;
+    SLOGI("starting verify_hw_fde_passwd\n");
+    if (get_keymaster_hw_fde_passwd(passwd, newpw, crypt_ftr->salt, crypt_ftr))
+        key_index = set_hw_device_encryption_key(passwd,
+                                           (char*) crypt_ftr->crypto_type_name);
+    else
+        key_index = set_hw_device_encryption_key((const char*)newpw,
+                                           (char*) crypt_ftr->crypto_type_name);
+    return key_index;
+}
+
+static int verify_and_update_hw_fde_passwd(const char *passwd,
+                                           struct crypt_mnt_ftr* crypt_ftr)
+{
+    char* new_passwd = NULL;
+    unsigned char newpw[32] = {0};
+    int key_index = -1;
+    int passwd_updated = -1;
+    int ascii_passwd_updated = (crypt_ftr->flags & CRYPT_ASCII_PASSWORD_UPDATED);
+
+    key_index = verify_hw_fde_passwd(passwd, crypt_ftr);
+    if (key_index < 0) {
+        ++crypt_ftr->failed_decrypt_count;
+
+        if (ascii_passwd_updated) {
+            SLOGI("Ascii password was updated");
+        } else {
+            /* Code in else part would execute only once:
+             * When device is upgraded from L->M release.
+             * Once upgraded, code flow should never come here.
+             * L release passed actual password in hex, so try with hex
+             * Each nible of passwd was encoded as a byte, so allocate memory
+             * twice of password len plus one more byte for null termination
+             */
+            if (crypt_ftr->crypt_type == CRYPT_TYPE_DEFAULT) {
+                new_passwd = (char*)malloc(strlen(DEFAULT_HEX_PASSWORD) + 1);
+                if (new_passwd == NULL) {
+                    SLOGE("System out of memory. Password verification  incomplete");
+                    goto out;
+                }
+                strlcpy(new_passwd, DEFAULT_HEX_PASSWORD, strlen(DEFAULT_HEX_PASSWORD) + 1);
+            } else {
+                new_passwd = (char*)malloc(strlen(passwd) * 2 + 1);
+                if (new_passwd == NULL) {
+                    SLOGE("System out of memory. Password verification  incomplete");
+                    goto out;
+                }
+                convert_key_to_hex_ascii_for_upgrade((const unsigned char*)passwd,
+                                       strlen(passwd), new_passwd);
+            }
+            key_index = set_hw_device_encryption_key((const char*)new_passwd,
+                                       (char*) crypt_ftr->crypto_type_name);
+            if (key_index >=0) {
+                crypt_ftr->failed_decrypt_count = 0;
+                SLOGI("Hex password verified...will try to update with Ascii value");
+                /* Before updating password, tie that with keymaster to tie with ROT */
+
+                if (get_keymaster_hw_fde_passwd(passwd, newpw,
+                                                crypt_ftr->salt, crypt_ftr)) {
+                    passwd_updated = update_hw_device_encryption_key(new_passwd,
+                                     passwd, (char*)crypt_ftr->crypto_type_name);
+                } else {
+                    passwd_updated = update_hw_device_encryption_key(new_passwd,
+                                     (const char*)newpw, (char*)crypt_ftr->crypto_type_name);
+                }
+
+                if (passwd_updated >= 0) {
+                    crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+                    SLOGI("Ascii password recorded and updated");
+                } else {
+                    SLOGI("Passwd verified, could not update...Will try next time");
+                }
+            } else {
+                ++crypt_ftr->failed_decrypt_count;
+            }
+            free(new_passwd);
+        }
+    } else {
+        if (!ascii_passwd_updated)
+            crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+    }
+out:
+    // update footer before leaving
+    //put_crypt_ftr_and_key(crypt_ftr);
+    return key_index;
+}
+#endif
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs)
+{
+  strcpy(key_fname, key_location);
+  strcpy(real_blkdev, block_device);
+  strcpy(file_system, fs);
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+                                 const unsigned char *object,
+                                 const size_t object_size,
+                                 unsigned char **signature,
+                                 size_t *signature_size)
+{
+    SLOGI("TWRP keymaster max API: %i\n", TW_KEYMASTER_MAX_API);
+    unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+    size_t to_sign_size = sizeof(to_sign);
+    memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+    // To sign a message with RSA, the message must satisfy two
+    // constraints:
+    //
+    // 1. The message, when interpreted as a big-endian numeric value, must
+    //    be strictly less than the public modulus of the RSA key.  Note
+    //    that because the most significant bit of the public modulus is
+    //    guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+    //    key), an n-bit message with most significant bit 0 always
+    //    satisfies this requirement.
+    //
+    // 2. The message must have the same length in bits as the public
+    //    modulus of the RSA key.  This requirement isn't mathematically
+    //    necessary, but is necessary to ensure consistency in
+    //    implementations.
+    switch (ftr->kdf_type) {
+        case KDF_SCRYPT_KEYMASTER_UNPADDED:
+            // This is broken: It produces a message which is shorter than
+            // the public modulus, failing criterion 2.
+            memcpy(to_sign, object, object_size);
+            to_sign_size = object_size;
+            SLOGI("Signing unpadded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+            // This is broken: Since the value of object is uniformly
+            // distributed, it produces a message that is larger than the
+            // public modulus with probability 0.25.
+            memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
+            SLOGI("Signing end-padded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER:
+            // This ensures the most significant byte of the signed message
+            // is zero.  We could have zero-padded to the left instead, but
+            // this approach is slightly more robust against changes in
+            // object size.  However, it's still broken (but not unusably
+            // so) because we really should be using a proper deterministic
+            // RSA padding function, such as PKCS1.
+            memcpy(to_sign + 1, object, min((size_t)RSA_KEY_SIZE_BYTES - 1, object_size));
+            SLOGI("Signing safely-padded object");
+            break;
+        default:
+            SLOGE("Unknown KDF type %d", ftr->kdf_type);
+            return -1;
+    }
+
+    int rc = -1;
+
+#if TW_KEYMASTER_MAX_API >= 1
+    keymaster0_device_t *keymaster0_dev = 0;
+    keymaster1_device_t *keymaster1_dev = 0;
+    keymaster2_device_t *keymaster2_dev = 0;
+    if (keymaster_init(&keymaster0_dev, &keymaster1_dev, &keymaster2_dev)) {
+#else
+    keymaster_device_t *keymaster0_dev = 0;
+    if (keymaster_init(&keymaster0_dev)) {
+#endif
+        printf("Failed to init keymaster 0/1\n");
+        goto initfail;
+    }
+    if (keymaster0_dev) {
+        keymaster_rsa_sign_params_t params;
+        params.digest_type = DIGEST_NONE;
+        params.padding_type = PADDING_NONE;
+
+        rc = keymaster0_dev->sign_data(keymaster0_dev,
+                                      &params,
+                                      ftr->keymaster_blob,
+                                      ftr->keymaster_blob_size,
+                                      to_sign,
+                                      to_sign_size,
+                                      signature,
+                                      signature_size);
+        goto out;
+    }
+#if TW_KEYMASTER_MAX_API >= 1
+    else if (keymaster1_dev) {
+        keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size };
+        keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        };
+        keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+        keymaster_operation_handle_t op_handle;
+        keymaster_error_t error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                                        &param_set, NULL /* out_params */,
+                                                        &op_handle);
+        if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) {
+            // Key usage has been rate-limited.  Wait a bit and try again.
+            sleep(KEYMASTER_CRYPTFS_RATE_LIMIT);
+            error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                          &param_set, NULL /* out_params */,
+                                          &op_handle);
+        }
+        if (error != KM_ERROR_OK) {
+            printf("Error starting keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t input = { to_sign, to_sign_size };
+        size_t input_consumed;
+        error = keymaster1_dev->update(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       &input, &input_consumed, NULL /* out_params */,
+                                       NULL /* output */);
+        if (error != KM_ERROR_OK) {
+            printf("Error sending data to keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+        if (input_consumed != to_sign_size) {
+            // This should never happen.  If it does, it's a bug in the keymaster implementation.
+            printf("Keymaster update() did not consume all data.\n");
+            keymaster1_dev->abort(keymaster1_dev, op_handle);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t tmp_sig;
+        error = keymaster1_dev->finish(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       NULL /* verify signature */, NULL /* out_params */,
+                                       &tmp_sig);
+        if (error != KM_ERROR_OK) {
+            printf("Error finishing keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        *signature = (uint8_t*)tmp_sig.data;
+        *signature_size = tmp_sig.data_length;
+        rc = 0;
+    }
+    else if (keymaster2_dev) {
+        keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size };
+        keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        };
+        keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+        keymaster_operation_handle_t op_handle;
+        keymaster_key_param_t config_params[] = {
+            // Set these to crazy values so we don't need to synchronize
+            // the recovery with system updates.
+            // key upgrades will be required; it will be upgraded in-memory
+            keymaster_param_int(KM_TAG_OS_VERSION, 999999),
+	    keymaster_param_int(KM_TAG_OS_PATCHLEVEL, 209912),
+        };
+        keymaster_key_param_set_t config_param_set = { config_params, sizeof(config_params)/sizeof(*config_params) };
+        keymaster2_dev->configure(keymaster2_dev, &config_param_set);
+        keymaster_error_t error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+                                                        &param_set, NULL /* out_params */,
+                                                        &op_handle);
+        if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) {
+            // Key usage has been rate-limited.  Wait a bit and try again.
+            sleep(KEYMASTER_CRYPTFS_RATE_LIMIT);
+            error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+                                          &param_set, NULL /* out_params */,
+                                          &op_handle);
+        }
+
+        if (error == KM_ERROR_KEY_REQUIRES_UPGRADE) {
+            // Upgrade key in-memory if required
+            // Do not actually write it back; just keep it in memory
+            const keymaster_key_blob_t key_to_upd = key;
+            keymaster2_dev->upgrade_key(keymaster2_dev, &key_to_upd, &config_param_set, &key);
+            error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+                                          &param_set, NULL /* out_params */,
+                                          &op_handle);
+        }
+
+        if (error != KM_ERROR_OK) {
+            printf("Error starting keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t input = { to_sign, to_sign_size };
+        size_t input_consumed;
+        error = keymaster2_dev->update(keymaster2_dev, op_handle, NULL /* in_params */,
+                                       &input, &input_consumed, NULL /* out_params */,
+                                       NULL /* output */);
+        if (error != KM_ERROR_OK) {
+            printf("Error sending data to keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+        if (input_consumed != to_sign_size) {
+            // This should never happen.  If it does, it's a bug in the keymaster implementation.
+            printf("Keymaster update() did not consume all data.\n");
+            keymaster2_dev->abort(keymaster2_dev, op_handle);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t tmp_sig;
+        error = keymaster2_dev->finish(keymaster2_dev, op_handle, NULL /* in_params */,
+                                       NULL, NULL /* verify signature */, NULL /* out_params */,
+                                       &tmp_sig);
+        if (error != KM_ERROR_OK) {
+            printf("Error finishing keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        *signature = (uint8_t*)tmp_sig.data;
+        *signature_size = tmp_sig.data_length;
+        rc = 0;
+    }
+#endif // TW_KEYMASTER_API >= 1
+
+    out:
+#if TW_KEYMASTER_MAX_API >= 1
+        if (keymaster1_dev)
+            keymaster1_close(keymaster1_dev);
+#endif
+        if (keymaster0_dev)
+#if TW_KEYMASTER_MAX_API >= 1
+            keymaster0_close(keymaster0_dev);
+#else
+            keymaster_close(keymaster0_dev);
+#endif
+
+        if (rc == 0)
+            return 0; // otherwise we'll try for a newer keymaster API
+
+initfail:
+#if TW_KEYMASTER_MAX_API == 3
+    return keymaster_sign_object_for_cryptfs_scrypt(ftr->keymaster_blob, ftr->keymaster_blob_size,
+            KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign, to_sign_size, signature, signature_size,
+            ftr->keymaster_blob, KEYMASTER_BLOB_SIZE, &ftr->keymaster_blob_size);
+#endif //TW_KEYMASTER_MAX_API == 3
+#if TW_KEYMASTER_MAX_API >= 4
+    for (int c = 1;c <= 20;c++) { // 20 tries are enough for signing keymaster
+        if (c > 2)
+            usleep(5000); // if failed in two tries lets rest
+        auto result = keymaster_sign_object_for_cryptfs_scrypt(
+            ftr->keymaster_blob, ftr->keymaster_blob_size, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign,
+            to_sign_size, signature, signature_size);
+        switch (result) {
+            case KeymasterSignResult::ok:
+                return 0;
+            case KeymasterSignResult::upgrade:
+                break;
+            default:
+                return -1;
+        }
+        SLOGD("Upgrading key\n");
+        if (keymaster_upgrade_key_for_cryptfs_scrypt(
+                RSA_KEY_SIZE, RSA_EXPONENT, KEYMASTER_CRYPTFS_RATE_LIMIT, ftr->keymaster_blob,
+                ftr->keymaster_blob_size, ftr->keymaster_blob, KEYMASTER_BLOB_SIZE,
+                &ftr->keymaster_blob_size) != 0) {
+            SLOGE("Failed to upgrade key\n");
+            return -1;
+        }
+        /*if (put_crypt_ftr_and_key(ftr) != 0) {
+            SLOGE("Failed to write upgraded key to disk");
+        }*/
+        SLOGD("Key upgraded successfully\n");
+    }
+#endif
+    return -1;
+}
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+    memset(io, 0, dataSize);
+    io->data_size = dataSize;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags;
+    if (name) {
+        strlcpy(io->name, name, sizeof(io->name));
+    }
+}
+
+namespace {
+
+struct CryptoType;
+
+// Use to get the CryptoType in use on this device.
+const CryptoType &get_crypto_type();
+
+struct CryptoType {
+    // We should only be constructing CryptoTypes as part of
+    // supported_crypto_types[].  We do it via this pseudo-builder pattern,
+    // which isn't pure or fully protected as a concession to being able to
+    // do it all at compile time.  Add new CryptoTypes in
+    // supported_crypto_types[] below.
+    constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
+    constexpr CryptoType set_keysize(uint32_t size) const {
+        return CryptoType(this->property_name, this->crypto_name, size);
+    }
+    constexpr CryptoType set_property_name(const char *property) const {
+        return CryptoType(property, this->crypto_name, this->keysize);
+    }
+    constexpr CryptoType set_crypto_name(const char *crypto) const {
+        return CryptoType(this->property_name, crypto, this->keysize);
+    }
+
+    constexpr const char *get_property_name() const { return property_name; }
+    constexpr const char *get_crypto_name() const { return crypto_name; }
+    constexpr uint32_t get_keysize() const { return keysize; }
+
+ private:
+    const char *property_name;
+    const char *crypto_name;
+    uint32_t keysize;
+
+    constexpr CryptoType(const char *property, const char *crypto,
+                         uint32_t ksize)
+        : property_name(property), crypto_name(crypto), keysize(ksize) {}
+    friend const CryptoType &get_crypto_type();
+    static const CryptoType &get_device_crypto_algorithm();
+};
+
+// We only want to parse this read-only property once.  But we need to wait
+// until the system is initialized before we can read it.  So we use a static
+// scoped within this function to get it only once.
+const CryptoType &get_crypto_type() {
+    static CryptoType crypto_type = CryptoType::get_device_crypto_algorithm();
+    return crypto_type;
+}
+
+constexpr CryptoType default_crypto_type = CryptoType()
+    .set_property_name("AES-128-CBC")
+    .set_crypto_name("aes-cbc-essiv:sha256")
+    .set_keysize(16);
+
+constexpr CryptoType supported_crypto_types[] = {
+    default_crypto_type,
+    CryptoType()
+        .set_property_name("Speck128/128-XTS")
+        .set_crypto_name("speck128-xts-plain64")
+        .set_keysize(32),
+    // Add new CryptoTypes here.  Order is not important.
+};
+
+
+// ---------- START COMPILE-TIME SANITY CHECK BLOCK -------------------------
+// We confirm all supported_crypto_types have a small enough keysize and
+// had both set_property_name() and set_crypto_name() called.
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) { return N; }
+
+constexpr bool indexOutOfBoundsForCryptoTypes(size_t index) {
+    return (index >= array_length(supported_crypto_types));
+}
+
+constexpr bool isValidCryptoType(const CryptoType &crypto_type) {
+    return ((crypto_type.get_property_name() != nullptr) &&
+            (crypto_type.get_crypto_name() != nullptr) &&
+            (crypto_type.get_keysize() <= MAX_KEY_LEN));
+}
+
+// Note in C++11 that constexpr functions can only have a single line.
+// So our code is a bit convoluted (using recursion instead of a loop),
+// but it's asserting at compile time that all of our key lengths are valid.
+constexpr bool validateSupportedCryptoTypes(size_t index) {
+    return indexOutOfBoundsForCryptoTypes(index) ||
+        (isValidCryptoType(supported_crypto_types[index]) &&
+         validateSupportedCryptoTypes(index + 1));
+}
+
+static_assert(validateSupportedCryptoTypes(0),
+              "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
+              "incompletely constructed.");
+//  ---------- END COMPILE-TIME SANITY CHECK BLOCK -------------------------
+
+
+// Don't call this directly, use get_crypto_type(), which caches this result.
+const CryptoType &CryptoType::get_device_crypto_algorithm() {
+    constexpr char CRYPT_ALGO_PROP[] = "ro.crypto.fde_algorithm";
+    char paramstr[PROPERTY_VALUE_MAX];
+
+    property_get(CRYPT_ALGO_PROP, paramstr,
+                 default_crypto_type.get_property_name());
+    for (auto const &ctype : supported_crypto_types) {
+        if (strcmp(paramstr, ctype.get_property_name()) == 0) {
+            return ctype;
+        }
+    }
+    ALOGE("Invalid name (%s) for %s.  Defaulting to %s\n", paramstr,
+          CRYPT_ALGO_PROP, default_crypto_type.get_property_name());
+    return default_crypto_type;
+}
+
+}  // namespace
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+    int params[3] = {};
+    char *token;
+    char *saveptr;
+    int i;
+
+    /*
+     * The token we're looking for should be three integers separated by
+     * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+     */
+    for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+            token != nullptr && i < 3;
+            i++, token = strtok_r(nullptr, ":", &saveptr)) {
+        char *endptr;
+        params[i] = strtol(token, &endptr, 10);
+
+        /*
+         * Check that there was a valid number and it's 8-bit.
+         */
+        if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+            return false;
+        }
+    }
+    if (token != nullptr) {
+        return false;
+    }
+    *Nf = params[0]; *rf = params[1]; *pf = params[2];
+    return true;
+}
+
+uint32_t cryptfs_get_keysize() {
+    return get_crypto_type().get_keysize();
+}
+
+const char *cryptfs_get_crypto_name() {
+    return get_crypto_type().get_crypto_name();
+}
+
+static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+  static int cached_data = 0;
+  static off64_t cached_off = 0;
+  static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+  int fd;
+  //char key_loc[PROPERTY_VALUE_MAX];
+  //char real_blkdev[PROPERTY_VALUE_MAX];
+  int rc = -1;
+
+  if (!cached_data) {
+    //fs_mgr_get_crypt_info(fstab_default, key_loc, real_blkdev, sizeof(key_loc));
+
+    if (!strcmp(key_fname, KEY_IN_FOOTER)) {
+      if ( (fd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
+        SLOGE("Cannot open real block device %s\n", real_blkdev);
+        return -1;
+      }
+
+      unsigned long nr_sec = 0;
+      get_blkdev_size(fd, &nr_sec);
+      if (nr_sec != 0) {
+        /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+         * encryption info footer and key, and plenty of bytes to spare for future
+         * growth.
+         */
+        strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+        cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+        cached_data = 1;
+      } else {
+        SLOGE("Cannot get size of block device %s\n", real_blkdev);
+      }
+      close(fd);
+    } else {
+      strlcpy(cached_metadata_fname, key_fname, sizeof(cached_metadata_fname));
+      cached_off = 0;
+      cached_data = 1;
+    }
+  }
+
+  if (cached_data) {
+    if (metadata_fname) {
+        *metadata_fname = cached_metadata_fname;
+    }
+    if (off) {
+        *off = cached_off;
+    }
+    rc = 0;
+  }
+
+  return rc;
+}
+
+static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+  int fd;
+  unsigned int cnt;
+  off64_t starting_off;
+  int rc = -1;
+  char *fname = NULL;
+  struct stat statbuf;
+
+  if (get_crypt_ftr_info(&fname, &starting_off)) {
+    SLOGE("Unable to get crypt_ftr_info\n");
+    return -1;
+  }
+  if (fname[0] != '/') {
+    SLOGE("fde::get_crypt_ftr_and_key::Unexpected value for crypto key location: %s\n", fname);
+    return -1;
+  }
+  if ( (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) {
+    SLOGE("Cannot open footer file %s for get\n", fname);
+    return -1;
+  }
+
+  /* Make sure it's 16 Kbytes in length */
+  fstat(fd, &statbuf);
+  if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+    SLOGE("footer file %s is not the expected size!\n", fname);
+    goto errout;
+  }
+
+  /* Seek to the start of the crypt footer */
+  if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+    SLOGE("Cannot seek to real block device footer\n");
+    goto errout;
+  }
+
+  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    SLOGE("Cannot read real block device footer\n");
+    goto errout;
+  }
+
+  if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+    SLOGE("Bad magic for real block device %s\n", fname);
+    goto errout;
+  }
+
+  if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+    SLOGE("Cannot understand major version %d real block device footer; expected %d\n",
+          crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
+    goto errout;
+  }
+
+  // We risk buffer overflows with oversized keys, so we just reject them.
+  // 0-sized keys are problematic (essentially by-passing encryption), and
+  // AES-CBC key wrapping only works for multiples of 16 bytes.
+  if ((crypt_ftr->keysize == 0) || ((crypt_ftr->keysize % 16) != 0) ||
+      (crypt_ftr->keysize > MAX_KEY_LEN)) {
+    SLOGE("Invalid keysize (%u) for block device %s; Must be non-zero, "
+          "divisible by 16, and <= %d\n", crypt_ftr->keysize, fname,
+          MAX_KEY_LEN);
+    goto errout;
+  }
+
+  if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+    SLOGW("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+          crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
+  }
+
+  /* Success! */
+  rc = 0;
+
+errout:
+  close(fd);
+  return rc;
+}
+
+int cryptfs_check_footer()
+{
+    int rc = -1;
+    struct crypt_mnt_ftr crypt_ftr;
+
+    rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+    return rc;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii) {
+    unsigned int i, a;
+    unsigned char nibble;
+
+    for (i=0, a=0; i<keysize; i++, a+=2) {
+        /* For each byte, write out two ascii hex digits */
+        nibble = (master_key[i] >> 4) & 0xf;
+        master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+        nibble = master_key[i] & 0xf;
+        master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+    }
+
+    /* Add the null termination */
+    master_key_ascii[a] = '\0';
+
+}
+
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr,
+        const unsigned char *master_key, const char *real_blk_name,
+        const char *name, int fd, const char *extra_params) {
+  alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  char *crypt_params;
+  // We need two ASCII characters to represent each byte, and need space for
+  // the '\0' terminator.
+  char master_key_ascii[MAX_KEY_LEN * 2 + 1];
+  size_t buff_offset;
+  int i;
+
+  io = (struct dm_ioctl *) buffer;
+
+  /* Load the mapping table for this device */
+  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  io->target_count = 1;
+  tgt->status = 0;
+  tgt->sector_start = 0;
+  tgt->length = crypt_ftr->fs_size;
+  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+  buff_offset = crypt_params - buffer;
+  SLOGI(
+	"Creating crypto dev \"%s\"; cipher=%s, keysize=%u, real_dev=%s, len=%llu, params=\"%s\"\n",
+	name, crypt_ftr->crypto_type_name, crypt_ftr->keysize, real_blk_name, tgt->length * 512,
+	extra_params);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+  if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+    strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME);
+    if (is_ice_enabled())
+      convert_key_to_hex_ascii(master_key, sizeof(int), master_key_ascii);
+    else
+      convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  }
+  else {
+    convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+    strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+  }
+  snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s 0",
+           crypt_ftr->crypto_type_name, master_key_ascii,
+           real_blk_name, extra_params);
+
+  SLOGI("target_type = %s", tgt->target_type);
+  SLOGI("real_blk_name = %s, extra_params = %s", real_blk_name, extra_params);
+#else
+  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+  snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s",
+           crypt_ftr->crypto_type_name, master_key_ascii, real_blk_name,
+           extra_params);
+#endif
+
+  crypt_params += strlen(crypt_params) + 1;
+  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+  tgt->next = crypt_params - buffer;
+
+  for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+    int ret = ioctl(fd, DM_TABLE_LOAD, io);
+    if (!ret)  {
+      SLOGI("ioctl err: %d", ret);
+      break;
+    }
+    usleep(500000);
+  }
+
+  if (i == TABLE_LOAD_RETRIES) {
+    /* We failed to load the table, return an error */
+    return -1;
+  } else {
+    return i + 1;
+  }
+}
+
+static int get_dm_crypt_version(int fd, const char *name,  int *version)
+{
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl *io;
+    struct dm_target_versions *v;
+
+    io = (struct dm_ioctl *) buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+        return -1;
+    }
+
+    /* Iterate over the returned versions, looking for name of "crypt".
+     * When found, get and return the version.
+     */
+    v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+    while (v->next) {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+        if (! strcmp(v->name, "crypt") || ! strcmp(v->name, "req-crypt")) {
+#else
+        if (! strcmp(v->name, "crypt")) {
+#endif
+            /* We found the crypt driver, return the version, and get out */
+            version[0] = v->version[0];
+            version[1] = v->version[1];
+            version[2] = v->version[2];
+            return 0;
+        }
+        v = (struct dm_target_versions *)(((char *)v) + v->next);
+    }
+
+    return -1;
+}
+
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+static std::string extra_params_as_string(const std::vector<std::string>& extra_params_vec) {
+    if (extra_params_vec.empty()) return "";
+    char temp[10];
+    snprintf(temp, sizeof(temp), "%zd", extra_params_vec.size());
+    std::string extra_params = temp; //std::to_string(extra_params_vec.size());
+    for (const auto& p : extra_params_vec) {
+        extra_params.append(" ");
+        extra_params.append(p);
+    }
+    return extra_params;
+}
+#endif
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr* crypt_ftr, const unsigned char* master_key,
+                                 const char* real_blk_name, char* crypto_blk_name, const char* name,
+                                 uint32_t flags) {
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl* io;
+    unsigned int minor;
+    int fd = 0;
+    int err;
+    int retval = -1;
+    int version[3];
+    int load_count;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    char encrypted_state[PROPERTY_VALUE_MAX] = {0};
+    char progress[PROPERTY_VALUE_MAX] = {0};
+    const char *extra_params;
+#else
+    std::vector<std::string> extra_params_vec;
+#endif
+
+    if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+        SLOGE("Cannot open device-mapper\n");
+        goto errout;
+    }
+
+    io = (struct dm_ioctl*)buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+    err = ioctl(fd, DM_DEV_CREATE, io);
+    if (err) {
+        SLOGE("Cannot create dm-crypt device %s: %s\n", name, strerror(errno));
+        goto errout;
+    }
+
+    /* Get the device status, in particular, the name of it's device file */
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+    if (ioctl(fd, DM_DEV_STATUS, io)) {
+        SLOGE("Cannot retrieve dm-crypt device status\n");
+        goto errout;
+    }
+    minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+    snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+      /* Set fde_enabled if either FDE completed or in-progress */
+      property_get("ro.crypto.state", encrypted_state, ""); /* FDE completed */
+      property_get("vold.encrypt_progress", progress, ""); /* FDE in progress */
+      if (!strcmp(encrypted_state, "encrypted") || strcmp(progress, "")) {
+        if (is_ice_enabled()) {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled ice allow_encrypt_override";
+          else
+            extra_params = "fde_enabled ice";
+        } else {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled allow_encrypt_override";
+          else
+            extra_params = "fde_enabled";
+        }
+      } else {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled allow_encrypt_override";
+          else
+            extra_params = "fde_enabled";
+      }
+    } else {
+      extra_params = "";
+      if (! get_dm_crypt_version(fd, name, version)) {
+        /* Support for allow_discards was added in version 1.11.0 */
+        if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "2 allow_discards allow_encrypt_override";
+          else
+            extra_params = "1 allow_discards";
+          SLOGI("Enabling support for allow_discards in dmcrypt.\n");
+        }
+      }
+    }
+    load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+                                           extra_params);
+#else
+    if (!get_dm_crypt_version(fd, name, version)) {
+        /* Support for allow_discards was added in version 1.11.0 */
+        if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+            extra_params_vec.push_back(std::string("allow_discards")); // Used to be extra_params_vec.emplace_back("allow_discards"); but this won't compile in 5.1 trees
+        }
+    }
+    if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE) {
+        extra_params_vec.push_back(std::string("allow_encrypt_override")); // Used to be extra_params_vec.emplace_back("allow_encrypt_override"); but this won't compile in 5.1 trees
+    }
+    load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+                                           extra_params_as_string(extra_params_vec).c_str());
+#endif
+    if (load_count < 0) {
+        SLOGE("Cannot load dm-crypt mapping table.\n");
+        goto errout;
+    } else if (load_count > 1) {
+        SLOGI("Took %d tries to load dmcrypt table.\n", load_count);
+    }
+
+    /* Resume this device to activate it */
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+        SLOGE("Cannot resume the dm-crypt device\n");
+        goto errout;
+    }
+
+    /* We made it here with no errors.  Woot! */
+    retval = 0;
+
+errout:
+  close(fd);   /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+}
+
+int delete_crypto_blk_dev(const char *name)
+{
+  int fd;
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  int retval = -1;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) {
+    SLOGE("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_REMOVE, io)) {
+    SLOGE("Cannot remove dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);    /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+
+}
+
+static int pbkdf2(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params UNUSED)
+{
+    SLOGI("Using pbkdf2 for cryptfs KDF\n");
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    return PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+                                  HASH_COUNT, INTERMEDIATE_BUF_SIZE,
+                                  ikey) != 1;
+}
+
+static int scrypt(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params)
+{
+    SLOGI("Using scrypt for cryptfs KDF\n");
+
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+                  salt, SALT_LEN, N, r, p, ikey,
+                  INTERMEDIATE_BUF_SIZE);
+
+   return 0;
+}
+
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+                            unsigned char *ikey, void *params)
+{
+    SLOGI("Using scrypt with keymaster for cryptfs KDF\n");
+
+    int rc;
+    size_t signature_size;
+    unsigned char* signature;
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    rc = crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+                       salt, SALT_LEN, N, r, p, ikey,
+                       INTERMEDIATE_BUF_SIZE);
+
+    if (rc) {
+        SLOGE("scrypt failed");
+        return -1;
+    }
+
+    if (keymaster_sign_object(ftr, ikey, INTERMEDIATE_BUF_SIZE,
+                              &signature, &signature_size)) {
+        SLOGE("Keymaster signing failed");
+        return -1;
+    }
+
+    rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+                       N, r, p, ikey, INTERMEDIATE_BUF_SIZE);
+    free(signature);
+
+    if (rc) {
+        SLOGE("scrypt failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int decrypt_master_key_aux(const char *passwd, unsigned char *salt,
+                                  const unsigned char *encrypted_master_key,
+                                  size_t keysize,
+                                  unsigned char *decrypted_master_key,
+                                  kdf_func kdf, void *kdf_params,
+                                  unsigned char** intermediate_key,
+                                  size_t* intermediate_key_size)
+{
+  unsigned char ikey[INTERMEDIATE_BUF_SIZE] = { 0 };
+  EVP_CIPHER_CTX d_ctx;
+  int decrypted_len, final_len;
+
+  /* Turn the password into an intermediate key and IV that can decrypt the
+     master key */
+  if (kdf(passwd, salt, ikey, kdf_params)) {
+    SLOGE("kdf failed");
+    return -1;
+  }
+
+  /* Initialize the decryption engine */
+  EVP_CIPHER_CTX_init(&d_ctx);
+  if (! EVP_DecryptInit_ex(&d_ctx, EVP_aes_128_cbc(), NULL, ikey, ikey+INTERMEDIATE_KEY_LEN_BYTES)) {
+    return -1;
+  }
+  EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+  /* Decrypt the master key */
+  if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+                            encrypted_master_key, keysize)) {
+    return -1;
+  }
+  if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+    return -1;
+  }
+
+  if (decrypted_len + final_len != static_cast<int>(keysize)) {
+    return -1;
+  }
+
+  /* Copy intermediate key if needed by params */
+  if (intermediate_key && intermediate_key_size) {
+    *intermediate_key = (unsigned char*) malloc(INTERMEDIATE_KEY_LEN_BYTES);
+    if (*intermediate_key) {
+      memcpy(*intermediate_key, ikey, INTERMEDIATE_KEY_LEN_BYTES);
+      *intermediate_key_size = INTERMEDIATE_KEY_LEN_BYTES;
+    }
+  }
+
+  EVP_CIPHER_CTX_cleanup(&d_ctx);
+
+  return 0;
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+    if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+        *kdf = scrypt_keymaster;
+        *kdf_params = ftr;
+    } else if (ftr->kdf_type == KDF_SCRYPT) {
+        *kdf = scrypt;
+        *kdf_params = ftr;
+    } else {
+        *kdf = pbkdf2;
+        *kdf_params = NULL;
+    }
+}
+
+static int decrypt_master_key(const char *passwd, unsigned char *decrypted_master_key,
+                              struct crypt_mnt_ftr *crypt_ftr,
+                              unsigned char** intermediate_key,
+                              size_t* intermediate_key_size)
+{
+    kdf_func kdf;
+    void *kdf_params;
+    int ret;
+
+    get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+    ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+                                 crypt_ftr->keysize,
+                                 decrypted_master_key, kdf, kdf_params,
+                                 intermediate_key, intermediate_key_size);
+    if (ret != 0) {
+        SLOGW("failure decrypting master key");
+    }
+
+    return ret;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+             const char *passwd, const char *mount_point, const char *label)
+{
+  /* Allocate enough space for a 256 bit key, but we may use less */
+  unsigned char decrypted_master_key[32];
+  char crypto_blkdev[MAXPATHLEN];
+  //char real_blkdev[MAXPATHLEN];
+  unsigned int orig_failed_decrypt_count;
+  int rc = 0;
+
+  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+  orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+  //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+  int key_index = 0;
+  if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+    key_index = verify_and_update_hw_fde_passwd(passwd, crypt_ftr);
+    if (key_index < 0) {
+      rc = -1;
+      goto errout;
+    }
+    else {
+      if (is_ice_enabled()) {
+#ifndef CONFIG_HW_DISK_ENCRYPT_PERF
+        if (create_crypto_blk_dev(crypt_ftr, (unsigned char*)&key_index,
+                            real_blkdev, crypto_blkdev, label, 0)) {
+          SLOGE("Error creating decrypted block device");
+          rc = -1;
+          goto errout;
+        }
+#endif
+      } else {
+        if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+                            real_blkdev, crypto_blkdev, label, 0)) {
+          SLOGE("Error creating decrypted block device");
+          rc = -1;
+          goto errout;
+        }
+      }
+    }
+  }
+
+  if (rc == 0) {
+    /* Save the name of the crypto block device
+     * so we can mount it when restarting the framework. */
+#ifdef CONFIG_HW_DISK_ENCRYPT_PERF
+    if (!is_ice_enabled())
+#endif
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+    master_key_saved = 1;
+  }
+
+ errout:
+  return rc;
+}
+#endif
+
+static int try_mount_multiple_fs(const char *crypto_blkdev,
+                                 const char *mount_point,
+                                 const char *file_system)
+{
+    if (!mount(crypto_blkdev, mount_point, file_system, 0, NULL))
+        return 0;
+    if (strcmp(file_system, "ext4") &&
+        !mount(crypto_blkdev, mount_point, "ext4", 0, NULL))
+        return 0;
+    if (strcmp(file_system, "f2fs") &&
+        !mount(crypto_blkdev, mount_point, "f2fs", 0, NULL))
+        return 0;
+    return 1;
+}
+
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                                   const char *passwd, const char *mount_point, const char *label)
+{
+  unsigned char decrypted_master_key[MAX_KEY_LEN];
+  char crypto_blkdev[MAXPATHLEN];
+  //char real_blkdev[MAXPATHLEN];
+  char tmp_mount_point[64];
+  unsigned int orig_failed_decrypt_count;
+  int rc;
+  int use_keymaster = 0;
+  unsigned char* intermediate_key = 0;
+  size_t intermediate_key_size = 0;
+  int N = 1 << crypt_ftr->N_factor;
+  int r = 1 << crypt_ftr->r_factor;
+  int p = 1 << crypt_ftr->p_factor;
+
+  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+  orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+  if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+    if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+                           &intermediate_key, &intermediate_key_size)) {
+      SLOGE("Failed to decrypt master key\n");
+      rc = -1;
+      goto errout;
+    }
+  }
+
+  //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+  // Create crypto block device - all (non fatal) code paths
+  // need it
+  if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, label, 0)) {
+      SLOGE("Error creating decrypted block device\n");
+      rc = -1;
+      goto errout;
+  }
+
+  /* Work out if the problem is the password or the data */
+  unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+                                                 scrypted_intermediate_key)];
+
+  rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+                     crypt_ftr->salt, sizeof(crypt_ftr->salt),
+                     N, r, p, scrypted_intermediate_key,
+                     sizeof(scrypted_intermediate_key));
+
+  // Does the key match the crypto footer?
+  if (rc == 0 && memcmp(scrypted_intermediate_key,
+                        crypt_ftr->scrypted_intermediate_key,
+                        sizeof(scrypted_intermediate_key)) == 0) {
+    SLOGI("Password matches");
+    rc = 0;
+  } else {
+    /* Try mounting the file system anyway, just in case the problem's with
+     * the footer, not the key. */
+    snprintf(tmp_mount_point, sizeof(tmp_mount_point), "%s/tmp_mnt",
+             mount_point);
+    mkdir(tmp_mount_point, 0755);
+    if (try_mount_multiple_fs(crypto_blkdev, tmp_mount_point, file_system)) {
+      SLOGE("Error temp mounting decrypted block device\n");
+      delete_crypto_blk_dev(label);
+
+      rc = -1;
+    } else {
+      /* Success! */
+      SLOGI("Password did not match but decrypted drive mounted - continue");
+      umount(tmp_mount_point);
+      rc = 0;
+    }
+  }
+
+  if (rc == 0) {
+    /* Save the name of the crypto block device
+     * so we can mount it when restarting the framework. */
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+    /* Also save a the master key so we can reencrypted the key
+     * the key when we want to change the password on it. */
+    memcpy(saved_master_key, decrypted_master_key, crypt_ftr->keysize);
+    saved_mount_point = strdup(mount_point);
+    master_key_saved = 1;
+    SLOGD("%s(): Master key saved\n", __FUNCTION__);
+    rc = 0;
+  }
+
+ errout:
+  if (intermediate_key) {
+    memset(intermediate_key, 0, intermediate_key_size);
+    free(intermediate_key);
+  }
+  return rc;
+}
+
+/*
+ * 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.  We
+ * assume it must be using our same crypt type and keysize.
+ *
+ * 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) {
+        SLOGE("Failed to open %s: %s", real_blkdev, strerror(errno));
+        return -1;
+    }
+
+    unsigned long nr_sec = 0;
+    get_blkdev_size(fd, &nr_sec);
+    close(fd);
+
+    if (nr_sec == 0) {
+        SLOGE("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 = cryptfs_get_keysize();
+    strlcpy((char*) ext_crypt_ftr.crypto_type_name, cryptfs_get_crypto_name(),
+            MAX_CRYPTO_TYPE_NAME_LEN);
+    uint32_t flags = 0;
+    /*if (e4crypt_is_native() &&
+        android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false))
+        flags |= CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE;*/
+
+    return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev, out_crypto_blkdev, label, flags);
+}
+
+/*
+ * 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(label);
+}
+
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    property_get("ro.crypto.state", encrypted_state, "");
+    if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("encrypted fs already validated or not running with encryption,"
+              " aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key");
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+int cryptfs_check_passwd_hw(const char* passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
+    unsigned char master_key[KEY_LEN_BYTES];
+    /* get key */
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key");
+        return -1;
+    }
+
+    /*
+     * in case of manual encryption (from GUI), the encryption is done with
+     * default password
+     */
+    if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+        /* compare scrypted_intermediate_key with stored scrypted_intermediate_key
+         * which was created with actual password before reboot.
+         */
+        rc = cryptfs_get_master_key(&crypt_ftr, passwd, master_key);
+        if (rc) {
+            SLOGE("password doesn't match");
+            return rc;
+        }
+
+        rc = test_mount_hw_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+            DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+        if (rc) {
+            SLOGE("Default password did not match on reboot encryption");
+            return rc;
+        }
+    } else {
+        rc = test_mount_hw_encrypted_fs(&crypt_ftr, passwd,
+            DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+        SLOGE("test mount returned %i\n", rc);
+    }
+
+    return rc;
+}
+#endif
+
+int cryptfs_check_passwd(const char *passwd)
+{
+    /*if (e4crypt_is_native()) {
+        SLOGE("cryptfs_check_passwd not valid for file encryption");
+        return -1;
+    }*/
+
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
+
+    rc = check_unmounted_and_get_ftr(&crypt_ftr);
+    if (rc) {
+        SLOGE("Could not get footer");
+        return rc;
+    }
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    if (is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name))
+        return cryptfs_check_passwd_hw(passwd);
+#endif
+
+    rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+                                 DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+    if (rc) {
+        SLOGE("Password did not match");
+        return rc;
+    }
+
+    if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+        // Here we have a default actual password but a real password
+        // we must test against the scrypted value
+        // First, we must delete the crypto block device that
+        // test_mount_encrypted_fs leaves behind as a side effect
+        delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
+        rc = test_mount_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+                                     DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+        if (rc) {
+            SLOGE("Default password did not match on reboot encryption");
+            return rc;
+        }
+    }
+
+    return rc;
+}
+
+int cryptfs_verify_passwd(const char *passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    unsigned char decrypted_master_key[MAX_KEY_LEN];
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    int rc;
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("device not encrypted, aborting");
+        return -2;
+    }
+
+    if (!master_key_saved) {
+        SLOGE("encrypted fs not yet mounted, aborting");
+        return -1;
+    }
+
+    if (!saved_mount_point) {
+        SLOGE("encrypted fs failed to save mount point, aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) {
+        /* If the device has no password, then just say the password is valid */
+        rc = 0;
+    } else {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+        if(is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name)) {
+            if (verify_hw_fde_passwd(passwd, &crypt_ftr) >= 0)
+              rc = 0;
+            else
+              rc = -1;
+        } else {
+            decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+            if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+                /* They match, the password is correct */
+                rc = 0;
+            } else {
+              /* If incorrect, sleep for a bit to prevent dictionary attacks */
+                sleep(1);
+                rc = 1;
+            }
+        }
+#else
+        decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+        if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+            /* They match, the password is correct */
+            rc = 0;
+        } else {
+            /* If incorrect, sleep for a bit to prevent dictionary attacks */
+            sleep(1);
+            rc = 1;
+        }
+#endif
+    }
+
+    return rc;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+        return -1;
+    }
+
+    return crypt_ftr.crypt_type;
+}
+
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+                           unsigned char* master_key)
+{
+    int rc;
+
+    unsigned char* intermediate_key = 0;
+    size_t intermediate_key_size = 0;
+
+    if (password == 0 || *password == 0) {
+        password = DEFAULT_PASSWORD;
+    }
+
+    rc = decrypt_master_key(password, master_key, ftr, &intermediate_key,
+                            &intermediate_key_size);
+
+    if (rc) {
+        SLOGE("Can't calculate intermediate key");
+        return rc;
+    }
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    unsigned char scrypted_intermediate_key[sizeof(ftr->scrypted_intermediate_key)];
+
+    rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+                       ftr->salt, sizeof(ftr->salt), N, r, p,
+                       scrypted_intermediate_key,
+                       sizeof(scrypted_intermediate_key));
+
+    free(intermediate_key);
+
+    if (rc) {
+        SLOGE("Can't scrypt intermediate key");
+        return rc;
+    }
+
+    return memcmp(scrypted_intermediate_key, ftr->scrypted_intermediate_key,
+                  intermediate_key_size);
+}
+
diff --git a/crypto/fde/cryptfs.h b/crypto/fde/cryptfs.h
new file mode 100644
index 0000000..c67113a
--- /dev/null
+++ b/crypto/fde/cryptfs.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_CRYPTFS_H
+#define ANDROID_VOLD_CRYPTFS_H
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted, or in a separate partition.  It's location
+ * is specified by a property set in init.<device>.rc.
+ * The structure allocates 48 bytes for a key, but the real key size is
+ * specified in the struct.  Currently, the code is hardcoded to use 128
+ * bit keys.
+ * The fields after salt are only valid in rev 1.1 and later stuctures.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition if the crypt_mnt_ftr lives at the end of the
+ * partition.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <cutils/properties.h>
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48
+#define SALT_LEN 16
+#define SCRYPT_LEN 32
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Encryption partially completed,
+                                            encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE 0x4 /* Set when starting encryption, clear when
+                                        exit cleanly, either through success or
+                                        correctly marked partial encryption */
+#define CRYPT_DATA_CORRUPT 0x8 /* Set when encryption is fine, but the
+                                  underlying volume is corrupt */
+#define CRYPT_FORCE_ENCRYPTION 0x10 /* Set when it is time to encrypt this
+                                       volume on boot. Everything in this
+                                       structure is set up correctly as
+                                       though device is encrypted except
+                                       that the master key is encrypted with the
+                                       default password. */
+#define CRYPT_FORCE_COMPLETE 0x20 /* Set when the above encryption cycle is
+                                     complete. On next cryptkeeper entry, match
+                                     the password. If it matches fix the master
+                                     key and remove this flag. */
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+/* This flag is used to transition from L->M upgrade. L release passed
+ * a byte for every nible of user password while M release is passing
+ * ascii value of user password.
+ * Random flag value is chosen so that it does not conflict with other use cases
+ */
+#define CRYPT_ASCII_PASSWORD_UPDATED 0x1000
+#endif
+
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+                               * Must be zero to be compatible with pre-L
+                               * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT  1 /* master_key is encrypted with default
+                               * password */
+#define CRYPT_TYPE_PATTERN  2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN      3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+#define PERSIST_DATA_MAGIC 0xE950CD44
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
+#define KDF_SCRYPT_KEYMASTER_UNPADDED 3
+#define KDF_SCRYPT_KEYMASTER_BADLY_PADDED 4
+#define KDF_SCRYPT_KEYMASTER 5
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
+
+/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
+#define __le8  unsigned char
+
+#if !defined(SHA256_DIGEST_LENGTH)
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+struct crypt_mnt_ftr {
+  __le32 magic;         /* See above */
+  __le16 major_version;
+  __le16 minor_version;
+  __le32 ftr_size;      /* in bytes, not including key following */
+  __le32 flags;         /* See above */
+  __le32 keysize;       /* in bytes */
+  __le32 crypt_type;    /* how master_key is encrypted. Must be a
+                         * CRYPT_TYPE_XXX value */
+  __le64 fs_size;       /* Size of the encrypted fs, in 512 byte sectors */
+  __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+                                  mount, set to 0 on successful mount */
+  unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+                                                               needed to decrypt this
+                                                               partition, null terminated */
+  __le32 spare2;        /* ignored */
+  unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
+  unsigned char salt[SALT_LEN];   /* The salt used for this encryption */
+  __le64 persist_data_offset[2];  /* Absolute offset to both copies of crypt_persist_data
+                                   * on device with that info, either the footer of the
+                                   * real_blkdevice or the metadata partition. */
+
+  __le32 persist_data_size;       /* The number of bytes allocated to each copy of the
+                                   * persistent data table*/
+
+  __le8  kdf_type; /* The key derivation function used. */
+
+  /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+  __le8  N_factor; /* (1 << N) */
+  __le8  r_factor; /* (1 << r) */
+  __le8  p_factor; /* (1 << p) */
+  __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+                            we have to stop (e.g. power low) this is the last
+                            encrypted 512 byte sector.*/
+  __le8  hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+                                                    set, hash of first block, used
+                                                    to validate before continuing*/
+
+  /* key_master key, used to sign the derived key which is then used to generate
+   * the intermediate key
+   * This key should be used for no other purposes! We use this key to sign unpadded 
+   * data, which is acceptable but only if the key is not reused elsewhere. */
+  __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+  __le32 keymaster_blob_size;
+
+  /* Store scrypt of salted intermediate key. When decryption fails, we can
+     check if this matches, and if it does, we know that the problem is with the
+     drive, and there is no point in asking the user for more passwords.
+
+     Note that if any part of this structure is corrupt, this will not match and
+     we will continue to believe the user entered the wrong password. In that
+     case the only solution is for the user to enter a password enough times to
+     force a wipe.
+
+     Note also that there is no need to worry about migration. If this data is
+     wrong, we simply won't recognise a right password, and will continue to
+     prompt. On the first password change, this value will be populated and
+     then we will be OK.
+   */
+  unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+
+  /* sha of this structure with this element set to zero
+     Used when encrypting on reboot to validate structure before doing something
+     fatal
+   */
+  unsigned char sha256[SHA256_DIGEST_LENGTH];
+};
+
+#define DATA_MNT_POINT "/data"
+
+/* Return values for cryptfs_crypto_complete */
+#define CRYPTO_COMPLETE_NOT_ENCRYPTED  1
+#define CRYPTO_COMPLETE_ENCRYPTED      0
+#define CRYPTO_COMPLETE_BAD_METADATA  (-1)
+#define CRYPTO_COMPLETE_PARTIAL       (-2)
+#define CRYPTO_COMPLETE_INCONSISTENT  (-3)
+#define CRYPTO_COMPLETE_CORRUPT       (-4)
+
+/* Return values for cryptfs_enable_inplace*() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER (-1)
+#define ENABLE_INPLACE_ERR_DEV (-2)  /* crypto_blkdev issue */
+
+typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
+                        void* params);
+
+int cryptfs_check_passwd(const char* pw);
+int cryptfs_verify_passwd(const char* pw);
+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);
+int cryptfs_get_password_type(void);
+
+uint32_t cryptfs_get_keysize();
+const char* cryptfs_get_crypto_name();
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs);
+int cryptfs_check_footer();
+int delete_crypto_blk_dev(const char *name);
+
+#endif /* ANDROID_VOLD_CRYPTFS_H */
diff --git a/crypto/fde/main.cpp b/crypto/fde/main.cpp
new file mode 100644
index 0000000..7051a6d
--- /dev/null
+++ b/crypto/fde/main.cpp
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+void usage() {
+	printf("  Usage:\n");
+	printf("    twrpdec /path/to/userdata /path/to/metadata filesystem password\n");
+	printf("\n");
+	printf("  The metadata path is the path to the footer. If no metadata\n");
+	printf("  partition is present then use footer for this argument.\n");
+	printf("\n");
+	printf("  Example:\n");
+	printf("    twrpdec /dev/block/bootdevice/by-name/userdata footer ext4 0000\n");
+}
+
+int main(int argc, char **argv) {
+	if (argc != 5) {
+		usage();
+		return -1;
+	}
+	set_partition_data(argv[1], argv[2], argv[3]);
+	//int ret = cryptfs_check_passwd("30303030");
+	int ret = cryptfs_check_passwd(argv[4]);
+	return 0;
+}
diff --git a/crypto/fscrypt/Android.mk b/crypto/fscrypt/Android.mk
new file mode 100755
index 0000000..d9fb6ef
--- /dev/null
+++ b/crypto/fscrypt/Android.mk
@@ -0,0 +1,87 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtwrpfscrypt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \
+    -DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function -fpic
+LOCAL_SRC_FILES := FsCrypt.cpp Decrypt.cpp ScryptParameters.cpp fscrypt_policy.cpp Utils.cpp HashPassword.cpp \
+    KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
+    Process.cpp EncryptInplace.cpp Weaver1.cpp cryptfs.cpp Checkpoint.cpp CryptoType.cpp VoldUtil.cpp
+LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \
+libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \
+android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \
+libfs_mgr android.hardware.keymaster@4.0 android.hardware.keymaster@4.1 libkeymaster4support libkeymaster4_1support \
+libf2fs_sparseblock libkeystore_parcelables libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder \
+libchrome android.hardware.boot@1.0 libbootloader_message
+LOCAL_STATIC_LIBRARIES := libscrypt_static libvold_binder libc++fs
+LOCAL_C_INCLUDES := system/extras/ext4_utils \
+    system/extras/ext4_utils/include/ext4_utils \
+    external/scrypt/lib/crypto \
+    system/security/keystore/include \
+    hardware/libhardware/include/hardware \
+    system/security/softkeymaster/include/keymaster \
+    system/keymaster/include \
+    system/extras/libfscrypt/include \
+    system/core/fs_mgr/libfs_avb/include/ \
+    system/core/fs_mgr/include_fstab/ \
+    system/core/fs_mgr/include/ \
+    system/core/fs_mgr/libdm/include/ \
+    system/core/fs_mgr/liblp/include/ \
+    system/gsid/include/ \
+    system/core/init/ \
+    system/vold/model \
+    system/vold/ \
+    system/extras/f2fs_utils/ \
+    bootable/recovery/bootloader_message/include
+
+ifeq  ($(TW_USE_FSCRYPT_POLICY), 1)
+    LOCAL_CFLAGS += -DUSE_FSCRYPT_POLICY_V1
+else
+    LOCAL_CFLAGS += -DUSE_FSCRYPT_POLICY_V2
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+    LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+endif
+
+LOCAL_REQUIRED_MODULES := keystore_auth keystore
+LOCAL_CLANG := true
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fscryptpolicyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_C_INCLUDES += system/extras/libfscrypt/include
+LOCAL_SRC_FILES := fscryptpolicyget.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpfbe
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keystore_auth
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_SRC_FILES := keystore_auth.cpp
+LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
+LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
+LOCAL_SHARED_LIBRARIES += libkeystore_aidl
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fscrypt/Checkpoint.cpp b/crypto/fscrypt/Checkpoint.cpp
new file mode 100644
index 0000000..fac0d9f
--- /dev/null
+++ b/crypto/fscrypt/Checkpoint.cpp
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Checkpoint"
+#include "Checkpoint.h"
+#include "VoldUtil.h"
+#include "VolumeManager.h"
+
+#include <fstream>
+#include <list>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <cutils/android_reboot.h>
+#include <fcntl.h>
+#include <fs_mgr.h>
+#include <linux/fs.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
+using android::base::SetProperty;
+using android::binder::Status;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::ReadDefaultFstab;
+using android::fs_mgr::ReadFstabFromFile;
+using android::hardware::hidl_string;
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::IBootControl;
+using android::hardware::boot::V1_0::Slot;
+
+
+namespace {
+const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
+
+android::binder::Status error(const std::string& msg) {
+    PLOG(ERROR) << msg;
+    return android::binder::Status::fromServiceSpecificError(errno, android::String8(msg.c_str()));
+}
+
+android::binder::Status error(int error, const std::string& msg) {
+    LOG(ERROR) << msg;
+    return android::binder::Status::fromServiceSpecificError(error, android::String8(msg.c_str()));
+}
+
+bool setBowState(std::string const& block_device, std::string const& state) {
+    std::string bow_device = fs_mgr_find_bow_device(block_device);
+    if (bow_device.empty()) return false;
+
+    if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
+        PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace
+
+Status cp_supportsCheckpoint(bool& result) {
+    result = false;
+
+    for (const auto& entry : fstab_default) {
+        if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
+            result = true;
+            return Status::ok();
+        }
+    }
+    return Status::ok();
+}
+
+Status cp_supportsBlockCheckpoint(bool& result) {
+    result = false;
+
+    for (const auto& entry : fstab_default) {
+        if (entry.fs_mgr_flags.checkpoint_blk) {
+            result = true;
+            return Status::ok();
+        }
+    }
+    return Status::ok();
+}
+
+Status cp_supportsFileCheckpoint(bool& result) {
+    result = false;
+
+    for (const auto& entry : fstab_default) {
+        if (entry.fs_mgr_flags.checkpoint_fs) {
+            result = true;
+            return Status::ok();
+        }
+    }
+    return Status::ok();
+}
+
+Status cp_startCheckpoint(int retry) {
+    bool result;
+    if (!cp_supportsCheckpoint(result).isOk() || !result)
+        return error(ENOTSUP, "Checkpoints not supported");
+
+    if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
+    std::string content = std::to_string(retry + 1);
+    if (retry == -1) {
+        android::sp<IBootControl> module = IBootControl::getService();
+        if (module) {
+            std::string suffix;
+            auto cb = [&suffix](hidl_string s) { suffix = s; };
+            if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
+        }
+    }
+    if (!android::base::WriteStringToFile(content, kMetadataCPFile))
+        return error("Failed to write checkpoint file");
+    return Status::ok();
+}
+
+namespace {
+
+volatile bool isCheckpointing = false;
+
+volatile bool needsCheckpointWasCalled = false;
+
+// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
+// of isCheckpointing
+std::mutex isCheckpointingLock;
+}
+
+Status cp_commitChanges() {
+    std::lock_guard<std::mutex> lock(isCheckpointingLock);
+
+    if (!isCheckpointing) {
+        return Status::ok();
+    }
+    if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
+        LOG(WARNING)
+            << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
+        return Status::ok();
+    }
+    // TWRP should not mark errors on slots
+    // android::sp<IBootControl> module = IBootControl::getService();
+    // if (module) {
+    //     CommandResult cr;
+    //     module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
+    //     if (!cr.success)
+    //         return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
+    //     LOG(INFO) << "Marked slot as booted successfully.";
+    //     // Clears the warm reset flag for next reboot.
+    //     if (!SetProperty("ota.warm_reset", "0")) {
+    //         LOG(WARNING) << "Failed to reset the warm reset flag";
+    //     }
+    // }
+    // Must take action for list of mounted checkpointed things here
+    // To do this, we walk the list of mounted file systems.
+    // But we also need to get the matching fstab entries to see
+    // the original flags
+    std::string err_str;
+
+    Fstab mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+        return error(EINVAL, "Failed to get /proc/mounts");
+    }
+
+    // Walk mounted file systems
+    for (const auto& mount_rec : mounts) {
+        const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
+        if (!fstab_rec) continue;
+
+        if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
+            if (fstab_rec->fs_type == "f2fs") {
+                std::string options = mount_rec.fs_options + ",checkpoint=enable";
+                if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
+                          MS_REMOUNT | fstab_rec->flags, options.c_str())) {
+                    return error(EINVAL, "Failed to remount");
+                }
+            }
+        } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
+            if (!setBowState(mount_rec.blk_device, "2"))
+                return error(EINVAL, "Failed to set bow state");
+        }
+    }
+    SetProperty("vold.checkpoint_committed", "1");
+    LOG(INFO) << "Checkpoint has been committed.";
+    isCheckpointing = false;
+    if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
+        return error(err_str.c_str());
+
+    return Status::ok();
+}
+
+namespace {
+void abort_metadata_file() {
+    std::string oldContent, newContent;
+    int retry = 0;
+    struct stat st;
+    int result = stat(kMetadataCPFile.c_str(), &st);
+
+    // If the file doesn't exist, we aren't managing a checkpoint retry counter
+    if (result != 0) return;
+    if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
+        PLOG(ERROR) << "Failed to read checkpoint file";
+        return;
+    }
+    std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
+
+    if (!android::base::ParseInt(retryContent, &retry)) {
+        PLOG(ERROR) << "Could not parse retry count";
+        return;
+    }
+    if (retry > 0) {
+        newContent = "0";
+        if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
+            PLOG(ERROR) << "Could not write checkpoint file";
+    }
+}
+}  // namespace
+
+void cp_abortChanges(const std::string& message, bool retry) {
+    if (!cp_needsCheckpoint()) return;
+    if (!retry) abort_metadata_file();
+    android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
+}
+
+bool cp_needsRollback() {
+    std::string content;
+    bool ret;
+
+    ret = android::base::ReadFileToString(kMetadataCPFile, &content);
+    if (ret) {
+        if (content == "0") return true;
+        if (content.substr(0, 3) == "-1 ") {
+            std::string oldSuffix = content.substr(3);
+            android::sp<IBootControl> module = IBootControl::getService();
+            std::string newSuffix;
+
+            if (module) {
+                auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
+                module->getSuffix(module->getCurrentSlot(), cb);
+                if (oldSuffix == newSuffix) return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool cp_needsCheckpoint() {
+    std::lock_guard<std::mutex> lock(isCheckpointingLock);
+
+    // Make sure we only return true during boot. See b/138952436 for discussion
+    if (needsCheckpointWasCalled) return isCheckpointing;
+    needsCheckpointWasCalled = true;
+
+    bool ret;
+    std::string content;
+    android::sp<IBootControl> module = IBootControl::getService();
+
+    if (isCheckpointing) return isCheckpointing;
+
+    if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
+        isCheckpointing = true;
+        return true;
+    }
+    ret = android::base::ReadFileToString(kMetadataCPFile, &content);
+    if (ret) {
+        ret = content != "0";
+        isCheckpointing = ret;
+        return ret;
+    }
+    return false;
+}
+
+bool cp_isCheckpointing() {
+    return isCheckpointing;
+}
+
+namespace {
+const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
+const uint32_t msleeptime_default = 1000;  // 1 s
+const uint32_t max_msleeptime = 3600000;   // 1 h
+
+const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
+const uint64_t min_free_bytes_default = 100 * (1 << 20);  // 100 MiB
+
+const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
+const bool commit_on_full_default = true;
+
+static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
+    struct statvfs data;
+    uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
+    uint64_t min_free_bytes =
+        GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
+    bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
+
+    struct timespec req;
+    req.tv_sec = msleeptime / 1000;
+    msleeptime %= 1000;
+    req.tv_nsec = msleeptime * 1000000;
+    while (isCheckpointing) {
+        uint64_t free_bytes = 0;
+        if (is_fs_cp) {
+            statvfs(mnt_pnt.c_str(), &data);
+            free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
+        } else {
+            std::string bow_device = fs_mgr_find_bow_device(blk_device);
+            if (!bow_device.empty()) {
+                std::string content;
+                if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
+                    free_bytes = std::strtoull(content.c_str(), NULL, 10);
+                }
+            }
+        }
+        if (free_bytes < min_free_bytes) {
+            if (commit_on_full) {
+                LOG(INFO) << "Low space for checkpointing. Commiting changes";
+                cp_commitChanges();
+                break;
+            } else {
+                LOG(INFO) << "Low space for checkpointing. Rebooting";
+                cp_abortChanges("checkpoint,low_space", false);
+                break;
+            }
+        }
+        nanosleep(&req, NULL);
+    }
+}
+
+}  // namespace
+
+Status cp_prepareCheckpoint() {
+    // Log to notify CTS - see b/137924328 for context
+    LOG(INFO) << "cp_prepareCheckpoint called";
+    std::lock_guard<std::mutex> lock(isCheckpointingLock);
+    if (!isCheckpointing) {
+        return Status::ok();
+    }
+
+    Fstab mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+        return error(EINVAL, "Failed to get /proc/mounts");
+    }
+
+    for (const auto& mount_rec : mounts) {
+        const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
+        if (!fstab_rec) continue;
+
+        if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
+            android::base::unique_fd fd(
+                TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
+            if (fd == -1) {
+                PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
+                continue;
+            }
+
+            struct fstrim_range range = {};
+            range.len = ULLONG_MAX;
+            nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
+            if (ioctl(fd, FITRIM, &range)) {
+                PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
+                continue;
+            }
+            nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
+            LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
+                      << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
+
+            setBowState(mount_rec.blk_device, "1");
+        }
+        if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
+            std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
+                        std::string(mount_rec.blk_device),
+                        fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
+                .detach();
+        }
+    }
+    return Status::ok();
+}
+
+namespace {
+const int kSectorSize = 512;
+
+typedef uint64_t sector_t;
+
+struct log_entry {
+    sector_t source;  // in sectors of size kSectorSize
+    sector_t dest;    // in sectors of size kSectorSize
+    uint32_t size;    // in bytes
+    uint32_t checksum;
+} __attribute__((packed));
+
+struct log_sector_v1_0 {
+    uint32_t magic;
+    uint16_t header_version;
+    uint16_t header_size;
+    uint32_t block_size;
+    uint32_t count;
+    uint32_t sequence;
+    uint64_t sector0;
+} __attribute__((packed));
+
+// MAGIC is BOW in ascii
+const int kMagic = 0x00574f42;
+// Partially restored MAGIC is WOB in ascii
+const int kPartialRestoreMagic = 0x00424f57;
+
+void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
+    static uint32_t table[0x100] = {
+        0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
+        0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
+        0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
+        0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+        0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
+        0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+        0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
+        0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+        0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
+        0xB6662D3D,
+
+        0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
+        0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+        0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
+        0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+        0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
+        0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
+        0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
+        0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+        0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
+        0xC0BA6CAD,
+
+        0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
+        0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
+        0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
+        0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+        0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
+        0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
+        0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
+        0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+        0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
+        0x5BDEAE1D,
+
+        0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
+        0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
+        0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
+        0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+        0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
+        0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+        0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
+        0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+        0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
+        0x2D02EF8D};
+
+    for (size_t i = 0; i < n_bytes; ++i) {
+        *crc ^= ((uint8_t*)data)[i];
+        *crc = table[(uint8_t)*crc] ^ *crc >> 8;
+    }
+}
+
+// A map of relocations.
+// The map must be initialized so that relocations[0] = 0
+// During restore, we replay the log records in reverse, copying from dest to
+// source
+// To validate, we must be able to read the 'dest' sectors as though they had
+// been copied but without actually copying. This map represents how the sectors
+// would have been moved. To read a sector s, find the index <= s and read
+// relocations[index] + s - index
+typedef std::map<sector_t, sector_t> Relocations;
+
+void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
+    // Find first one we're equal to or greater than
+    auto s = --relocations.upper_bound(source);
+
+    // Take slice
+    Relocations slice;
+    slice[dest] = source - s->first + s->second;
+    ++s;
+
+    // Add rest of elements
+    for (; s != relocations.end() && s->first < source + count; ++s)
+        slice[dest - source + s->first] = s->second;
+
+    // Split range at end of dest
+    auto dest_end = --relocations.upper_bound(dest + count);
+    relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
+
+    // Remove all elements in [dest, dest + count)
+    relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
+
+    // Add new elements
+    relocations.insert(slice.begin(), slice.end());
+}
+
+// A map of sectors that have been written to.
+// The final entry must always be False.
+// When we restart the restore after an interruption, we must take care that
+// when we copy from dest to source, that the block we copy to was not
+// previously copied from.
+// i e. A->B C->A; If we replay this sequence, we end up copying C->B
+// We must save our partial result whenever we finish a page, or when we copy
+// to a location that was copied from earlier (our source is an earlier dest)
+typedef std::map<sector_t, bool> Used_Sectors;
+
+bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+    auto second_overlap = used_sectors.upper_bound(start);
+    auto first_overlap = --second_overlap;
+
+    if (first_overlap->second) {
+        return true;
+    } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
+        return true;
+    }
+    return false;
+}
+
+void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+    auto start_pos = used_sectors.insert_or_assign(start, true).first;
+    auto end_pos = used_sectors.insert_or_assign(end, false).first;
+
+    if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
+        start_pos++;
+    }
+    if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
+        end_pos++;
+    }
+    if (start_pos->first < end_pos->first) {
+        used_sectors.erase(start_pos, end_pos);
+    }
+}
+
+// Restores the given log_entry's data from dest -> source
+// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
+void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
+                   log_entry* le, std::vector<char>& buffer) {
+    log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+    uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
+    int count = (le->size - 1) / kSectorSize + 1;
+
+    if (checkCollision(used_sectors, le->source, le->source + count)) {
+        fsync(device_fd);
+        lseek64(device_fd, 0, SEEK_SET);
+        ls.count = index + 1;
+        ls.magic = kPartialRestoreMagic;
+        write(device_fd, &ls_buffer[0], ls.block_size);
+        fsync(device_fd);
+        used_sectors.clear();
+        used_sectors[0] = false;
+    }
+
+    markUsed(used_sectors, le->dest, le->dest + count);
+
+    if (index == 0 && ls.sequence != 0) {
+        log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
+        if (next->magic == kMagic) {
+            next->magic = kPartialRestoreMagic;
+        }
+    }
+
+    lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
+    write(device_fd, &buffer[0], le->size);
+
+    if (index == 0) {
+        fsync(device_fd);
+    }
+}
+
+// Read from the device
+// If we are validating, the read occurs as though the relocations had happened
+std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
+                                sector_t sector, uint32_t size, uint32_t block_size) {
+    if (!validating) {
+        std::vector<char> buffer(size);
+        lseek64(device_fd, sector * kSectorSize, SEEK_SET);
+        read(device_fd, &buffer[0], size);
+        return buffer;
+    }
+
+    std::vector<char> buffer(size);
+    for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
+        auto relocation = --relocations.upper_bound(sector);
+        lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
+                SEEK_SET);
+        read(device_fd, &buffer[i], block_size);
+    }
+
+    return buffer;
+}
+
+}  // namespace
+
+Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
+    bool validating = true;
+    std::string action = "Validating";
+    int restore_count = 0;
+
+    for (;;) {
+        Relocations relocations;
+        relocations[0] = 0;
+        Status status = Status::ok();
+
+        LOG(INFO) << action << " checkpoint on " << blockDevice;
+        android::base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
+        if (device_fd < 0) return error("Cannot open " + blockDevice);
+
+        log_sector_v1_0 original_ls;
+        read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
+        if (original_ls.magic == kPartialRestoreMagic) {
+            validating = false;
+            action = "Restoring";
+        } else if (original_ls.magic != kMagic) {
+            return error(EINVAL, "No magic");
+        }
+
+        LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
+
+        for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
+            auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
+                                           original_ls.block_size, original_ls.block_size);
+            log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+
+            Used_Sectors used_sectors;
+            used_sectors[0] = false;
+
+            if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
+                status = error(EINVAL, "No magic");
+                break;
+            }
+
+            if (ls.block_size != original_ls.block_size) {
+                status = error(EINVAL, "Block size mismatch");
+                break;
+            }
+
+            if ((int)ls.sequence != sequence) {
+                status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
+                                           " but got " + std::to_string(ls.sequence));
+                break;
+            }
+
+            LOG(INFO) << action << " from log sector " << ls.sequence;
+            for (log_entry* le =
+                     reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
+                 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
+                // This is very noisy - limit to DEBUG only
+                LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
+                             << " to " << le->source << " with checksum " << std::hex
+                             << le->checksum;
+
+                auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
+                                            ls.block_size);
+                uint32_t checksum = le->source / (ls.block_size / kSectorSize);
+                for (size_t i = 0; i < le->size; i += ls.block_size) {
+                    crc32(&buffer[i], ls.block_size, &checksum);
+                }
+
+                if (le->checksum && checksum != le->checksum) {
+                    status = error(EINVAL, "Checksums don't match");
+                    break;
+                }
+
+                if (validating) {
+                    relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
+                } else {
+                    restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
+                    restore_count++;
+                    if (restore_limit && restore_count >= restore_limit) {
+                        status = error(EAGAIN, "Hit the test limit");
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!status.isOk()) {
+            if (!validating) {
+                LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
+                return status;
+            }
+
+            LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
+            auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
+                                        original_ls.block_size, original_ls.block_size);
+            lseek64(device_fd, 0, SEEK_SET);
+            write(device_fd, &buffer[0], original_ls.block_size);
+            return Status::ok();
+        }
+
+        if (!validating) break;
+
+        validating = false;
+        action = "Restoring";
+    }
+
+    return Status::ok();
+}
+
+Status cp_markBootAttempt() {
+    std::string oldContent, newContent;
+    int retry = 0;
+    struct stat st;
+    int result = stat(kMetadataCPFile.c_str(), &st);
+
+    // If the file doesn't exist, we aren't managing a checkpoint retry counter
+    if (result != 0) return Status::ok();
+    if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
+        return error("Failed to read checkpoint file");
+    std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
+
+    if (!android::base::ParseInt(retryContent, &retry))
+        return error(EINVAL, "Could not parse retry count");
+    if (retry > 0) {
+        retry--;
+
+        newContent = std::to_string(retry);
+        if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
+            return error("Could not write checkpoint file");
+    }
+    return Status::ok();
+}
+
+void cp_resetCheckpoint() {
+    std::lock_guard<std::mutex> lock(isCheckpointingLock);
+    needsCheckpointWasCalled = false;
+}
diff --git a/crypto/fscrypt/Checkpoint.h b/crypto/fscrypt/Checkpoint.h
new file mode 100644
index 0000000..cd17c78
--- /dev/null
+++ b/crypto/fscrypt/Checkpoint.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CHECKPOINT_H
+#define _CHECKPOINT_H
+
+#include <binder/Status.h>
+#include <string>
+
+
+android::binder::Status cp_supportsCheckpoint(bool& result);
+
+android::binder::Status cp_supportsBlockCheckpoint(bool& result);
+
+android::binder::Status cp_supportsFileCheckpoint(bool& result);
+
+android::binder::Status cp_startCheckpoint(int retry);
+
+android::binder::Status cp_commitChanges();
+
+void cp_abortChanges(const std::string& message, bool retry);
+
+bool cp_needsRollback();
+
+bool cp_needsCheckpoint();
+
+bool cp_isCheckpointing();
+
+android::binder::Status cp_prepareCheckpoint();
+
+android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0);
+
+android::binder::Status cp_markBootAttempt();
+
+void cp_resetCheckpoint();
+
+#endif
diff --git a/crypto/fscrypt/CryptoType.cpp b/crypto/fscrypt/CryptoType.cpp
new file mode 100644
index 0000000..cf1ef42
--- /dev/null
+++ b/crypto/fscrypt/CryptoType.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CryptoType.h"
+
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+                                          const CryptoType& default_alg, const char* property) {
+    char paramstr[PROPERTY_VALUE_MAX];
+
+    property_get(property, paramstr, default_alg.get_config_name());
+    for (int i = 0; i < table_len; i++) {
+        if (strcmp(paramstr, table[i].get_config_name()) == 0) {
+            return table[i];
+        }
+    }
+    LOG(ERROR) << "Invalid name (" << paramstr << ") for " << property << ".  Defaulting to "
+               << default_alg.get_config_name() << ".";
+    return default_alg;
+}
diff --git a/crypto/fscrypt/CryptoType.h b/crypto/fscrypt/CryptoType.h
new file mode 100644
index 0000000..79ea93d
--- /dev/null
+++ b/crypto/fscrypt/CryptoType.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+
+// Struct representing an encryption algorithm supported by vold.
+// "config_name" represents the name we give the algorithm in
+// read-only properties and fstab files
+// "kernel_name" is the name we present to the Linux kernel
+// "keysize" is the size of the key in bytes.
+struct CryptoType {
+    // We should only be constructing CryptoTypes as part of
+    // supported_crypto_types[].  We do it via this pseudo-builder pattern,
+    // which isn't pure or fully protected as a concession to being able to
+    // do it all at compile time.  Add new CryptoTypes in
+    // supported_crypto_types[] below.
+    constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
+    constexpr CryptoType set_keysize(size_t size) const {
+        return CryptoType(this->config_name, this->kernel_name, size);
+    }
+    constexpr CryptoType set_config_name(const char* property) const {
+        return CryptoType(property, this->kernel_name, this->keysize);
+    }
+    constexpr CryptoType set_kernel_name(const char* crypto) const {
+        return CryptoType(this->config_name, crypto, this->keysize);
+    }
+
+    constexpr const char* get_config_name() const { return config_name; }
+    constexpr const char* get_kernel_name() const { return kernel_name; }
+    constexpr size_t get_keysize() const { return keysize; }
+
+  private:
+    const char* config_name;
+    const char* kernel_name;
+    size_t keysize;
+
+    constexpr CryptoType(const char* property, const char* crypto, size_t ksize)
+        : config_name(property), kernel_name(crypto), keysize(ksize) {}
+};
+
+// Use the named android property to look up a type from the table
+// If the property is not set or matches no table entry, return the default.
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+                                          const CryptoType& default_alg, const char* property);
+
+// Some useful types
+
+constexpr CryptoType invalid_crypto_type = CryptoType();
+
+constexpr CryptoType aes_256_xts = CryptoType()
+                                           .set_config_name("aes-256-xts")
+                                           .set_kernel_name("aes-xts-plain64")
+                                           .set_keysize(64);
+
+constexpr CryptoType adiantum = CryptoType()
+                                        .set_config_name("adiantum")
+                                        .set_kernel_name("xchacha12,aes-adiantum-plain64")
+                                        .set_keysize(32);
+
+// Support compile-time validation of a crypto type table
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) {
+    return N;
+}
+
+constexpr bool isValidCryptoType(size_t max_keylen, const CryptoType& crypto_type) {
+    return ((crypto_type.get_config_name() != nullptr) &&
+            (crypto_type.get_kernel_name() != nullptr) &&
+            (crypto_type.get_keysize() <= max_keylen));
+}
+
+// Confirms that all supported_crypto_types have a small enough keysize and
+// had both set_config_name() and set_kernel_name() called.
+// Note in C++11 that constexpr functions can only have a single line.
+// So our code is a bit convoluted (using recursion instead of a loop),
+// but it's asserting at compile time that all of our key lengths are valid.
+constexpr bool validateSupportedCryptoTypes(size_t max_keylen, const CryptoType types[],
+                                            size_t len) {
+    return len == 0 || (isValidCryptoType(max_keylen, types[len - 1]) &&
+                        validateSupportedCryptoTypes(max_keylen, types, len - 1));
+}
diff --git a/crypto/fscrypt/Decrypt.cpp b/crypto/fscrypt/Decrypt.cpp
new file mode 100755
index 0000000..0217358
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.cpp
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (C) 2016 - 2020 The TeamWin Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Decrypt.h"
+#include "FsCrypt.h"
+#include <fscrypt/fscrypt.h>
+
+#include <map>
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+#include "keystore_client.pb.h"
+#include "Weaver1.h"
+#include "cutils/properties.h"
+
+#include <openssl/sha.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fstream>
+#include <future>
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <base/threading/platform_thread.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/security/BnConfirmationPromptCallback.h>
+#include <android/security/keystore/IKeystoreService.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <hardware/hw_auth_token.h>
+
+#include <keystore/keystore.h>
+#include <keystore/keystore_client.h>
+#include <keystore/keystore_client_impl.h>
+#include <keystore/KeystoreResponse.h>
+#include <keystore/keystore_hidl_support.h>
+#include <keystore/keystore_promises.h>
+#include <keystore/keystore_return_types.h>
+#include <keystore/keymaster_types.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keystore/OperationResult.h>
+#include "keystore_client.pb.h"
+
+#include <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+extern "C" {
+#include "crypto_scrypt.h"
+}
+
+#include "fscrypt_policy.h"
+#include "fscrypt-common.h"
+#include "HashPassword.h"
+#include "KeyStorage.h"
+#include "android/os/IVold.h"
+
+using android::security::keystore::IKeystoreService;
+using keystore::KeystoreResponsePromise;
+using keystore::OperationResultPromise;
+using android::security::keymaster::OperationResult;
+using android::hardware::keymaster::V4_1::support::blob2hidlVec;
+
+
+inline std::string hidlVec2String(const ::keystore::hidl_vec<uint8_t>& value) {
+    return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
+}
+
+static bool lookup_ref_key_internal(std::map<userid_t, android::fscrypt::EncryptionPolicy> key_map, const uint8_t* policy, userid_t* user_id) {
+#ifdef USE_FSCRYPT_POLICY_V1
+	char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+	char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+	bytes_to_hex(policy, FS_KEY_DESCRIPTOR_SIZE, policy_string_hex);
+#else
+	char policy_string_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+	char key_map_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+	bytes_to_hex(policy, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_string_hex);
+#endif
+
+    for (std::map<userid_t, android::fscrypt::EncryptionPolicy>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+#ifdef USE_FSCRYPT_POLICY_V1
+		bytes_to_hex(reinterpret_cast<const uint8_t*>(&it->second.key_raw_ref[0]), FS_KEY_DESCRIPTOR_SIZE, key_map_hex);
+#else
+		bytes_to_hex(reinterpret_cast<const uint8_t*>(&it->second.key_raw_ref[0]), FSCRYPT_KEY_IDENTIFIER_SIZE, key_map_hex);
+#endif
+		std::string key_map_hex_string = std::string(key_map_hex);
+		if (key_map_hex_string == policy_string_hex) {
+            *user_id = it->first;
+            return true;
+        }
+    }
+    return false;
+}
+
+#ifdef USE_FSCRYPT_POLICY_V1
+extern "C" bool lookup_ref_key(fscrypt_policy_v1* fep, uint8_t* policy_type) {
+#else
+extern "C" bool lookup_ref_key(fscrypt_policy_v2* fep, uint8_t* policy_type) {
+#endif
+	userid_t user_id = 0;
+	std::string policy_type_string;
+
+#ifdef USE_FSCRYPT_POLICY_V1
+	char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+	bytes_to_hex(fep->master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE, policy_hex);
+	if (std::strncmp((const char*)fep->master_key_descriptor, de_key_raw_ref.c_str(), FS_KEY_DESCRIPTOR_SIZE) == 0) {
+		policy_type_string = SYSTEM_DE_FSCRYPT_POLICY;
+		memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+		return true;
+	}
+    if (!lookup_ref_key_internal(s_de_policies, fep->master_key_descriptor, &user_id)) {
+        if (!lookup_ref_key_internal(s_ce_policies, fep->master_key_descriptor, &user_id)) {
+            return false;
+		} else {
+			policy_type_string = USER_CE_FSCRYPT_POLICY + std::to_string(user_id);
+		}
+    } else {
+			policy_type_string = USER_DE_FSCRYPT_POLICY + std::to_string(user_id);
+	}
+#else
+	char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+	bytes_to_hex(fep->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
+	if (std::strncmp((const char*)fep->master_key_identifier, de_key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE) == 0) {
+		policy_type_string = SYSTEM_DE_FSCRYPT_POLICY;
+		memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+		return true;
+	}
+    if (!lookup_ref_key_internal(s_de_policies, fep->master_key_identifier, &user_id)) {
+        if (!lookup_ref_key_internal(s_ce_policies, fep->master_key_identifier, &user_id)) {
+            return false;
+		} else {
+			policy_type_string = USER_CE_FSCRYPT_POLICY + std::to_string(user_id);
+		}
+    } else {
+			policy_type_string = USER_DE_FSCRYPT_POLICY + std::to_string(user_id);
+	}
+#endif
+
+	memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+	LOG(INFO) << "storing policy type: " << policy_type;
+    return true;
+}
+
+extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) {
+	std::string policy_type_string = std::string((char *) policy_type);
+#ifdef USE_FSCRYPT_POLICY_V1
+	char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+	bytes_to_hex(policy_type, FS_KEY_DESCRIPTOR_SIZE, policy_hex);
+#else
+	char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+	bytes_to_hex(policy_type, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
+#endif
+
+	userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
+
+	// TODO Update version # and make magic strings
+#ifdef USE_FSCRYPT_POLICY_V1
+	if (policy_type_string.substr(0,1) != FSCRYPT_V1) {
+#else
+	if (policy_type_string.substr(0,1) != FSCRYPT_V2) {
+#endif
+        LOG(ERROR) << "Unexpected version:" << policy_type[0];
+        return false;
+    }
+
+	if (policy_type_string.substr(1, 2) == SYSTEM_DE_KEY) {
+        memcpy(policy, de_key_raw_ref.data(), de_key_raw_ref.size());
+        return true;
+    }
+
+    std::string raw_ref;
+
+	if (policy_type_string.substr(1, 1) == USER_DE_KEY) {
+        if (lookup_key_ref(s_de_policies, user_id, &raw_ref)) {
+            memcpy(policy, raw_ref.data(), raw_ref.size());
+        } else
+            return false;
+    } else if (policy_type_string.substr(1, 1) == USER_CE_KEY) {
+        if (lookup_key_ref(s_ce_policies, user_id, &raw_ref)) {
+            memcpy(policy, raw_ref.data(), raw_ref.size());
+        } else
+            return false;
+    } else {
+        LOG(ERROR) << "unknown policy type: " << policy_type;
+        return false;
+    }
+    return true;
+}
+
+bool Decrypt_DE() {
+	if (!fscrypt_initialize_systemwide_keys()) { // this deals with the overarching device encryption
+		printf("fscrypt_initialize_systemwide_keys returned fail\n");
+		return false;
+	}
+	if (!fscrypt_init_user0()) {
+		printf("fscrypt_init_user0 returned fail\n");
+		return false;
+	}
+	return true;
+}
+
+// Crappy functions for debugging, please ignore unless you need to debug
+// void output_hex(const std::string& in) {
+// 	const char *buf = in.data();
+// 	char hex[in.size() * 2 + 1];
+// 	unsigned int index;
+// 	for (index = 0; index < in.size(); index++)
+// 		sprintf(&hex[2 * index], "%02X", buf[index]);
+// 	printf("%s", hex);
+// }
+
+// void output_hex(const char* buf, const int size) {
+// 	char hex[size * 2 + 1];
+// 	int index;
+// 	for (index = 0; index < size; index++)
+// 		sprintf(&hex[2 * index], "%02X", buf[index]);
+// 	printf("%s", hex);
+// }
+
+// void output_hex(const unsigned char* buf, const int size) {
+// 	char hex[size * 2 + 1];
+// 	int index;
+// 	for (index = 0; index < size; index++)
+// 		sprintf(&hex[2 * index], "%02X", buf[index]);
+// 	printf("%s", hex);
+// }
+
+// void output_hex(std::vector<uint8_t>* vec) {
+// 	char hex[3];
+// 	unsigned int index;
+// 	for (index = 0; index < vec->size(); index++) {
+// 		sprintf(&hex[0], "%02X", vec->at(index));
+// 		printf("%s", hex);
+// 	}
+// }
+
+/* An alternative is to use:
+ * sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;"
+ * but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the
+ * password data file (*.pwd) and get the handle from the filename instead. This is a replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+ * We never use this data as an actual long. We always use it as a string. */
+bool Find_Handle(const std::string& spblob_path, std::string& handle_str) {
+	DIR* dir = opendir(spblob_path.c_str());
+	if (!dir) {
+		printf("Error opening '%s'\n", spblob_path.c_str());
+		return false;
+	}
+
+	struct dirent* de = 0;
+
+	while ((de = readdir(dir)) != 0) {
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+		size_t len = strlen(de->d_name);
+		if (len <= 4)
+			continue;
+		char* p = de->d_name;
+		p += len - 4;
+		if (strncmp(p, ".pwd", 4) == 0) {
+			handle_str = de->d_name;
+			handle_str = handle_str.substr(0, len - 4);
+			//*handle = strtoull(handle_str.c_str(), 0 , 16);
+			closedir(dir);
+			return true;
+		}
+	}
+	closedir(dir);
+	return false;
+}
+
+/* This is the structure of the data in the password data (*.pwd) file which the structure can be found
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */
+struct password_data_struct {
+	int password_type;
+	unsigned char scryptN;
+	unsigned char scryptR;
+	unsigned char scryptP;
+	int salt_len;
+	void* salt;
+	int handle_len;
+	void* password_handle;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */
+bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) {
+	std::string pwd_file = spblob_path + handle_str + ".pwd";
+	std::string pwd_data;
+	if (!android::base::ReadFileToString(pwd_file, &pwd_data)) {
+		printf("Failed to read '%s'\n", pwd_file.c_str());
+		return false;
+	}
+	// output_hex(pwd_data.data(), pwd_data.size());printf("\n");
+	const int* intptr = (const int*)pwd_data.data();
+	pwd->password_type = *intptr;
+	endianswap(&pwd->password_type);
+	//printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password
+	const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int);
+	pwd->scryptN = *byteptr;
+	byteptr++;
+	pwd->scryptR = *byteptr;
+	byteptr++;
+	pwd->scryptP = *byteptr;
+	byteptr++;
+	intptr = (const int*)byteptr;
+	pwd->salt_len = *intptr;
+	endianswap(&pwd->salt_len);
+	if (pwd->salt_len != 0) {
+		pwd->salt = malloc(pwd->salt_len);
+		if (!pwd->salt) {
+			printf("Get_Password_Data malloc salt\n");
+			return false;
+		}
+		memcpy(pwd->salt, intptr + 1, pwd->salt_len);
+		intptr++;
+		byteptr = (const unsigned char*)intptr;
+		byteptr += pwd->salt_len;
+	} else {
+		printf("Get_Password_Data salt_len is 0\n");
+		return false;
+	}
+	intptr = (const int*)byteptr;
+	pwd->handle_len = *intptr;
+	endianswap(&pwd->handle_len);
+	if (pwd->handle_len != 0) {
+		pwd->password_handle = malloc(pwd->handle_len);
+		if (!pwd->password_handle) {
+			printf("Get_Password_Data malloc password_handle\n");
+			return false;
+		}
+		memcpy(pwd->password_handle, intptr + 1, pwd->handle_len);
+	} else {
+		printf("Get_Password_Data handle_len is 0\n");
+		// Not an error if using weaver
+	}
+	return true;
+}
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */
+bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) {
+	if (!password_token) {
+		printf("password_token is null\n");
+		return false;
+	}
+	unsigned int N = 1 << pwd->scryptN;
+	unsigned int r = 1 << pwd->scryptR;
+	unsigned int p = 1 << pwd->scryptP;
+	//printf("N %i r %i p %i\n", N, r, p);
+	int ret = crypto_scrypt(reinterpret_cast<const uint8_t*>(Password.data()), Password.size(),
+                          reinterpret_cast<const uint8_t*>(pwd->salt), pwd->salt_len,
+                          N, r, p,
+                          password_token, 32);
+	if (ret != 0) {
+		printf("scrypt error\n");
+		return false;
+	}
+	return true;
+}
+
+// Data structure for the *.weaver file, see Get_Weaver_Data below
+struct weaver_data_struct {
+	unsigned char version;
+	int slot;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */
+bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) {
+	std::string weaver_file = spblob_path + handle_str + ".weaver";
+	std::string weaver_data;
+	if (!android::base::ReadFileToString(weaver_file, &weaver_data)) {
+		printf("Failed to read '%s'\n", weaver_file.c_str());
+		return false;
+	}
+	// output_hex(weaver_data.data(), weaver_data.size());printf("\n");
+	const unsigned char* byteptr = (const unsigned char*)weaver_data.data();
+	wd->version = *byteptr;
+	// printf("weaver version %i\n", wd->version);
+	const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char);
+	wd->slot = *intptr;
+	//endianswap(&wd->slot); not needed
+	// printf("weaver slot %i\n", wd->slot);
+	return true;
+}
+
+namespace android {
+
+// On Android 8.0 for some reason init can't seem to completely stop keystore
+// so we have to kill it too if it doesn't die on its own.
+static void kill_keystore() {
+    DIR* dir = opendir("/proc");
+    if (dir) {
+        struct dirent* de = 0;
+
+        while ((de = readdir(dir)) != 0) {
+            if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+                continue;
+
+            int pid = -1;
+            int ret = sscanf(de->d_name, "%d", &pid);
+
+            if (ret == 1) {
+                char cmdpath[PATH_MAX];
+                sprintf(cmdpath, "/proc/%d/cmdline", pid);
+
+                FILE* file = fopen(cmdpath, "r");
+                size_t task_size = PATH_MAX;
+                char task[PATH_MAX];
+                char* p = task;
+                if (getline(&p, &task_size, file) > 0) {
+                    if (strstr(task, "keystore") != 0) {
+                        printf("keystore pid %d found, sending kill.\n", pid);
+                        kill(pid, SIGINT);
+                        usleep(5000);
+                        kill(pid, SIGKILL);
+                    }
+                }
+                fclose(file);
+            }
+        }
+        closedir(dir);
+    }
+}
+
+// The keystore holds a file open on /data so we have to stop / kill it
+// if we want to be able to unmount /data for things like formatting.
+static void stop_keystore() {
+    printf("Stopping keystore...\n");
+    property_set("ctl.stop", "keystore");
+    usleep(5000);
+    kill_keystore();
+}
+
+/* These next 2 functions try to get the keystore service 50 times because
+ * the keystore is not always ready when TWRP boots */
+android::sp<IBinder> getKeystoreBinder() {
+	android::sp<IServiceManager> sm = android::defaultServiceManager();
+    return sm->getService(String16("android.security.keystore"));
+}
+
+android::sp<IBinder> getKeystoreBinderRetry() {
+	printf("Starting keystore...\n");
+    property_set("ctl.start", "keystore");
+	int retry_count = 50;
+	android::sp<IBinder> binder = getKeystoreBinder();
+	while (binder == NULL && retry_count) {
+		printf("Waiting for keystore service... %i\n", retry_count--);
+		sleep(1);
+		binder = getKeystoreBinder();
+	}
+	return binder;
+}
+
+namespace keystore {
+
+#define SYNTHETIC_PASSWORD_VERSION_V1 1
+#define SYNTHETIC_PASSWORD_VERSION_V2 2
+#define SYNTHETIC_PASSWORD_VERSION_V3 3
+#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
+#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
+#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_"
+
+static std::string mKey_Prefix;
+
+/* The keystore alias subid is sometimes the same as the handle, but not always.
+ * In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29
+ * without the leading 0. We could try to parse the data from a previous
+ * keystore request, but I think this is an easier solution because there
+ * is little to no documentation on the format of data we get back from
+ * the keystore in this instance. We also want to copy everything to a temp
+ * folder so that any key upgrades that might take place do not actually
+ * upgrade the keys on the data partition. We rename all 1000 uid files to 0
+ * to pass the keystore permission checks. */
+bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) {
+	char path_c[PATH_MAX];
+	sprintf(path_c, "/data/misc/keystore/user_%d", user_id);
+	char user_dir[PATH_MAX];
+	sprintf(user_dir, "user_%d", user_id);
+	std::string source_path = "/data/misc/keystore/";
+	source_path += user_dir;
+	std::string handle_sub = handle_str;
+	while (handle_sub.substr(0,1) == "0") {
+		std::string temp = handle_sub.substr(1);
+		handle_sub = temp;
+	}
+	mKey_Prefix = "";
+
+	mkdir("/tmp/misc", 0755);
+	mkdir("/tmp/misc/keystore", 0755);
+	std::string destination_path = "/tmp/misc/keystore/";
+	destination_path += user_dir;
+	if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) {
+		printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno));
+		return false;
+	}
+	destination_path += "/";
+
+	DIR* dir = opendir(source_path.c_str());
+	if (!dir) {
+		printf("Error opening '%s'\n", source_path.c_str());
+		return false;
+	}
+	source_path += "/";
+
+	struct dirent* de = 0;
+	size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX);
+	bool found_subid = false;
+	bool has_pkey = false; // PKEY has priority over SKEY
+
+	while ((de = readdir(dir)) != 0) {
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+		if (!found_subid) {
+			size_t len = strlen(de->d_name);
+			if (len <= prefix_len)
+				continue;
+			if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey)
+				mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX;
+			else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) {
+				mKey_Prefix = USR_PRIVATE_KEY_PREFIX;
+				has_pkey = true;
+			} else
+				continue;
+			if (strstr(de->d_name, handle_sub.c_str())) {
+				keystoreid = handle_sub;
+				printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str());
+				found_subid = true;
+			} else {
+				std::string file = de->d_name;
+				std::size_t found = file.find_last_of("_");
+				if (found != std::string::npos) {
+					keystoreid = file.substr(found + 1);
+					// printf("possible keystoreid: '%s'\n", keystoreid.c_str());
+					//found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub
+				}
+			}
+		}
+		std::string src = source_path;
+		src += de->d_name;
+		std::ifstream srcif(src.c_str(), std::ios::binary);
+		std::string dst = destination_path;
+		dst += de->d_name;
+		std::size_t source_uid = dst.find("1000");
+		if (source_uid != std::string::npos)
+			dst.replace(source_uid, 4, "0");
+		std::ofstream dstof(dst.c_str(), std::ios::binary);
+		printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str());
+		dstof << srcif.rdbuf();
+		srcif.close();
+		dstof.close();
+	}
+	closedir(dir);
+	if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty())
+		found_subid = true;
+	return found_subid;
+}
+
+/* C++ replacement for function of the same name
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867
+ * returning an empty string indicates an error */
+std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id,
+	const void* application_id, const size_t application_id_size, uint32_t auth_token_len) {
+	std::string disk_decryption_secret_key = "";
+
+	android::ProcessState::self()->startThreadPool();
+
+	std::string keystore_alias_subid;
+	// Can be stored in user 0, so check for both.
+	if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str) &&
+		!Find_Keystore_Alias_SubID_And_Prep_Files(0, keystore_alias_subid, handle_str))
+	{
+		printf("failed to scan keystore alias subid and prep keystore files\n");
+		return disk_decryption_secret_key;
+	}
+
+	// First get the keystore service
+    android::sp<IBinder> binder = getKeystoreBinderRetry();
+	android::sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+	if (service == NULL) {
+		printf("error: could not connect to keystore service\n");
+		return disk_decryption_secret_key;
+	}
+
+	if (auth_token_len > 0) {
+		printf("Starting keystore_auth service...\n");
+		property_set("ctl.start", "keystore_auth");
+	}
+
+	// Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869
+	std::string spblob_file = spblob_path + handle_str + ".spblob";
+	std::string spblob_data;
+	if (!android::base::ReadFileToString(spblob_file, &spblob_data)) {
+		printf("Failed to read '%s'\n", spblob_file.c_str());
+		return disk_decryption_secret_key;
+	}
+	unsigned char* byteptr = (unsigned char*)spblob_data.data();
+	if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1
+			&& *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) {
+		printf("Unsupported synthetic password version %i\n", *byteptr);
+		return disk_decryption_secret_key;
+	}
+	const unsigned char* synthetic_password_version = byteptr;
+	byteptr++;
+	if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) {
+		printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n");
+		return disk_decryption_secret_key;
+	}
+	byteptr++; // Now we're pointing to the blob data itself
+	if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2
+			|| *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+		printf("spblob v2 / v3\n");
+		/* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped
+		 * as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12
+		 */
+		/* First decrypt call found in
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135
+		 * We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+		 * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+		 * First we set some algorithm parameters as seen in two places:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+		// When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+		if (auth_token_len > 0) {
+			/*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+			if (!auth_result.isOk()) {
+				// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+				printf("keystore error adding auth token\n");
+				return disk_decryption_secret_key;
+			}*/
+			// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+			// run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+			// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+			// the /auth_token file never gets deleted.
+			int auth_wait_count = 20;
+			while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+				usleep(5000);
+			if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+				printf("error during keymaster_auth service\n");
+				/* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+				 * service keystore_auth /system/bin/keystore_auth
+				 *     disabled
+				 *     oneshot
+				 *     user system
+				 *     group root
+				 *     seclabel u:r:recovery:s0
+				 *
+				 * And check dmesg for error codes regarding this service if needed. */
+				return disk_decryption_secret_key;
+			}
+		}
+		int32_t ret;
+		size_t maclen = 128;
+		unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+		::keystore::hidl_vec<uint8_t> iv_hidlvec;
+		iv_hidlvec.setToExternal((unsigned char*)byteptr, 12);
+		// printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+		std::string keystore_alias = mKey_Prefix;
+		keystore_alias += keystore_alias_subid;
+		String16 keystore_alias16(keystore_alias.data(), keystore_alias.size());
+		int32_t error_code;
+		unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+		std::string cipher_text_str(byteptr, byteptr + spblob_data.size() - 14);
+
+		::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
+		::keystore::AuthorizationSetBuilder begin_params;
+
+        cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
+		begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+		begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+		begin_params.Padding(::keystore::PaddingMode::NONE);
+		begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec);
+		begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+
+		::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+		entropy.resize(0);
+		android::security::keymaster::KeymasterArguments empty_params;
+		android::hardware::keymaster::V4_0::KeyPurpose decryptPurpose  = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+		android::sp<android::IBinder> decryptAuthToken(new android::BBinder);
+
+		android::sp<OperationResultPromise> promise = new OperationResultPromise;
+		auto future = promise->get_future();
+		auto binder_result = service->begin(promise, decryptAuthToken, keystore_alias16, (int32_t)decryptPurpose, true,
+			android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), 
+			entropy, -1, &error_code);
+	    if (!binder_result.isOk()) {
+        	printf("communication error while calling keystore\n");
+			return disk_decryption_secret_key;
+   		}
+		::keystore::KeyStoreNativeReturnCode rc(error_code);
+		if (!rc.isOk()) {
+			printf("Keystore begin returned: %u\n", error_code);
+        	return disk_decryption_secret_key;
+    	}
+		OperationResult result = future.get();
+		std::map<uint64_t, android::sp<android::IBinder>> active_operations_;
+		uint64_t next_virtual_handle_ = 1;
+		active_operations_[next_virtual_handle_] = result.token;
+
+		// The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+		// See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+		future = {};
+		promise = new OperationResultPromise();
+	    future = promise->get_future();
+		binder_result = service->update(promise, active_operations_[next_virtual_handle_], empty_params, cipher_text_hidlvec, &error_code);
+		rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+        if (!rc.isOk()) {
+            printf("Keystore update returned: %d\n", error_code);
+            return disk_decryption_secret_key;
+        }
+		result = future.get();
+        if (!result.resultCode.isOk()) {
+            printf("update failed: %d\n", error_code);
+            return disk_decryption_secret_key;
+        }
+
+		size_t keystore_result_size = result.data.size();
+		unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size);
+		if (!keystore_result) {
+			printf("malloc on keystore_result\n");
+			return disk_decryption_secret_key;
+		}
+		memcpy(keystore_result, &result.data[0], result.data.size());
+		future = {};
+		promise = new OperationResultPromise();
+		future = promise->get_future();
+
+		auto hidlSignature = blob2hidlVec("");
+		auto hidlInput = blob2hidlVec(disk_decryption_secret_key);
+		binder_result = service->finish(promise, active_operations_[next_virtual_handle_], empty_params, hidlInput, hidlSignature, ::keystore::hidl_vec<uint8_t>(), &error_code);
+		if (!binder_result.isOk()) {
+			printf("communication error while calling keystore\n");
+			free(keystore_result);
+			return disk_decryption_secret_key;
+		}
+		rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+		if (!rc.isOk()) {
+			printf("Keystore finish returned: %d\n", error_code);
+			return disk_decryption_secret_key;
+		}
+		result = future.get();
+		if (!result.resultCode.isOk()) {
+			printf("finish failed: %d\n", error_code);
+			return disk_decryption_secret_key;
+		}
+		stop_keystore();
+		/* Now we do the second decrypt call as seen in:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136
+		 */
+		const unsigned char* intermediate_iv = keystore_result;
+		// printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n");
+		const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV
+		int cipher_size = keystore_result_size - 12;
+		// First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+		void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+		if (!personalized_application_id) {
+			return disk_decryption_secret_key;
+		}
+		// printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+		// Now we'll decrypt using openssl AES/GCM/NoPadding
+		OpenSSL_add_all_ciphers();
+		int actual_size=0, final_size=0;
+		EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+		const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+		// printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+		EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv);
+		unsigned char* secret_key = (unsigned char*)malloc(cipher_size);
+		if (!secret_key) {
+			printf("malloc failure on secret key\n");
+			return disk_decryption_secret_key;
+		}
+		EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size);
+		unsigned char tag[AES_BLOCK_SIZE];
+		EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+		EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size);
+		EVP_CIPHER_CTX_free(d_ctx);
+		free(personalized_application_id);
+		free(keystore_result);
+		int secret_key_real_size = actual_size - 16;
+		// printf("secret key:  "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n");
+		// The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+		// We now have the disk decryption key!
+		if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+			// V3 uses SP800 instead of SHA512
+			disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size);
+		} else {
+			disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size);
+		}
+		// printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+		free(secret_key);
+		return disk_decryption_secret_key;
+	}
+	return disk_decryption_secret_key;
+}
+
+}}
+
+#define PASSWORD_TOKEN_SIZE 32
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */
+bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) {
+	std::string secdis_file = spblob_path + handle_str + ".secdis";
+	if (!android::base::ReadFileToString(secdis_file, &secdis_data)) {
+		printf("Failed to read '%s'\n", secdis_file.c_str());
+		return false;
+	}
+	// output_hex(secdis_data.data(), secdis_data.size());printf("\n");
+	return true;
+}
+
+// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033
+userid_t fakeUid(const userid_t uid) {
+    return 100000 + uid;
+}
+
+bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) {
+	std::string weaver_file = spblob_path + handle_str + ".weaver";
+	struct stat st;
+	if (stat(weaver_file.c_str(), &st) == 0)
+		return true;
+	return false;
+}
+
+bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) {
+	if (weaver_key)
+		free(weaver_key);
+	if (pwd->salt)
+		free(pwd->salt);
+	if (pwd->password_handle)
+		free(pwd->password_handle);
+	return retval;
+}
+
+/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */
+bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) {
+	bool retval = false;
+	void* weaver_key = NULL;
+	password_data_struct pwd;
+	pwd.salt = NULL;
+	pwd.salt_len = 0;
+	pwd.password_handle = NULL;
+	pwd.handle_len = 0;
+	char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
+
+    uint32_t auth_token_len = 0;
+
+	std::string secret; // this will be the disk decryption key that is sent to vold
+	std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
+	int flags = android::os::IVold::STORAGE_FLAG_CE;
+	char spblob_path_char[PATH_MAX];
+	sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+	std::string spblob_path = spblob_path_char;
+	long handle = 0;
+	std::string handle_str;
+	// Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+	if (!Find_Handle(spblob_path, handle_str)) {
+		printf("Error getting handle\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	// printf("Handle is '%s'\n", handle_str.c_str());
+	// Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758
+	// First we read the password data which contains scrypt parameters
+	if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+		printf("Failed to Get_Password_Data\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	// printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n");
+	unsigned char password_token[PASSWORD_TOKEN_SIZE];
+	// printf("Password: '%s'\n", Password.c_str());
+	// The password token is the password scrypted with the parameters from the password data file
+	if (!Get_Password_Token(&pwd, Password, &password_token[0])) {
+		printf("Failed to Get_Password_Token\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	// output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\n");
+	if (Is_Weaver(spblob_path, handle_str)) {
+		printf("using weaver\n");
+		// BEGIN PIXEL 2 WEAVER
+		// Get the weaver data from the .weaver file which tells us which slot to use when we ask weaver for the escrowed key
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768
+		weaver_data_struct wd;
+		if (!Get_Weaver_Data(spblob_path, handle_str, &wd)) {
+			printf("Failed to get weaver data\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// The weaver key is the the password token prefixed with "weaver-key" padded to 128 with nulls with the password token appended then SHA512
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1059
+		weaver_key = PersonalizedHashBinary(PERSONALISATION_WEAVER_KEY, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		if (!weaver_key) {
+			printf("malloc error getting weaver_key\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// Now we start driving weaverVerify: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#343
+		// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#776
+		android::vold::Weaver weaver;
+		if (!weaver) {
+			printf("Failed to get weaver service\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// Get the key size from weaver service
+		uint32_t weaver_key_size = 0;
+		if (!weaver.GetKeySize(&weaver_key_size)) {
+			printf("Failed to get weaver key size\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		} else {
+			printf("weaver key size is %u\n", weaver_key_size);
+		}
+		// printf("weaver key: "); output_hex((unsigned char*)weaver_key, weaver_key_size); printf("\n");
+		// Send the slot from the .weaver file, the computed weaver key, and get the escrowed key data
+		std::vector<uint8_t> weaver_payload;
+		// TODO: we should return more information about the status including time delays before the next retry
+		if (!weaver.WeaverVerify(wd.slot, weaver_key, &weaver_payload)) {
+			printf("failed to weaver verify\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// printf("weaver payload: "); output_hex(&weaver_payload); printf("\n");
+		// Done with weaverVerify
+		// Now we will compute the application ID
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#964
+		// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#780
+		// The escrowed weaver key data is prefixed with "weaver-pwd" padded to 128 with nulls with the weaver payload appended then SHA512
+		void* weaver_secret = PersonalizedHashBinary(PERSONALISATION_WEAVER_PASSWORD, (const char*)weaver_payload.data(), weaver_payload.size());
+		// printf("weaver secret: "); output_hex((unsigned char*)weaver_secret, SHA512_DIGEST_LENGTH); printf("\n");
+		// The application ID is the password token and weaver secret appended to each other
+		memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], weaver_secret, SHA512_DIGEST_LENGTH);
+		// printf("application ID: "); output_hex((unsigned char*)application_id, PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH); printf("\n");
+		// END PIXEL 2 WEAVER
+	} else {
+		printf("using secdis\n");
+		std::string secdis_data;
+		if (!Get_Secdis(spblob_path, handle_str, secdis_data)) {
+			printf("Failed to get secdis data\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		void* secdiscardable = PersonalizedHashBinary(PERSONALISATION_SECDISCARDABLE, (char*)secdis_data.data(), secdis_data.size());
+		if (!secdiscardable) {
+			printf("malloc error getting secdiscardable\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], secdiscardable, SHA512_DIGEST_LENGTH);
+
+		int ret = -1;
+		bool request_reenroll = false;
+		android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+		gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+		if (gk_device == nullptr) {
+			printf("failed to get gatekeeper service\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		if (pwd.handle_len <= 0) {
+			printf("no password handle supplied\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		android::hardware::hidl_vec<uint8_t> pwd_handle_hidl;
+		pwd_handle_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)pwd.password_handle), pwd.handle_len);
+		void* gk_pwd_token = PersonalizedHashBinary(PERSONALIZATION_USER_GK_AUTH, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		if (!gk_pwd_token) {
+			printf("malloc error getting gatekeeper_key\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		android::hardware::hidl_vec<uint8_t> gk_pwd_token_hidl;
+		gk_pwd_token_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)gk_pwd_token), SHA512_DIGEST_LENGTH);
+		android::hardware::Return<void> hwRet =
+			gk_device->verify(fakeUid(user_id), 0 /* challange */,
+							  pwd_handle_hidl,
+							  gk_pwd_token_hidl,
+							  [&ret, &request_reenroll, &auth_token_len]
+								(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+									ret = static_cast<int>(rsp.code); // propagate errors
+									if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+										auth_token_len = rsp.data.size();
+										request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+										ret = 0; // all success states are reported as 0
+										// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file here and later
+										// run a separate service that runs as the system user to add the auth token. We wait for the auth token file to be
+										// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after a while seconds if
+										// the /auth_token file never gets deleted.
+										unlink("/auth_token");
+										FILE* auth_file = fopen("/auth_token","wb");
+										if (auth_file != NULL) {
+											fwrite(rsp.data.data(), sizeof(uint8_t), rsp.data.size(), auth_file);
+											fclose(auth_file);
+										} else {
+											printf("failed to open /auth_token for writing\n");
+											ret = -2;
+										}
+									} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+										ret = rsp.timeout;
+									}
+								}
+							 );
+		free(gk_pwd_token);
+		if (!hwRet.isOk() || ret != 0) {
+			printf("gatekeeper verification failed\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+	}
+	// Now we will handle https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#816
+	// Plus we will include the last bit that computes the disk decrypt key found in:
+	// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+	secret = android::keystore::unwrapSyntheticPasswordBlob(spblob_path, handle_str, user_id, (const void*)&application_id[0], 
+		PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH, auth_token_len);
+	if (!secret.size()) {
+		printf("failed to unwrapSyntheticPasswordBlob\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+
+	if (!fscrypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+		printf("fscrypt_unlock_user_key returned fail\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+
+	if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+		printf("failed to fscrypt_prepare_user_storage\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	printf("User %i Decrypted Successfully!\n", user_id);
+	retval = true;
+	return Free_Return(retval, weaver_key, &pwd);
+}
+
+int Get_Password_Type(const userid_t user_id, std::string& filename) {
+	struct stat st;
+	char spblob_path_char[PATH_MAX];
+	sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+	if (stat(spblob_path_char, &st) == 0) {
+		printf("Using synthetic password method\n");
+		std::string spblob_path = spblob_path_char;
+		std::string handle_str;
+		if (!Find_Handle(spblob_path, handle_str)) {
+			printf("Error getting handle\n");
+			return 0;
+		}
+		printf("Handle is '%s'\n", handle_str.c_str());
+		password_data_struct pwd;
+		if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+			printf("Failed to Get_Password_Data\n");
+			return 0;
+		}
+		// In Android type 1 is pattern
+		// In Android <11 type 2 is PIN or password
+		// In Android 11+ type 3 is PIN and type 4 is password
+		if (pwd.password_type == 2) {
+			printf("password type: password/PIN\n");
+			return 1; // In TWRP this means password or PIN (Android <11)
+		} else if (pwd.password_type == 4) {
+			printf("password type: password\n");
+			return 1; // In TWRP this means password
+		} else if (pwd.password_type == 1) {
+			printf("password type: pattern\n");
+			return 2; // In TWRP this means pattern
+		} else if (pwd.password_type == 3) {
+			printf("password type: PIN\n");
+			return 3; // In TWRP this means PIN
+		}
+		printf("using default password\n");
+		return 0; // We'll try the default password
+	}
+	std::string path;
+    if (user_id == 0) {
+		path = "/data/system/";
+	} else {
+		char user_id_str[5];
+		sprintf(user_id_str, "%i", user_id);
+		path = "/data/system/users/";
+		path += user_id_str;
+		path += "/";
+	}
+	filename = path + "gatekeeper.password.key";
+	if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+		return 1;
+	filename = path + "gatekeeper.pattern.key";
+	if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+		return 2;
+	printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str());
+	filename = "";
+	return 0;
+}
+
+bool Decrypt_User(const userid_t user_id, const std::string& Password) {
+    uint8_t *auth_token;
+    uint32_t auth_token_len;
+    int ret;
+
+    struct stat st;
+    if (user_id > 9999) {
+		printf("user_id is too big\n");
+		return false;
+	}
+    std::string filename;
+    bool Default_Password = (Password == "!");
+    if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) {
+		printf("Unknown password type\n");
+		return false;
+	}
+
+	int flags = android::os::IVold::STORAGE_FLAG_CE;
+
+	if (Default_Password) {
+		if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) {
+			printf("unlock_user_key returned fail\n");
+			return false;
+		}
+		if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+			printf("failed to fscrypt_prepare_user_storage\n");
+			return false;
+		}
+		printf("User %i Decrypted Successfully!\n", user_id);
+		return true;
+	}
+	if (stat("/data/system_de/0/spblob", &st) == 0) {
+		printf("Using synthetic password method\n");
+		return Decrypt_User_Synth_Pass(user_id, Password);
+	}
+	// printf("password filename is '%s'\n", filename.c_str());
+	if (stat(filename.c_str(), &st) != 0) {
+		printf("error stat'ing key file: %s\n", strerror(errno));
+		return false;
+	}
+	std::string handle;
+    if (!android::base::ReadFileToString(filename, &handle)) {
+		printf("Failed to read '%s'\n", filename.c_str());
+		return false;
+	}
+    bool should_reenroll;
+	bool request_reenroll = false;
+	android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+	gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+	if (gk_device == nullptr)
+		return false;
+	android::hardware::hidl_vec<uint8_t> curPwdHandle;
+	curPwdHandle.setToExternal(const_cast<uint8_t *>((const uint8_t *)handle.c_str()), st.st_size);
+	android::hardware::hidl_vec<uint8_t> enteredPwd;
+	enteredPwd.setToExternal(const_cast<uint8_t *>((const uint8_t *)Password.c_str()), Password.size());
+
+	android::hardware::Return<void> hwRet =
+		gk_device->verify(user_id, 0 /* challange */,
+						  curPwdHandle,
+						  enteredPwd,
+						  [&ret, &request_reenroll, &auth_token, &auth_token_len]
+							(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+								ret = static_cast<int>(rsp.code); // propagate errors
+								if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+									auth_token = new uint8_t[rsp.data.size()];
+									auth_token_len = rsp.data.size();
+									memcpy(auth_token, rsp.data.data(), auth_token_len);
+									request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+									ret = 0; // all success states are reported as 0
+								} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+									ret = rsp.timeout;
+								}
+							}
+						 );
+	if (!hwRet.isOk()) {
+		return false;
+	}
+
+	char token_hex[(auth_token_len*2)+1];
+	token_hex[(auth_token_len*2)] = 0;
+	uint32_t i;
+	for (i=0;i<auth_token_len;i++) {
+		sprintf(&token_hex[2*i], "%02X", auth_token[i]);
+	}
+	// The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
+	std::string secret = HashPassword(Password);
+	if (!fscrypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+		printf("fscrypt_unlock_user_key returned fail\n");
+		return false;
+	}
+
+	if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+		printf("failed to fscrypt_prepare_user_storage\n");
+		return false;
+	}
+	printf("User %i Decrypted Successfully!\n", user_id);
+	return true;
+}
diff --git a/crypto/fscrypt/Decrypt.h b/crypto/fscrypt/Decrypt.h
new file mode 100755
index 0000000..f397749
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__BEGIN_DECLS
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+// For 9.0 Ext4CryptPie.cpp
+static constexpr int STORAGE_FLAG_DE = 1;
+static constexpr int STORAGE_FLAG_CE = 2;
+
+
+int Get_Password_Type(const userid_t user_id, std::string& filename);
+bool Decrypt_DE();
+bool Decrypt_User(const userid_t user_id, const std::string& Password);
+__END_DECLS
diff --git a/crypto/fscrypt/EncryptInplace.cpp b/crypto/fscrypt/EncryptInplace.cpp
new file mode 100644
index 0000000..9d304da
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.cpp
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EncryptInplace.h"
+
+#include <ext4_utils/ext4.h>
+#include <ext4_utils/ext4_utils.h>
+#include <f2fs_sparseblock.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+// HORRIBLE HACK, FIXME
+#include "cryptfs.h"
+
+// FIXME horrible cut-and-paste code
+static inline int unix_read(int fd, void* buff, int len) {
+    return TEMP_FAILURE_RETRY(read(fd, buff, len));
+}
+
+static inline int unix_write(int fd, const void* buff, int len) {
+    return TEMP_FAILURE_RETRY(write(fd, buff, len));
+}
+
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
+
+/* aligned 32K writes tends to make flash happy.
+ * SD card association recommends it.
+ */
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+#define BLOCKS_AT_A_TIME 8
+#else
+#define BLOCKS_AT_A_TIME 1024
+#endif
+
+struct encryptGroupsData {
+    int realfd;
+    int cryptofd;
+    off64_t numblocks;
+    off64_t one_pct, cur_pct, new_pct;
+    off64_t blocks_already_done, tot_numblocks;
+    off64_t used_blocks_already_done, tot_used_blocks;
+    const char* real_blkdev;
+    const char* crypto_blkdev;
+    int count;
+    off64_t offset;
+    char* buffer;
+    off64_t last_written_sector;
+    int completed;
+    time_t time_started;
+    int remaining_time;
+    bool set_progress_properties;
+};
+
+static void update_progress(struct encryptGroupsData* data, int is_used) {
+    data->blocks_already_done++;
+
+    if (is_used) {
+        data->used_blocks_already_done++;
+    }
+    if (data->tot_used_blocks) {
+        data->new_pct = data->used_blocks_already_done / data->one_pct;
+    } else {
+        data->new_pct = data->blocks_already_done / data->one_pct;
+    }
+
+    if (!data->set_progress_properties) return;
+
+    if (data->new_pct > data->cur_pct) {
+        char buf[8];
+        data->cur_pct = data->new_pct;
+        snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
+        android::base::SetProperty("vold.encrypt_progress", buf);
+    }
+
+    if (data->cur_pct >= 5) {
+        struct timespec time_now;
+        if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
+            LOG(WARNING) << "Error getting time";
+        } else {
+            double elapsed_time = difftime(time_now.tv_sec, data->time_started);
+            off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done;
+            int remaining_time =
+                (int)(elapsed_time * remaining_blocks / data->used_blocks_already_done);
+
+            // Change time only if not yet set, lower, or a lot higher for
+            // best user experience
+            if (data->remaining_time == -1 || remaining_time < data->remaining_time ||
+                remaining_time > data->remaining_time + 60) {
+                char buf[8];
+                snprintf(buf, sizeof(buf), "%d", remaining_time);
+                android::base::SetProperty("vold.encrypt_time_remaining", buf);
+                data->remaining_time = remaining_time;
+            }
+        }
+    }
+}
+
+static void log_progress(struct encryptGroupsData const* data, bool completed) {
+    // Precondition - if completed data = 0 else data != 0
+
+    // Track progress so we can skip logging blocks
+    static off64_t offset = -1;
+
+    // Need to close existing 'Encrypting from' log?
+    if (completed || (offset != -1 && data->offset != offset)) {
+        LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE;
+        offset = -1;
+    }
+
+    // Need to start new 'Encrypting from' log?
+    if (!completed && offset != data->offset) {
+        LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE;
+    }
+
+    // Update offset
+    if (!completed) {
+        offset = data->offset + (off64_t)data->count * info.block_size;
+    }
+}
+
+static int flush_outstanding_data(struct encryptGroupsData* data) {
+    if (data->count == 0) {
+        return 0;
+    }
+
+    LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
+
+    if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+        LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt";
+        return -1;
+    }
+
+    if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+        LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev
+                   << " for inplace encrypt";
+        return -1;
+    } else {
+        log_progress(data, false);
+    }
+
+    data->count = 0;
+    data->last_written_sector =
+        (data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1;
+    return 0;
+}
+
+static int encrypt_groups(struct encryptGroupsData* data) {
+    unsigned int i;
+    u8* block_bitmap = 0;
+    unsigned int block;
+    off64_t ret;
+    int rc = -1;
+
+    data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME);
+    if (!data->buffer) {
+        LOG(ERROR) << "Failed to allocate crypto buffer";
+        goto errout;
+    }
+
+    block_bitmap = (u8*)malloc(info.block_size);
+    if (!block_bitmap) {
+        LOG(ERROR) << "failed to allocate block bitmap";
+        goto errout;
+    }
+
+    for (i = 0; i < aux_info.groups; ++i) {
+        LOG(INFO) << "Encrypting group " << i;
+
+        u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+        u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block));
+
+        off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
+
+        ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
+        if (ret != (int)info.block_size) {
+            LOG(ERROR) << "failed to read all of block group bitmap " << i;
+            goto errout;
+        }
+
+        offset = (u64)info.block_size * first_block;
+
+        data->count = 0;
+
+        for (block = 0; block < block_count; block++) {
+            int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
+                           ? 0
+                           : bitmap_get_bit(block_bitmap, block);
+            update_progress(data, used);
+            if (used) {
+                if (data->count == 0) {
+                    data->offset = offset;
+                }
+                data->count++;
+            } else {
+                if (flush_outstanding_data(data)) {
+                    goto errout;
+                }
+            }
+
+            offset += info.block_size;
+
+            /* Write data if we are aligned or buffer size reached */
+            if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 ||
+                data->count == BLOCKS_AT_A_TIME) {
+                if (flush_outstanding_data(data)) {
+                    goto errout;
+                }
+            }
+        }
+        if (flush_outstanding_data(data)) {
+            goto errout;
+        }
+    }
+
+    data->completed = 1;
+    rc = 0;
+
+errout:
+    log_progress(0, true);
+    free(data->buffer);
+    free(block_bitmap);
+    return rc;
+}
+
+static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
+                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
+                                       off64_t previously_encrypted_upto,
+                                       bool set_progress_properties) {
+    u32 i;
+    struct encryptGroupsData data;
+    int rc;  // Can't initialize without causing warning -Wclobbered
+    int retries = RETRY_MOUNT_ATTEMPTS;
+    struct timespec time_started = {0};
+
+    if (previously_encrypted_upto > *size_already_done) {
+        LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+        return -1;
+    }
+
+    memset(&data, 0, sizeof(data));
+    data.real_blkdev = real_blkdev;
+    data.crypto_blkdev = crypto_blkdev;
+    data.set_progress_properties = set_progress_properties;
+
+    LOG(DEBUG) << "Opening" << real_blkdev;
+    if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+        rc = -1;
+        goto errout;
+    }
+
+    LOG(DEBUG) << "Opening" << crypto_blkdev;
+    // Wait until the block device appears.  Re-use the mount retry values since it is reasonable.
+    while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+        if (--retries) {
+            PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+                        << " for ext4 inplace encrypt, retrying";
+            sleep(RETRY_MOUNT_DELAY_SECONDS);
+        } else {
+            PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+                        << " for ext4 inplace encrypt";
+            rc = ENABLE_INPLACE_ERR_DEV;
+            goto errout;
+        }
+    }
+
+    if (setjmp(setjmp_env)) {  // NOLINT
+        LOG(ERROR) << "Reading ext4 extent caused an exception";
+        rc = -1;
+        goto errout;
+    }
+
+    if (read_ext(data.realfd, 0) != 0) {
+        LOG(ERROR) << "Failed to read ext4 extent";
+        rc = -1;
+        goto errout;
+    }
+
+    data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+    LOG(INFO) << "Encrypting ext4 filesystem in place...";
+
+    data.tot_used_blocks = data.numblocks;
+    for (i = 0; i < aux_info.groups; ++i) {
+        data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
+    }
+
+    data.one_pct = data.tot_used_blocks / 100;
+    data.cur_pct = 0;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+        LOG(WARNING) << "Error getting time at start";
+        // Note - continue anyway - we'll run with 0
+    }
+    data.time_started = time_started.tv_sec;
+    data.remaining_time = -1;
+
+    rc = encrypt_groups(&data);
+    if (rc) {
+        LOG(ERROR) << "Error encrypting groups";
+        goto errout;
+    }
+
+    *size_already_done += data.completed ? size : data.last_written_sector;
+    rc = 0;
+
+errout:
+    close(data.realfd);
+    close(data.cryptofd);
+
+    return rc;
+}
+
+static void log_progress_f2fs(u64 block, bool completed) {
+    // Precondition - if completed data = 0 else data != 0
+
+    // Track progress so we can skip logging blocks
+    static u64 last_block = (u64)-1;
+
+    // Need to close existing 'Encrypting from' log?
+    if (completed || (last_block != (u64)-1 && block != last_block + 1)) {
+        LOG(INFO) << "Encrypted to block " << last_block;
+        last_block = -1;
+    }
+
+    // Need to start new 'Encrypting from' log?
+    if (!completed && (last_block == (u64)-1 || block != last_block + 1)) {
+        LOG(INFO) << "Encrypting from block " << block;
+    }
+
+    // Update offset
+    if (!completed) {
+        last_block = block;
+    }
+}
+
+static int encrypt_one_block_f2fs(u64 pos, void* data) {
+    struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data;
+
+    priv_dat->blocks_already_done = pos - 1;
+    update_progress(priv_dat, 1);
+
+    off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
+
+    if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+        LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev
+                   << " for f2fs inplace encrypt";
+        return -1;
+    }
+
+    if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+        LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev
+                   << " for f2fs inplace encrypt";
+        return -1;
+    } else {
+        log_progress_f2fs(pos, false);
+    }
+
+    return 0;
+}
+
+static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
+                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
+                                       off64_t previously_encrypted_upto,
+                                       bool set_progress_properties) {
+    struct encryptGroupsData data;
+    struct f2fs_info* f2fs_info = NULL;
+    int rc = ENABLE_INPLACE_ERR_OTHER;
+    struct timespec time_started = {0};
+
+    if (previously_encrypted_upto > *size_already_done) {
+        LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+        return ENABLE_INPLACE_ERR_OTHER;
+    }
+    memset(&data, 0, sizeof(data));
+    data.real_blkdev = real_blkdev;
+    data.crypto_blkdev = crypto_blkdev;
+    data.set_progress_properties = set_progress_properties;
+    data.realfd = -1;
+    data.cryptofd = -1;
+    if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt";
+        goto errout;
+    }
+    if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+                    << " for f2fs inplace encrypt";
+        rc = ENABLE_INPLACE_ERR_DEV;
+        goto errout;
+    }
+
+    f2fs_info = generate_f2fs_info(data.realfd);
+    if (!f2fs_info) goto errout;
+
+    data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+    data.tot_used_blocks = get_num_blocks_used(f2fs_info);
+
+    data.one_pct = data.tot_used_blocks / 100;
+    data.cur_pct = 0;
+    if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+        LOG(WARNING) << "Error getting time at start";
+        // Note - continue anyway - we'll run with 0
+    }
+    data.time_started = time_started.tv_sec;
+    data.remaining_time = -1;
+
+
+    data.buffer = (char*)malloc(f2fs_info->block_size);
+    if (!data.buffer) {
+        LOG(ERROR) << "Failed to allocate crypto buffer";
+        goto errout;
+    }
+
+    data.count = 0;
+
+    /* Currently, this either runs to completion, or hits a nonrecoverable error */
+    rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
+
+    if (rc) {
+        LOG(ERROR) << "Error in running over f2fs blocks";
+        rc = ENABLE_INPLACE_ERR_OTHER;
+        goto errout;
+    }
+
+    *size_already_done += size;
+    rc = 0;
+
+errout:
+    if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
+
+    log_progress_f2fs(0, true);
+    free(f2fs_info);
+    free(data.buffer);
+    close(data.realfd);
+    close(data.cryptofd);
+
+    return rc;
+}
+
+static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev,
+                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
+                                       off64_t previously_encrypted_upto,
+                                       bool set_progress_properties) {
+    int realfd, cryptofd;
+    char* buf[CRYPT_INPLACE_BUFSIZE];
+    int rc = ENABLE_INPLACE_ERR_OTHER;
+    off64_t numblocks, i, remainder;
+    off64_t one_pct, cur_pct, new_pct;
+    off64_t blocks_already_done, tot_numblocks;
+
+    if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+        return ENABLE_INPLACE_ERR_OTHER;
+    }
+
+    if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+        close(realfd);
+        return ENABLE_INPLACE_ERR_DEV;
+    }
+
+    /* This is pretty much a simple loop of reading 4K, and writing 4K.
+     * The size passed in is the number of 512 byte sectors in the filesystem.
+     * So compute the number of whole 4K blocks we should read/write,
+     * and the remainder.
+     */
+    numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+    remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
+    tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+    blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+    LOG(ERROR) << "Encrypting filesystem in place...";
+
+    i = previously_encrypted_upto + 1 - *size_already_done;
+
+    if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
+        goto errout;
+    }
+
+    if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
+        goto errout;
+    }
+
+    for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
+        if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+            PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev
+                        << " for inplace encrypt";
+            goto errout;
+        }
+        if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+            PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev
+                        << " for inplace encrypt";
+            goto errout;
+        } else {
+            LOG(INFO) << "Encrypted 1 block at " << i;
+        }
+    }
+
+    one_pct = tot_numblocks / 100;
+    cur_pct = 0;
+    /* process the majority of the filesystem in blocks */
+    for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) {
+        new_pct = (i + blocks_already_done) / one_pct;
+        if (set_progress_properties && new_pct > cur_pct) {
+            char property_buf[8];
+
+            cur_pct = new_pct;
+            snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct);
+            android::base::SetProperty("vold.encrypt_progress", property_buf);
+        }
+        if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+            PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt";
+            goto errout;
+        }
+        if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+            PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+            goto errout;
+        } else {
+            LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at "
+                       << i * CRYPT_SECTORS_PER_BUFSIZE;
+        }
+    }
+
+    /* Do any remaining sectors */
+    for (i = 0; i < remainder; i++) {
+        if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+            LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev
+                       << " for inplace encrypt";
+            goto errout;
+        }
+        if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+            LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev
+                       << " for inplace encrypt";
+            goto errout;
+        } else {
+            LOG(INFO) << "Encrypted 1 block at next location";
+        }
+    }
+
+    *size_already_done += size;
+    rc = 0;
+
+errout:
+    close(realfd);
+    close(cryptofd);
+
+    return rc;
+}
+
+/* returns on of the ENABLE_INPLACE_* return codes */
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+                           off64_t* size_already_done, off64_t tot_size,
+                           off64_t previously_encrypted_upto, bool set_progress_properties) {
+    int rc_ext4, rc_f2fs, rc_full;
+    LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
+               << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto
+               << ", " << set_progress_properties << ")";
+    if (previously_encrypted_upto) {
+        LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto;
+    }
+
+    if (*size_already_done + size < previously_encrypted_upto) {
+        LOG(DEBUG) << "cryptfs_enable_inplace already done";
+        *size_already_done += size;
+        return 0;
+    }
+
+    /* TODO: identify filesystem type.
+     * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
+     * then we will drop down to cryptfs_enable_inplace_f2fs.
+     * */
+    if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done,
+                                               tot_size, previously_encrypted_upto,
+                                               set_progress_properties)) == 0) {
+        LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
+        return 0;
+    }
+    LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
+
+    if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done,
+                                               tot_size, previously_encrypted_upto,
+                                               set_progress_properties)) == 0) {
+        LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
+        return 0;
+    }
+    LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
+
+    rc_full =
+        cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size,
+                                    previously_encrypted_upto, set_progress_properties);
+    LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
+
+    /* Hack for b/17898962, the following is the symptom... */
+    if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV &&
+        rc_full == ENABLE_INPLACE_ERR_DEV) {
+        LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV";
+        return ENABLE_INPLACE_ERR_DEV;
+    }
+    return rc_full;
+}
diff --git a/crypto/fscrypt/EncryptInplace.h b/crypto/fscrypt/EncryptInplace.h
new file mode 100644
index 0000000..a2b46cf
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ENCRYPT_INPLACE_H
+#define _ENCRYPT_INPLACE_H
+
+#include <sys/types.h>
+
+#define CRYPT_INPLACE_BUFSIZE 4096
+#define CRYPT_SECTOR_SIZE 512
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+/* Return values for cryptfs_enable_inplace() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER (-1)
+#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
+
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+                           off64_t* size_already_done, off64_t tot_size,
+                           off64_t previously_encrypted_upto, bool set_progress_properties);
+
+#endif
diff --git a/crypto/fscrypt/FsCrypt.cpp b/crypto/fscrypt/FsCrypt.cpp
new file mode 100755
index 0000000..6284fa8
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.cpp
@@ -0,0 +1,1038 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FsCrypt.h"
+
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <algorithm>
+#include <map>
+#include <optional>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <selinux/android.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_projectid_config.h>
+
+#include "android/os/IVold.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <libdm/dm.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fscrypt-common.h"
+
+using android::base::Basename;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::fs_mgr::GetEntryForMountPoint;
+using ::BuildDataPath;
+using ::IsFilesystemSupported;
+using ::kEmptyAuthentication;
+using ::KeyBuffer;
+using ::KeyGeneration;
+using ::retrieveKey;
+using ::retrieveOrGenerateKey;
+using ::SetQuotaInherit;
+using ::SetQuotaProjectId;
+using ::writeStringToFile;
+using namespace android::fscrypt;
+using namespace android::dm;
+
+// Map user ids to encryption policies
+std::map<userid_t, EncryptionPolicy> s_de_policies;
+std::map<userid_t, EncryptionPolicy> s_ce_policies;
+std::string de_key_raw_ref;
+
+namespace {
+
+const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder;
+const std::string device_key_path = device_key_dir + "/key";
+const std::string device_key_temp = device_key_dir + "/temp";
+
+const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
+const std::string user_key_temp = user_key_dir + "/temp";
+const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
+
+const std::string systemwide_volume_key_dir =
+    std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+}  // namespace
+
+// Returns KeyGeneration suitable for key as described in EncryptionOptions
+static KeyGeneration makeGen(const EncryptionOptions& options) {
+    return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
+}
+
+static bool fscrypt_is_emulated() {
+    return property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+static const char* escape_empty(const std::string& value) {
+    return value.empty() ? "null" : value.c_str();
+}
+
+static std::string get_de_key_path(userid_t user_id) {
+    return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+    return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
+}
+
+// Returns the keys newest first
+static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+    if (!dirp) {
+        PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
+        return std::vector<std::string>();
+    }
+    std::vector<std::string> result;
+    for (;;) {
+        errno = 0;
+        auto const entry = readdir(dirp.get());
+        if (!entry) {
+            if (errno) {
+                PLOG(ERROR) << "Unable to read ce key directory: " + directory_path;
+                return std::vector<std::string>();
+            }
+            break;
+        }
+        if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
+            LOG(INFO) << "Skipping non-key " << entry->d_name;
+            continue;
+        }
+        result.emplace_back(directory_path + "/" + entry->d_name);
+    }
+    std::sort(result.begin(), result.end());
+    std::reverse(result.begin(), result.end());
+    return result;
+}
+
+static std::string get_ce_key_current_path(const std::string& directory_path) {
+    return directory_path + "/current";
+}
+
+static bool get_ce_key_new_path(const std::string& directory_path,
+                                const std::vector<std::string>& paths, std::string* ce_key_path) {
+    if (paths.empty()) {
+        *ce_key_path = get_ce_key_current_path(directory_path);
+        return true;
+    }
+    for (unsigned int i = 0; i < UINT_MAX; i++) {
+        auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i);
+        if (paths[0] < candidate) {
+            *ce_key_path = candidate;
+            return true;
+        }
+    }
+    return false;
+}
+
+// Discard all keys but the named one; rename it to canonical name.
+// No point in acting on errors in this; ignore them.
+static void fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix,
+                               const std::vector<std::string>& paths) {
+    for (auto const other_path : paths) {
+        if (other_path != to_fix) {
+            ::destroyKey(other_path);
+        }
+    }
+    auto const current_path = get_ce_key_current_path(directory_path);
+    if (to_fix != current_path) {
+        LOG(INFO) << "Renaming " << to_fix << " to " << current_path;
+        if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
+            PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
+            return;
+        }
+    }
+    ::FsyncDirectory(directory_path);
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+                                        const ::KeyAuthentication& auth,
+                                        KeyBuffer* ce_key) {
+    auto const directory_path = get_ce_key_directory_path(user_id);
+    auto const paths = get_ce_key_paths(directory_path);
+    for (auto const ce_key_path : paths) {
+        LOG(INFO) << "Trying user CE key " << ce_key_path;
+        if (retrieveKey(ce_key_path, auth, ce_key, true)) {
+            LOG(INFO) << "Successfully retrieved key";
+            fixate_user_ce_key(directory_path, ce_key_path, paths);
+            return true;
+        }
+    }
+    LOG(ERROR) << "Failed to find working ce key for user " << user_id;
+    return false;
+}
+
+static bool IsEmmcStorage(const std::string& blk_device) {
+    // Handle symlinks.
+    std::string real_path;
+    if (!Realpath(blk_device, &real_path)) {
+        real_path = blk_device;
+    }
+
+    // Handle logical volumes.
+    auto& dm = DeviceMapper::Instance();
+    for (;;) {
+        auto parent = dm.GetParentBlockDeviceByPath(real_path);
+        if (!parent.has_value()) break;
+        real_path = *parent;
+    }
+
+    // Now we should have the "real" block device.
+    LOG(INFO) << "IsEmmcStorage(): blk_device = " << blk_device << ", real_path=" << real_path;
+    return StartsWith(Basename(real_path), "mmcblk");
+}
+
+// Retrieve the options to use for encryption policies on the /data filesystem.
+static bool get_data_file_encryption_options(EncryptionOptions* options) {
+    if (fstab_default.empty()) {
+        if (!ReadDefaultFstab(&fstab_default)) {
+            PLOG(ERROR) << "Failed to open default fstab";
+            return false;
+        }
+    }
+    auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
+    if (entry == nullptr) {
+        LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
+        return false;
+    }
+    if (!ParseOptions(entry->encryption_options, options)) {
+        LOG(ERROR) << "Unable to parse encryption options for " << DATA_MNT_POINT ": "
+                   << entry->encryption_options;
+        return false;
+    }
+    if (options->version == 1) {
+        options->use_hw_wrapped_key =
+            GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT)->fs_mgr_flags.wrapped_key;
+    }
+    if ((options->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
+        !IsEmmcStorage(entry->blk_device)) {
+        LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage.  Remove "
+                      "this flag from the device's fstab";
+        return false;
+    }
+    return true;
+}
+
+static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options,
+                                const KeyBuffer& key, EncryptionPolicy* policy) {
+    KeyBuffer ephemeral_wrapped_key;
+    if (options.use_hw_wrapped_key) {
+        if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) {
+            LOG(ERROR) << "Failed to get ephemeral wrapped key";
+            return false;
+        }
+    }
+    return installKey(mountpoint, options, options.use_hw_wrapped_key ? ephemeral_wrapped_key : key,
+                      policy);
+}
+
+// Retrieve the options to use for encryption policies on adoptable storage.
+static bool get_volume_file_encryption_options(EncryptionOptions* options) {
+    // If we give the empty string, libfscrypt will use the default (currently XTS)
+    auto contents_mode = android::base::GetProperty("ro.crypto.volume.contents_mode", "");
+    // HEH as default was always a mistake. Use the libfscrypt default (CTS)
+    // for devices launching on versions above Android 10.
+    auto first_api_level = GetFirstApiLevel();
+    constexpr uint64_t pre_gki_level = 29;
+    auto filenames_mode =
+            android::base::GetProperty("ro.crypto.volume.filenames_mode",
+                                       first_api_level > pre_gki_level ? "" : "aes-256-heh");
+    auto options_string = android::base::GetProperty("ro.crypto.volume.options",
+                                                     contents_mode + ":" + filenames_mode);
+    if (!ParseOptionsForApiLevel(first_api_level, options_string, options)) {
+        LOG(ERROR) << "Unable to parse volume encryption options: " << options_string;
+        return false;
+    }
+    if (options->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
+        LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage.  Remove "
+                      "this flag from ro.crypto.volume.options";
+        return false;
+    }
+    return true;
+}
+
+bool is_metadata_wrapped_key_supported() {
+    if (fstab_default.empty()) {
+        if (!ReadDefaultFstab(&fstab_default)) {
+            PLOG(ERROR) << "Failed to open default fstab";
+            return false;
+        }
+    }
+    return GetEntryForMountPoint(&fstab_default, METADATA_MNT_POINT)->fs_mgr_flags.wrapped_key;
+}
+
+static bool read_and_install_user_ce_key(userid_t user_id,
+                                         const ::KeyAuthentication& auth) {
+    if (s_ce_policies.count(user_id) != 0) return true;
+    EncryptionOptions options;
+    if (!get_data_file_encryption_options(&options)) return false;
+    KeyBuffer ce_key;
+    if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+    EncryptionPolicy ce_policy;
+    if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false;
+    s_ce_policies[user_id] = ce_policy;
+    LOG(INFO) << "Installed ce key for user " << user_id;
+    return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+    LOG(INFO) << "Preparing: " << dir;
+    if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << dir;
+        return false;
+    }
+    return true;
+}
+
+static bool destroy_dir(const std::string& dir) {
+    LOG(INFO) << "Destroying: " << dir;
+    if (rmdir(dir.c_str()) != 0 && errno != ENOENT) {
+        PLOG(ERROR) << "Failed to destroy " << dir;
+        return false;
+    }
+    return true;
+}
+
+// NB this assumes that there is only one thread listening for crypt commands, because
+// it creates keys in a fixed location.
+static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
+    LOG(INFO) << "fscrypt::create_and_install_user_keys";
+    EncryptionOptions options;
+    if (!get_data_file_encryption_options(&options)) return false;
+    KeyBuffer de_key, ce_key;
+    if (!generateStorageKey(makeGen(options), &de_key)) return false;
+    if (!generateStorageKey(makeGen(options), &ce_key)) return false;
+    if (create_ephemeral) {
+        // If the key should be created as ephemeral, don't store it.
+        s_ephemeral_users.insert(user_id);
+    } else {
+        auto const directory_path = get_ce_key_directory_path(user_id);
+        if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
+        auto const paths = get_ce_key_paths(directory_path);
+        std::string ce_key_path;
+        if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
+        if (!::storeKeyAtomically(ce_key_path, user_key_temp, kEmptyAuthentication,
+                                               ce_key))
+            return false;
+        fixate_user_ce_key(directory_path, ce_key_path, paths);
+        // Write DE key second; once this is written, all is good.
+        if (!::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
+                                               kEmptyAuthentication, de_key))
+            return false;
+    }
+    EncryptionPolicy de_policy;
+    if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
+    s_de_policies[user_id] = de_policy;
+    LOG(INFO) << "fscrypt::added de_policy";
+    EncryptionPolicy ce_policy;
+    if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false;
+    s_ce_policies[user_id] = ce_policy;
+    LOG(INFO) << "Created keys for user " << user_id;
+    return true;
+}
+
+bool lookup_policy(const std::map<userid_t, EncryptionPolicy>& key_map, userid_t user_id,
+                          EncryptionPolicy* policy) {
+    auto refi = key_map.find(user_id);
+    if (refi == key_map.end()) {
+        LOG(ERROR) << "Cannot find key for " << user_id;
+        return false;
+    }
+    *policy = refi->second;
+    return true;
+}
+
+static bool is_numeric(const char* name) {
+    for (const char* p = name; *p != '\0'; p++) {
+        if (!isdigit(*p)) return false;
+    }
+    return true;
+}
+
+static bool load_all_de_keys() {
+    LOG(INFO) << "fscrypt::load_all_de_keys";
+    EncryptionOptions options;
+    if (!get_data_file_encryption_options(&options)) return false;
+    auto de_dir = user_key_dir + "/de";
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
+    if (!dirp) {
+        PLOG(ERROR) << "Unable to read de key directory";
+        return false;
+    }
+    for (;;) {
+        errno = 0;
+        auto entry = readdir(dirp.get());
+        if (!entry) {
+            if (errno) {
+                PLOG(ERROR) << "Unable to read de key directory";
+                return false;
+            }
+            break;
+        }
+        if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
+            LOG(INFO) << "Skipping non-de-key " << entry->d_name;
+            continue;
+        }
+        userid_t user_id = std::stoi(entry->d_name);
+        auto key_path = de_dir + "/" + entry->d_name;
+        KeyBuffer de_key;
+        if (!retrieveKey(key_path, kEmptyAuthentication, &de_key, true)) return false;
+        EncryptionPolicy de_policy;
+        if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
+        auto ret = s_de_policies.insert({user_id, de_policy});
+        LOG(INFO) << "fscrypt::load_all_de_keys::s_de_policies::size::" << s_de_policies.size();
+        if (!ret.second && ret.first->second != de_policy) {
+            LOG(INFO) << "DE policy for user" << user_id << " changed";
+            return false;
+        }
+        LOG(INFO) << "Installed de key for user " << user_id;
+        std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
+        property_set(user_prop.c_str(), "0");
+    }
+    // fscrypt:TODO: go through all DE directories, ensure that all user dirs have the
+    // correct policy set on them, and that no rogue ones exist.
+    return true;
+}
+
+// Attempt to reinstall CE keys for users that we think are unlocked.
+static bool try_reload_ce_keys() {
+    for (const auto& it : s_ce_policies) {
+        if (!::reloadKeyFromSessionKeyring(DATA_MNT_POINT, it.second)) {
+            LOG(ERROR) << "Failed to load CE key from session keyring for user " << it.first;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool fscrypt_initialize_systemwide_keys() {
+    LOG(INFO) << "fscrypt_initialize_systemwide_keys";
+
+    EncryptionOptions options;
+    if (!get_data_file_encryption_options(&options)) return false;
+
+    KeyBuffer device_key;
+    if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
+                               makeGen(options), &device_key))
+        return false;
+
+    EncryptionPolicy device_policy;
+    if (!install_storage_key(DATA_MNT_POINT, options, device_key, &device_policy)) return false;
+
+    std::string options_string;
+    if (!OptionsToString(device_policy.options, &options_string)) {
+        LOG(ERROR) << "Unable to serialize options";
+        return false;
+    }
+    std::string options_filename = std::string(DATA_MNT_POINT) + fscrypt_key_mode;
+    if (!::writeStringToFile(options_string, options_filename)) return false;
+
+    std::string ref_filename = std::string(DATA_MNT_POINT) + fscrypt_key_ref;
+    de_key_raw_ref = device_policy.key_raw_ref;
+    if (!::writeStringToFile(device_policy.key_raw_ref, ref_filename)) return false;
+    LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
+
+    KeyBuffer per_boot_key;
+    if (!generateStorageKey(makeGen(options), &per_boot_key)) return false;
+    EncryptionPolicy per_boot_policy;
+    if (!install_storage_key(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) return false;
+    std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
+    if (!::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename))
+        return false;
+    LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename;
+
+    if (!::FsyncDirectory(device_key_dir)) return false;
+    return true;
+}
+
+bool fscrypt_init_user0() {
+    if (fstab_default.empty()) {
+        if (!ReadDefaultFstab(&fstab_default)) {
+            PLOG(ERROR) << "Failed to open default fstab";
+            return -1;
+        }
+    }
+    LOG(INFO) << "fscrypt_init_user0";
+    if (fscrypt_is_native()) {
+        if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
+        if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
+        if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
+        if (!::pathExists(get_de_key_path(0))) {
+            if (!create_and_install_user_keys(0, false)) return false;
+        }
+        // TODO: switch to loading only DE_0 here once framework makes
+        // explicit calls to install DE keys for secondary users
+        if (!load_all_de_keys()) return false;
+    }
+    // We can only safely prepare DE storage here, since CE keys are probably
+    // entangled with user credentials.  The framework will always prepare CE
+    // storage once CE keys are installed.
+    LOG(INFO) << "attempting fscrypt_prepare_user_storage";
+    if (!fscrypt_prepare_user_storage("", 0, 0, android::os::IVold::STORAGE_FLAG_DE)) {
+        LOG(ERROR) << "Failed to prepare user 0 storage";
+        return false;
+    }
+
+    // If this is a non-FBE device that recently left an emulated mode,
+    // restore user data directories to known-good state.
+    if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
+        LOG(INFO) << "unlocking data media";
+        fscrypt_unlock_user_key(0, 0, "!", "!");
+    }
+
+    // In some scenarios (e.g. userspace reboot) we might unmount userdata
+    // without doing a hard reboot. If CE keys were stored in fs keyring then
+    // they will be lost after unmount. Attempt to re-install them.
+    if (fscrypt_is_native() && ::isFsKeyringSupported()) {
+        LOG(INFO) << "reloading ce keys";
+        if (!try_reload_ce_keys()) return false;
+    }
+
+    return true;
+}
+
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
+    LOG(INFO) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial;
+    if (!fscrypt_is_native()) {
+        return true; 
+    }
+    // FIXME test for existence of key that is not loaded yet
+    if (s_ce_policies.count(user_id) != 0) {
+        LOG(ERROR) << "Already exists, can't fscrypt_vold_create_user_key for " << user_id
+                   << " serial " << serial;
+        // FIXME should we fail the command?
+        return true;
+    }
+    if (!create_and_install_user_keys(user_id, ephemeral)) {
+        return false;
+    }
+    return true;
+}
+
+// "Lock" all encrypted directories whose key has been removed.  This is needed
+// in the case where the keys are being put in the session keyring (rather in
+// the newer filesystem-level keyrings), because removing a key from the session
+// keyring doesn't affect inodes in the kernel's inode cache whose per-file key
+// was already set up.  So to remove the per-file keys and make the files
+// "appear encrypted", these inodes must be evicted.
+//
+// To do this, sync() to clean all dirty inodes, then drop all reclaimable slab
+// objects systemwide.  This is overkill, but it's the best available method
+// currently.  Don't use drop_caches mode "3" because that also evicts pagecache
+// for in-use files; all files relevant here are already closed and sync'ed.
+static void drop_caches_if_needed() {
+    if (::isFsKeyringSupported()) {
+        return;
+    }
+    sync();
+    if (!writeStringToFile("2", "/proc/sys/vm/drop_caches")) {
+        PLOG(ERROR) << "Failed to drop caches during key eviction";
+    }
+}
+
+static bool evict_ce_key(userid_t user_id) {
+    bool success = true;
+    EncryptionPolicy policy;
+    // If we haven't loaded the CE key, no need to evict it.
+    if (lookup_policy(s_ce_policies, user_id, &policy)) {
+        success &= ::evictKey(DATA_MNT_POINT, policy);
+        drop_caches_if_needed();
+    }
+    s_ce_policies.erase(user_id);
+    return success;
+}
+
+bool fscrypt_destroy_user_key(userid_t user_id) {
+    LOG(INFO) << "fscrypt_destroy_user_key(" << user_id << ")";
+    if (!fscrypt_is_native()) {
+        return true;
+    }
+    bool success = true;
+    success &= evict_ce_key(user_id);
+    EncryptionPolicy de_policy;
+    success &= lookup_policy(s_de_policies, user_id, &de_policy) &&
+               ::evictKey(DATA_MNT_POINT, de_policy);
+    s_de_policies.erase(user_id);
+    auto it = s_ephemeral_users.find(user_id);
+    if (it != s_ephemeral_users.end()) {
+        s_ephemeral_users.erase(it);
+    } else {
+        for (auto const path : get_ce_key_paths(get_ce_key_directory_path(user_id))) {
+            success &= ::destroyKey(path);
+        }
+        auto de_key_path = get_de_key_path(user_id);
+        if (::pathExists(de_key_path)) {
+            success &= ::destroyKey(de_key_path);
+        } else {
+            LOG(INFO) << "Not present so not erasing: " << de_key_path;
+        }
+    }
+    return success;
+}
+
+static bool emulated_lock(const std::string& path) {
+    if (chmod(path.c_str(), 0000) != 0) {
+        PLOG(ERROR) << "Failed to chmod " << path;
+        return false;
+    }
+#if EMULATED_USES_SELINUX
+    if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) {
+        PLOG(WARNING) << "Failed to setfilecon " << path;
+        return false;
+    }
+#endif
+    return true;
+}
+
+static bool emulated_unlock(const std::string& path, mode_t mode) {
+    if (chmod(path.c_str(), mode) != 0) {
+        PLOG(ERROR) << "Failed to chmod " << path;
+        // FIXME temporary workaround for b/26713622
+        if (fscrypt_is_emulated()) return false;
+    }
+#if EMULATED_USES_SELINUX
+    if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
+        PLOG(WARNING) << "Failed to restorecon " << path;
+        // FIXME temporary workaround for b/26713622
+        if (fscrypt_is_emulated()) return false;
+    }
+#endif
+    return true;
+}
+
+static bool parse_hex(const std::string& hex, std::string* result) {
+    if (hex == "!") {
+        *result = "";
+        return true;
+    }
+    if (::HexToStr(hex, *result) != 0) {
+        LOG(ERROR) << "Invalid FBE hex string";  // Don't log the string for security reasons
+        return false;
+    }
+    return true;
+}
+
+static std::optional<::KeyAuthentication> authentication_from_hex(
+        const std::string& token_hex, const std::string& secret_hex) {
+    std::string token, secret;
+    if (!parse_hex(token_hex, &token)) return std::optional<::KeyAuthentication>();
+    if (!parse_hex(secret_hex, &secret)) return std::optional<::KeyAuthentication>();
+    if (secret.empty()) {
+        return kEmptyAuthentication;
+    } else {
+        return ::KeyAuthentication(token, secret);
+    }
+}
+
+static std::string volkey_path(const std::string& misc_path, const std::string& volume_uuid) {
+    return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
+}
+
+static std::string volume_secdiscardable_path(const std::string& volume_uuid) {
+    return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable";
+}
+
+static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
+                                  EncryptionPolicy* policy) {
+    auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+    std::string secdiscardable_hash;
+    if (::pathExists(secdiscardable_path)) {
+        if (!readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+            return false;
+    } else {
+        if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
+            PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
+            return false;
+        }
+        if (!::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+            return false;
+    }
+    auto key_path = volkey_path(misc_path, volume_uuid);
+    if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
+        PLOG(ERROR) << "Creating directories for: " << key_path;
+        return false;
+    }
+    ::KeyAuthentication auth("", secdiscardable_hash);
+
+    EncryptionOptions options;
+    if (!get_volume_file_encryption_options(&options)) return false;
+    KeyBuffer key;
+    if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key))
+        return false;
+    if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false;
+    return true;
+}
+
+static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) {
+    auto path = volkey_path(misc_path, volume_uuid);
+    if (!::pathExists(path)) return true;
+    return ::destroyKey(path);
+}
+
+static bool fscrypt_rewrap_user_key(userid_t user_id, int serial,
+                                    const ::KeyAuthentication& retrieve_auth,
+                                    const ::KeyAuthentication& store_auth) {
+    if (s_ephemeral_users.count(user_id) != 0) return true;
+    auto const directory_path = get_ce_key_directory_path(user_id);
+    KeyBuffer ce_key;
+    std::string ce_key_current_path = get_ce_key_current_path(directory_path);
+    if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key, true)) {
+        LOG(INFO) << "Successfully retrieved key";
+        // TODO(147732812): Remove this once Locksettingservice is fixed.
+        // Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is
+        // changed from swipe to none or vice-versa
+    } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key, true)) {
+        LOG(INFO) << "Successfully retrieved key with empty auth";
+    } else {
+        LOG(ERROR) << "Failed to retrieve key for user " << user_id;
+        return false;
+    }
+    auto const paths = get_ce_key_paths(directory_path);
+    std::string ce_key_path;
+    if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
+    if (!::storeKeyAtomically(ce_key_path, user_key_temp, store_auth, ce_key))
+        return false;
+    if (!::FsyncDirectory(directory_path)) return false;
+    return true;
+}
+
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+                               const std::string& secret_hex) {
+    LOG(INFO) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial
+               << " token_present=" << (token_hex != "!");
+    if (!fscrypt_is_native()) return true;
+    auto auth = authentication_from_hex(token_hex, secret_hex);
+    if (!auth) return false;
+    return fscrypt_rewrap_user_key(user_id, serial, kEmptyAuthentication, *auth);
+}
+
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+                                 const std::string& secret_hex) {
+    LOG(INFO) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial
+               << " token_present=" << (token_hex != "!");
+    if (!fscrypt_is_native()) return true;
+    auto auth = authentication_from_hex(token_hex, secret_hex);
+    if (!auth) return false;
+    return fscrypt_rewrap_user_key(user_id, serial, *auth, kEmptyAuthentication);
+}
+
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
+    LOG(INFO) << "fscrypt_fixate_newest_user_key_auth " << user_id;
+    if (!fscrypt_is_native()) return true;
+    if (s_ephemeral_users.count(user_id) != 0) return true;
+    auto const directory_path = get_ce_key_directory_path(user_id);
+    auto const paths = get_ce_key_paths(directory_path);
+    if (paths.empty()) {
+        LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id;
+        return false;
+    }
+    fixate_user_ce_key(directory_path, paths[0], paths);
+    return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
+                             const std::string& secret_hex) {
+    LOG(INFO) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial
+               << " token_present=" << (token_hex != "!");
+    if (fscrypt_is_native()) {
+        if (s_ce_policies.count(user_id) != 0) {
+            LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
+            return true;
+        }
+        auto auth = authentication_from_hex(token_hex, secret_hex);
+        if (!auth) return false;
+        if (!read_and_install_user_ce_key(user_id, *auth)) {
+            LOG(ERROR) << "Couldn't read key for " << user_id;
+            return false;
+        }
+    } else {
+        // When in emulation mode, we just use chmod. However, we also
+        // unlock directories when not in emulation mode, to bring devices
+        // back into a known-good state.
+        if (!emulated_unlock(::BuildDataSystemCePath(user_id), 0771) ||
+            !emulated_unlock(::BuildDataMiscCePath(user_id), 01771) ||
+            !emulated_unlock(::BuildDataMediaCePath("", user_id), 0770) ||
+            !emulated_unlock(::BuildDataUserCePath("", user_id), 0771)) {
+            LOG(ERROR) << "Failed to unlock user " << user_id;
+            return false;
+        }
+    }
+    return true;
+}
+
+// TODO: rename to 'evict' for consistency
+bool fscrypt_lock_user_key(userid_t user_id) {
+    LOG(INFO) << "fscrypt_lock_user_key " << user_id;
+    if (fscrypt_is_native()) {
+        return evict_ce_key(user_id);
+    } else if (fscrypt_is_emulated()) {
+        // When in emulation mode, we just use chmod
+        if (!emulated_lock(::BuildDataSystemCePath(user_id)) ||
+            !emulated_lock(::BuildDataMiscCePath(user_id)) ||
+            !emulated_lock(::BuildDataMediaCePath("", user_id)) ||
+            !emulated_lock(::BuildDataUserCePath("", user_id))) {
+            LOG(ERROR) << "Failed to lock user " << user_id;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid,
+                            userid_t user_id, int flags) {
+    if (0 != ::ForkExecvp(
+                 std::vector<std::string>{prepare_subdirs_path, action, volume_uuid,
+                                          std::to_string(user_id), std::to_string(flags)})) {
+        LOG(ERROR) << "vold_prepare_subdirs failed";
+        return false;
+    }
+    return true;
+}
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+                                  int flags) {
+    LOG(INFO) << "fscrypt_prepare_user_storage for volume " << escape_empty(volume_uuid)
+               << ", user " << user_id << ", serial " << serial << ", flags " << flags;
+
+    if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+        // DE_sys key
+        auto system_legacy_path = ::BuildDataSystemLegacyPath(user_id);
+        auto misc_legacy_path = ::BuildDataMiscLegacyPath(user_id);
+        auto profiles_de_path = ::BuildDataProfilesDePath(user_id);
+
+        // DE_n key
+        auto system_de_path = ::BuildDataSystemDePath(user_id);
+        auto misc_de_path = ::BuildDataMiscDePath(user_id);
+        auto vendor_de_path = ::BuildDataVendorDePath(user_id);
+        auto user_de_path = ::BuildDataUserDePath(volume_uuid, user_id);
+
+        if (volume_uuid.empty()) {
+            if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+#if MANAGE_MISC_DIRS
+            if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
+                             multiuser_get_uid(user_id, AID_EVERYBODY)))
+                return false;
+#endif
+            if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+            if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+            if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+            if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false;
+        }
+        if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+        if (fscrypt_is_native()) {
+            EncryptionPolicy de_policy;
+            if (volume_uuid.empty()) {
+                if (!lookup_policy(s_de_policies, user_id, &de_policy)) return false;
+                if (!EnsurePolicy(de_policy, system_de_path)) return false;
+                if (!EnsurePolicy(de_policy, misc_de_path)) return false;
+                if (!EnsurePolicy(de_policy, vendor_de_path)) return false;
+            } else {
+                if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_policy)) return false;
+            }
+            if (!EnsurePolicy(de_policy, user_de_path)) return false;
+        }
+    }
+
+    if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+        // CE_n key
+        auto system_ce_path = ::BuildDataSystemCePath(user_id);
+        auto misc_ce_path = ::BuildDataMiscCePath(user_id);
+        auto vendor_ce_path = ::BuildDataVendorCePath(user_id);
+        auto media_ce_path = ::BuildDataMediaCePath(volume_uuid, user_id);
+        auto user_ce_path = ::BuildDataUserCePath(volume_uuid, user_id);
+
+        if (volume_uuid.empty()) {
+            if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+            if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+            if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
+        }
+        if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+
+        if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+        if (fscrypt_is_native()) {
+            EncryptionPolicy ce_policy;
+            if (volume_uuid.empty()) {
+                if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) return false;
+                if (!EnsurePolicy(ce_policy, system_ce_path)) return false;
+                if (!EnsurePolicy(ce_policy, misc_ce_path)) return false;
+                if (!EnsurePolicy(ce_policy, vendor_ce_path)) return false;
+            } else {
+                if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_policy)) return false;
+            }
+            if (!EnsurePolicy(ce_policy, media_ce_path)) return false;
+            if (!EnsurePolicy(ce_policy, user_ce_path)) return false;
+        }
+
+        if (volume_uuid.empty()) {
+            // Now that credentials have been installed, we can run restorecon
+            // over these paths
+            // NOTE: these paths need to be kept in sync with libselinux
+            ::RestoreconRecursive(system_ce_path);
+            ::RestoreconRecursive(vendor_ce_path);
+            ::RestoreconRecursive(misc_ce_path);
+        }
+    }
+    if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false;
+
+    return true;
+}
+
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) {
+    LOG(INFO) << "fscrypt_destroy_user_storage for volume " << escape_empty(volume_uuid)
+               << ", user " << user_id << ", flags " << flags;
+    bool res = true;
+
+    res &= prepare_subdirs("destroy", volume_uuid, user_id, flags);
+
+    if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+        // CE_n key
+        auto system_ce_path = ::BuildDataSystemCePath(user_id);
+        auto misc_ce_path = ::BuildDataMiscCePath(user_id);
+        auto vendor_ce_path = ::BuildDataVendorCePath(user_id);
+        auto media_ce_path = ::BuildDataMediaCePath(volume_uuid, user_id);
+        auto user_ce_path = ::BuildDataUserCePath(volume_uuid, user_id);
+
+        res &= destroy_dir(media_ce_path);
+        res &= destroy_dir(user_ce_path);
+        if (volume_uuid.empty()) {
+            res &= destroy_dir(system_ce_path);
+            res &= destroy_dir(misc_ce_path);
+            res &= destroy_dir(vendor_ce_path);
+        } else {
+            if (fscrypt_is_native()) {
+                res &= destroy_volkey(misc_ce_path, volume_uuid);
+            }
+        }
+    }
+
+    if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+        // DE_sys key
+        auto system_legacy_path = ::BuildDataSystemLegacyPath(user_id);
+        auto misc_legacy_path = ::BuildDataMiscLegacyPath(user_id);
+        auto profiles_de_path = ::BuildDataProfilesDePath(user_id);
+
+        // DE_n key
+        auto system_de_path = ::BuildDataSystemDePath(user_id);
+        auto misc_de_path = ::BuildDataMiscDePath(user_id);
+        auto vendor_de_path = ::BuildDataVendorDePath(user_id);
+        auto user_de_path = ::BuildDataUserDePath(volume_uuid, user_id);
+
+        res &= destroy_dir(user_de_path);
+        if (volume_uuid.empty()) {
+            res &= destroy_dir(system_legacy_path);
+#if MANAGE_MISC_DIRS
+            res &= destroy_dir(misc_legacy_path);
+#endif
+            res &= destroy_dir(profiles_de_path);
+            res &= destroy_dir(system_de_path);
+            res &= destroy_dir(misc_de_path);
+            res &= destroy_dir(vendor_de_path);
+        } else {
+            if (fscrypt_is_native()) {
+                res &= destroy_volkey(misc_de_path, volume_uuid);
+            }
+        }
+    }
+
+    return res;
+}
+
+static bool destroy_volume_keys(const std::string& directory_path, const std::string& volume_uuid) {
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+    if (!dirp) {
+        PLOG(ERROR) << "Unable to open directory: " + directory_path;
+        return false;
+    }
+    bool res = true;
+    for (;;) {
+        errno = 0;
+        auto const entry = readdir(dirp.get());
+        if (!entry) {
+            if (errno) {
+                PLOG(ERROR) << "Unable to read directory: " + directory_path;
+                return false;
+            }
+            break;
+        }
+        if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
+            LOG(INFO) << "Skipping non-user " << entry->d_name;
+            continue;
+        }
+        res &= destroy_volkey(directory_path + "/" + entry->d_name, volume_uuid);
+    }
+    return res;
+}
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid) {
+    bool res = true;
+    LOG(INFO) << "fscrypt_destroy_volume_keys for volume " << escape_empty(volume_uuid);
+    auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+    res &= ::runSecdiscardSingle(secdiscardable_path);
+    res &= destroy_volume_keys("/data/misc_ce", volume_uuid);
+    res &= destroy_volume_keys("/data/misc_de", volume_uuid);
+    return res;
+}
+
+bool lookup_key_ref(const std::map<userid_t, android::fscrypt::EncryptionPolicy>& key_map, userid_t user_id,
+                           std::string* raw_ref) {
+    auto refi = key_map.find(user_id);
+    if (refi == key_map.end()) {
+        LOG(ERROR) << "Cannot find key for " << user_id;
+        return false;
+    }
+    *raw_ref = refi->second.key_raw_ref;
+    return true;
+}
diff --git a/crypto/fscrypt/FsCrypt.h b/crypto/fscrypt/FsCrypt.h
new file mode 100755
index 0000000..0cb38c1
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <string>
+
+#include <fscrypt/fscrypt.h>
+#include <cutils/multiuser.h>
+
+using namespace android::fscrypt;
+
+bool fscrypt_initialize_systemwide_keys();
+
+bool fscrypt_init_user0();
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+bool fscrypt_destroy_user_key(userid_t user_id);
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
+                               const std::string& secret);
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token,
+                                 const std::string& secret);
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
+
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+                             const std::string& secret);
+bool fscrypt_lock_user_key(userid_t user_id);
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+                                  int flags);
+bool is_metadata_wrapped_key_supported();
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid);
+
+bool lookup_key_ref(const std::map<userid_t, android::fscrypt::EncryptionPolicy>& key_map, userid_t user_id,
+                           std::string* raw_ref);
+bool lookup_policy(const std::map<userid_t, EncryptionPolicy>& key_map, userid_t user_id,
+                          EncryptionPolicy* policy);
\ No newline at end of file
diff --git a/crypto/fscrypt/HashPassword.cpp b/crypto/fscrypt/HashPassword.cpp
new file mode 100644
index 0000000..07ecb1f
--- /dev/null
+++ b/crypto/fscrypt/HashPassword.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This computes the "secret" used by Android as one of the parameters
+ * to decrypt File Based Encryption. The secret is prefixed with
+ * "Android FBE credential hash" padded with 0s to 128 bytes then the
+ * user's password is appended to the end of the 128 bytes. This string
+ * is then hashed with sha512 and the sha512 value is then converted to
+ * hex with upper-case characters.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#include "HashPassword.h"
+
+#define PASS_PADDING_SIZE 128
+#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
+#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+	size_t size = PASS_PADDING_SIZE + key_size;
+	unsigned char* buffer = (unsigned char*)calloc(1, size);
+	if (!buffer) return NULL; // failed to malloc
+	memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+	unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+	memcpy((void*)ptr, key, key_size);
+	unsigned char hash[SHA512_DIGEST_LENGTH];
+	SHA512_CTX sha512;
+	SHA512_Init(&sha512);
+	SHA512_Update(&sha512, buffer, size);
+	SHA512_Final(hash, &sha512);
+	free(buffer);
+	void* ret = malloc(SHA512_DIGEST_LENGTH);
+	if (!ret) return NULL; // failed to malloc
+	memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+	return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+	size_t size = PASS_PADDING_SIZE + key_size;
+	unsigned char* buffer = (unsigned char*)calloc(1, size);
+	if (!buffer) return ""; // failed to malloc
+	memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+	unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+	memcpy((void*)ptr, key, key_size);
+	unsigned char hash[SHA512_DIGEST_LENGTH];
+	SHA512_CTX sha512;
+	SHA512_Init(&sha512);
+	SHA512_Update(&sha512, buffer, size);
+	SHA512_Final(hash, &sha512);
+	int index = 0;
+	char hex_hash[SHA512_HEX_SIZE + 1];
+	for(index = 0; index < SHA512_DIGEST_LENGTH; index++)
+		sprintf(hex_hash + (index * 2), "%02X", hash[index]);
+	hex_hash[128] = 0;
+	std::string ret = hex_hash;
+	free(buffer);
+	return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+	return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) {
+	HMAC_CTX ctx;
+	HMAC_CTX_init(&ctx);
+	HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
+	unsigned int counter = 1;
+	endianswap(&counter);
+	HMAC_Update(&ctx, (const unsigned char*)&counter, 4);
+	HMAC_Update(&ctx, (const unsigned char*)label, strlen(label));
+	const unsigned char divider = 0;
+	HMAC_Update(&ctx, &divider, 1);
+	HMAC_Update(&ctx, (const unsigned char*)context, strlen(context));
+	unsigned int contextDisambiguation = strlen(context) * 8;
+	endianswap(&contextDisambiguation);
+	HMAC_Update(&ctx, (const unsigned char*)&contextDisambiguation, 4);
+	unsigned int finalValue = 256;
+	endianswap(&finalValue);
+	HMAC_Update(&ctx, (const unsigned char*)&finalValue, 4);
+
+	unsigned char output[SHA256_DIGEST_LENGTH];
+	unsigned int out_size = 0;
+	HMAC_Final(&ctx, output, &out_size);
+
+	int index = 0;
+	char hex_hash[SHA256_HEX_SIZE + 1];
+	for(index = 0; index < SHA256_DIGEST_LENGTH; index++)
+		sprintf(hex_hash + (index * 2), "%02x", output[index]);
+	hex_hash[SHA256_HEX_SIZE] = 0;
+	std::string ret = hex_hash;
+	return ret;
+}
+
+std::string HashPassword(const std::string& Password) {
+	const char* prefix = FBE_PERSONALIZATION;
+	return PersonalizedHash(prefix, Password);
+}
diff --git a/crypto/fscrypt/HashPassword.h b/crypto/fscrypt/HashPassword.h
new file mode 100644
index 0000000..73880b1
--- /dev/null
+++ b/crypto/fscrypt/HashPassword.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HASH_PASSWORD_H
+#define __HASH_PASSWORD_H
+
+#include <string>
+
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication"
+#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform"
+#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context"
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
+std::string PersonalizedHash(const char* prefix, const std::string& Password);
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size);
+std::string HashPassword(const std::string& Password);
+
+template <class T>
+void endianswap(T *objp) {
+	unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
+	std::reverse(memp, memp + sizeof(T));
+}
+
+#endif
diff --git a/crypto/fscrypt/KeyBuffer.cpp b/crypto/fscrypt/KeyBuffer.cpp
new file mode 100644
index 0000000..e7aede5
--- /dev/null
+++ b/crypto/fscrypt/KeyBuffer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace android {
+namespace vold {
+
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
+    std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
+    return std::move(lhs);
+}
+
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
+    std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
+    return std::move(lhs);
+}
+
+}  // namespace vold
+}  // namespace android
+
diff --git a/crypto/fscrypt/KeyBuffer.h b/crypto/fscrypt/KeyBuffer.h
new file mode 100644
index 0000000..2de9ac9
--- /dev/null
+++ b/crypto/fscrypt/KeyBuffer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYBUFFER_H
+#define ANDROID_VOLD_KEYBUFFER_H
+
+#include <cstring>
+#include <memory>
+#include <vector>
+
+
+/**
+ * Variant of memset() that should never be optimized away. Borrowed from keymaster code.
+ */
+#ifdef __clang__
+#define OPTNONE __attribute__((optnone))
+#else  // not __clang__
+#define OPTNONE __attribute__((optimize("O0")))
+#endif  // not __clang__
+inline OPTNONE void* memset_s(void* s, int c, size_t n) {
+    if (!s)
+        return s;
+    return memset(s, c, n);
+}
+#undef OPTNONE
+
+// Allocator that delegates useful work to standard one but zeroes data before deallocating.
+class ZeroingAllocator : public std::allocator<char> {
+    public:
+    void deallocate(pointer p, size_type n)
+    {
+        memset_s(p, 0, n);
+        std::allocator<char>::deallocate(p, n);
+    }
+};
+
+// Char vector that zeroes memory when deallocating.
+using KeyBuffer = std::vector<char, ZeroingAllocator>;
+
+// Convenience methods to concatenate key buffers.
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);
+
+#endif
+
diff --git a/crypto/fscrypt/KeyStorage.cpp b/crypto/fscrypt/KeyStorage.cpp
new file mode 100755
index 0000000..edb23a2
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.cpp
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage.h"
+
+#include "Checkpoint.h"
+#include "Keymaster.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <thread>
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30;  // Seconds
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+    if (actual != expected) {
+        LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+                   << actual;
+        return false;
+    }
+    return true;
+}
+
+static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
+    SHA512_CTX c;
+
+    SHA512_Init(&c);
+    // Personalise the hashing by introducing a fixed prefix.
+    // Hashing applications should use personalization except when there is a
+    // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+    std::string hashingPrefix = prefix;
+    hashingPrefix.resize(SHA512_CBLOCK);
+    SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+    SHA512_Update(&c, tohash.data(), tohash.size());
+    res->assign(SHA512_DIGEST_LENGTH, '\0');
+    SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
+}
+
+static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+                                 const std::string& appId, std::string* key) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                            .AesEncryptionKey(AES_KEY_BYTES * 8)
+                            .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+    if (auth.token.empty()) {
+        LOG(DEBUG) << "Creating key that doesn't need auth token";
+        paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
+    } else {
+        LOG(DEBUG) << "Auth token required for key";
+        if (auth.token.size() != sizeof(hw_auth_token_t)) {
+            LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+                       << auth.token.size() << " bytes";
+            return false;
+        }
+        const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+        auto user_id = at->user_id;  // Make a copy because at->user_id is unaligned.
+        paramBuilder.Authorization(km::TAG_USER_SECURE_ID, user_id);
+        paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
+        paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+    }
+
+    auto paramsWithRollback = paramBuilder;
+    paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
+
+    // Generate rollback-resistant key if possible.
+    return keymaster.generateKey(paramsWithRollback, key) ||
+           keymaster.generateKey(paramBuilder, key);
+}
+
+bool generateWrappedStorageKey(KeyBuffer* key) {
+    Keymaster keymaster;
+    if (!keymaster) return false;
+    std::string key_temp;
+    auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
+    km::KeyParameter param1;
+    param1.tag = static_cast<::android::hardware::keymaster::V4_0::Tag>(
+        android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
+    param1.f.boolValue = true;
+    paramBuilder.push_back(param1);
+    if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+    *key = KeyBuffer(key_temp.size());
+    memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+    return true;
+}
+
+bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key) {
+    Keymaster keymaster;
+    if (!keymaster) return false;
+    std::string key_temp;
+
+    auto ret = keymaster.exportKey(kmKey, &key_temp);
+    if (ret != km::ErrorCode::OK) {
+        if (ret == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+           std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
+           std::string Keystr;
+           if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &Keystr)) return false;
+           KeyBuffer upgradedKey = KeyBuffer(Keystr.size());
+           memcpy(reinterpret_cast<void*>(upgradedKey.data()), Keystr.c_str(), upgradedKey.size());
+           ret = keymaster.exportKey(upgradedKey, &key_temp);
+           if (ret != km::ErrorCode::OK) return false;
+        } else {
+           return false;
+        }
+    }    *key = KeyBuffer(key_temp.size());
+    memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+    return true;
+}
+
+static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
+    const KeyAuthentication& auth, const std::string& appId) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                            .GcmModeMacLen(GCM_MAC_BYTES * 8)
+                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+    km::HardwareAuthToken authToken;
+    if (!auth.token.empty()) {
+        LOG(INFO) << "Supplying auth token to Keymaster";
+        authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
+    }
+    return {paramBuilder, authToken};
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+    if (!android::base::ReadFileToString(filename, result)) {
+        PLOG(ERROR) << "Failed to read from " << filename;
+        return false;
+    }
+    return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+    auto status = ReadRandomBytes(count, *out);
+    if (status != android::OK) {
+        LOG(ERROR) << "Random read failed with status: " << status;
+        return false;
+    }
+    return true;
+}
+
+bool createSecdiscardable(const std::string& filename, std::string* hash) {
+    std::string secdiscardable;
+    if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+    if (!writeStringToFile(secdiscardable, filename)) return false;
+    hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    return true;
+}
+
+bool readSecdiscardable(const std::string& filename, std::string* hash) {
+    std::string secdiscardable;
+    if (!readFileToString(filename, &secdiscardable)) return false;
+    hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    return true;
+}
+
+static void deferedKmDeleteKey(const std::string& kmkey) {
+    while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+        LOG(ERROR) << "Wait for boot timed out";
+    }
+    Keymaster keymaster;
+    if (!keymaster || !keymaster.deleteKey(kmkey)) {
+        LOG(ERROR) << "Defered Key deletion failed during upgrade";
+    }
+}
+
+bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
+    bool needs_cp = cp_needsCheckpoint();
+
+    if (needs_cp) {
+        std::thread(deferedKmDeleteKey, kmKey).detach();
+        LOG(INFO) << "Deferring Key deletion during upgrade";
+        return true;
+    } else {
+        return keymaster.deleteKey(kmKey);
+    }
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+                                km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
+                                const km::AuthorizationSet& opParams,
+                                const km::HardwareAuthToken& authToken,
+                                km::AuthorizationSet* outParams, bool keepOld) {
+    auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+    std::string kmKey;
+    if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+    km::AuthorizationSet inParams(keyParams);
+    inParams.append(opParams.begin(), opParams.end());
+    for (;;) {
+        auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
+        if (opHandle) {
+            return opHandle;
+        }
+        if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+        LOG(DEBUG) << "Upgrading key: " << dir;
+        std::string newKey;
+        if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+        auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+        if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+        if (!keepOld) {
+            if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+                PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+                return KeymasterOperation();
+            }
+            if (!::FsyncDirectory(dir)) {
+                LOG(ERROR) << "Key dir sync failed: " << dir;
+                return KeymasterOperation();
+            }
+            if (!kmDeleteKey(keymaster, kmKey)) {
+                LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+            }
+        }
+        kmKey = newKey;
+        LOG(INFO) << "Key upgraded: " << dir;
+    }
+}
+
+static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+                                    const km::AuthorizationSet& keyParams,
+                                    const km::HardwareAuthToken& authToken, const KeyBuffer& message,
+                                    std::string* ciphertext, bool keepOld) {
+    km::AuthorizationSet opParams;
+    km::AuthorizationSet outParams;
+    auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
+                          &outParams, keepOld);
+    if (!opHandle) return false;
+    auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
+    if (!nonceBlob.isOk()) {
+        LOG(ERROR) << "GCM encryption but no nonce generated";
+        return false;
+    }
+    // nonceBlob here is just a pointer into existing data, must not be freed
+    std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
+                      nonceBlob.value().size());
+    if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+    std::string body;
+    if (!opHandle.updateCompletely(message, &body)) return false;
+
+    std::string mac;
+    if (!opHandle.finish(&mac)) return false;
+    if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+    *ciphertext = nonce + body + mac;
+    return true;
+}
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+                                    const km::AuthorizationSet& keyParams,
+                                    const km::HardwareAuthToken& authToken,
+                                    const std::string& ciphertext, KeyBuffer* message,
+                                    bool keepOld) {
+    auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+    auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+    auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
+                                                                km::support::blob2hidlVec(nonce));
+    auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
+                          nullptr, keepOld);
+    if (!opHandle) return false;
+    if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+    if (!opHandle.finish(nullptr)) return false;
+    return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+    if (!auth.usesKeymaster()) {
+        return kStretch_none;
+    } else if (auth.secret.empty()) {
+        return kStretch_nopassword;
+    } else {
+        char paramstr[PROPERTY_VALUE_MAX];
+
+        property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+        return std::string() + kStretchPrefix_scrypt + paramstr;
+    }
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+    return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+                          const std::string& salt, std::string* stretched) {
+    if (stretching == kStretch_nopassword) {
+        if (!secret.empty()) {
+            LOG(WARNING) << "Password present but stretching is nopassword";
+            // Continue anyway
+        }
+        stretched->clear();
+    } else if (stretching == kStretch_none) {
+        *stretched = secret;
+    } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+                          stretching.begin())) {
+        int Nf, rf, pf;
+        if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+                                     &rf, &pf)) {
+            LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+            return false;
+        }
+        stretched->assign(STRETCHED_BYTES, '\0');
+        if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+                          reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
+                          1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
+                          stretched->size()) != 0) {
+            LOG(ERROR) << "scrypt failed with params: " << stretching;
+            return false;
+        }
+    } else {
+        LOG(ERROR) << "Unknown stretching type: " << stretching;
+        return false;
+    }
+    return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+                          const std::string& salt, const std::string& secdiscardable_hash,
+                          std::string* appId) {
+    std::string stretched;
+    if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+    *appId = secdiscardable_hash + stretched;
+    return true;
+}
+
+static void logOpensslError() {
+    LOG(ERROR) << "Openssl error: " << ERR_get_error();
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
+                                    std::string* ciphertext) {
+    std::string key;
+    hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+    key.resize(AES_KEY_BYTES);
+    if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+                                reinterpret_cast<const uint8_t*>(key.data()),
+                                reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+        logOpensslError();
+        return false;
+    }
+    ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_EncryptUpdate(
+                 ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
+                 &outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext.size())) {
+        LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
+        return false;
+    }
+    if (1 != EVP_EncryptFinal_ex(
+                 ctx.get(),
+                 reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
+                 &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+                                 reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
+                                                            plaintext.size()))) {
+        logOpensslError();
+        return false;
+    }
+    return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
+                                    KeyBuffer* plaintext) {
+    if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+        LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+        return false;
+    }
+    std::string key;
+    hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+    key.resize(AES_KEY_BYTES);
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+                                reinterpret_cast<const uint8_t*>(key.data()),
+                                reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+        logOpensslError();
+        return false;
+    }
+    *plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+                               reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
+                               plaintext->size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext->size())) {
+        LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+                                 const_cast<void*>(reinterpret_cast<const void*>(
+                                     ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptFinal_ex(ctx.get(),
+                                 reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
+                                 &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+        return false;
+    }
+    return true;
+}
+
+bool pathExists(const std::string& path) {
+    return access(path.c_str(), F_OK) == 0;
+}
+
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
+    if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+        PLOG(ERROR) << "key mkdir " << dir;
+        return false;
+    }
+    if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+    std::string secdiscardable_hash;
+    if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+    std::string stretching = getStretching(auth);
+    if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+    std::string salt;
+    if (stretchingNeedsSalt(stretching)) {
+        if (ReadRandomBytes(SALT_BYTES, salt) != android::OK) {
+            LOG(ERROR) << "Random read failed";
+            return false;
+        }
+        if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+    }
+    std::string appId;
+    if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+    std::string encryptedKey;
+    if (auth.usesKeymaster()) {
+        Keymaster keymaster;
+        if (!keymaster) return false;
+        std::string kmKey;
+        if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+        if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+        km::AuthorizationSet keyParams;
+        km::HardwareAuthToken authToken;
+        std::tie(keyParams, authToken) = beginParams(auth, appId);
+        if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
+                                     false))
+            return false;
+    } else {
+        if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+    }
+    if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+    if (!FsyncDirectory(dir)) return false;
+    return true;
+}
+
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+                        const KeyAuthentication& auth, const KeyBuffer& key) {
+    if (pathExists(key_path)) {
+        LOG(ERROR) << "Already exists, cannot create key at: " << key_path;
+        return false;
+    }
+    if (pathExists(tmp_path)) {
+        LOG(DEBUG) << "Already exists, destroying: " << tmp_path;
+        destroyKey(tmp_path);  // May be partially created so ignore errors
+    }
+    if (!::storeKey(tmp_path, auth, key)) return false;
+    if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
+        PLOG(ERROR) << "Unable to move new key to location: " << key_path;
+        return false;
+    }
+    LOG(DEBUG) << "Created key: " << key_path;
+    return true;
+}
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
+                 bool keepOld) {
+    std::string version;
+    if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+    if (version != kCurrentVersion) {
+        LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+        return false;
+    }
+    std::string secdiscardable_hash;
+    if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+    std::string stretching;
+    if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+    std::string salt;
+    if (stretchingNeedsSalt(stretching)) {
+        if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+    }
+    std::string appId;
+    if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+    std::string encryptedMessage;
+    if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+    if (auth.usesKeymaster()) {
+        Keymaster keymaster;
+        if (!keymaster) return false;
+        km::AuthorizationSet keyParams;
+        km::HardwareAuthToken authToken;
+        std::tie(keyParams, authToken) = beginParams(auth, appId);
+        if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
+                                     keepOld))
+            return false;
+    } else {
+        if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+    }
+    return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+    std::string kmKey;
+    if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+    Keymaster keymaster;
+    if (!keymaster) return false;
+    if (!keymaster.deleteKey(kmKey)) return false;
+    return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+    if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
+        LOG(ERROR) << "secdiscard failed";
+        return false;
+    }
+    return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+    if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+        LOG(ERROR) << "recursive delete failed";
+        return false;
+    }
+    return true;
+}
+
+bool destroyKey(const std::string& dir) {
+    bool success = true;
+    // Try each thing, even if previous things failed.
+    bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
+    if (uses_km) {
+        success &= deleteKey(dir);
+    }
+    auto secdiscard_cmd = std::vector<std::string>{
+        kSecdiscardPath,
+        "--",
+        dir + "/" + kFn_encrypted_key,
+        dir + "/" + kFn_secdiscardable,
+    };
+    if (uses_km) {
+        secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+    }
+    if (ForkExecvp(secdiscard_cmd) != 0) {
+        LOG(ERROR) << "secdiscard failed";
+        success = false;
+    }
+    success &= recursiveDeleteKey(dir);
+    return success;
+}
diff --git a/crypto/fscrypt/KeyStorage.h b/crypto/fscrypt/KeyStorage.h
new file mode 100755
index 0000000..2c88820
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include "KeyBuffer.h"
+
+#include <string>
+
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+  public:
+    KeyAuthentication(const std::string& t, const std::string& s) : token{t}, secret{s} {};
+
+    bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+    const std::string token;
+    const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Checks if path "path" exists.
+bool pathExists(const std::string& path);
+
+bool createSecdiscardable(const std::string& path, std::string* hash);
+bool readSecdiscardable(const std::string& path, std::string* hash);
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Create a directory at the named path, and store "key" in it as storeKey
+// This version creates the key in "tmp_path" then atomically renames "tmp_path"
+// to "key_path" thereby ensuring that the key is either stored entirely or
+// not at all.
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+                        const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
+                 bool keepOld);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+bool runSecdiscardSingle(const std::string& file);
+
+// Generate wrapped storage key using keymaster. Uses STORAGE_KEY tag in keymaster.
+bool generateWrappedStorageKey(KeyBuffer* key);
+// Export the per-boot boot wrapped storage key using keymaster.
+bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key);
+
+#endif
diff --git a/crypto/fscrypt/KeyUtil.cpp b/crypto/fscrypt/KeyUtil.cpp
new file mode 100755
index 0000000..b7119a6
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.cpp
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyUtil.h"
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include <fcntl.h>
+#include <linux/fscrypt.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <keyutils.h>
+
+#include <fscrypt_uapi.h>
+#include "FsCrypt.h"
+#include "KeyStorage.h"
+#include "Utils.h"
+
+
+const KeyGeneration neverGen() {
+    return KeyGeneration{0, false, false};
+}
+
+static bool randomKey(size_t size, KeyBuffer* key) {
+    *key = KeyBuffer(size);
+    if (ReadRandomBytes(key->size(), key->data()) != 0) {
+        // TODO status_t plays badly with PLOG, fix it.
+        LOG(ERROR) << "Random read failed";
+        return false;
+    }
+    return true;
+}
+
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key) {
+    if (!gen.allow_gen) return false;
+    if (gen.use_hw_wrapped_key) {
+        if (gen.keysize != FSCRYPT_MAX_KEY_SIZE) {
+            LOG(ERROR) << "Cannot generate a wrapped key " << gen.keysize << " bytes long";
+            return false;
+        }
+        return generateWrappedStorageKey(key);
+    } else {
+        return randomKey(gen.keysize, key);
+    }
+}
+
+// Return true if the kernel supports the ioctls to add/remove fscrypt keys
+// directly to/from the filesystem.
+bool isFsKeyringSupported(void) {
+    static bool initialized = false;
+    static bool supported;
+
+    if (!initialized) {
+        android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+
+        // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
+        // if the ioctl isn't supported.  Otherwise it will fail with another
+        // error code such as EFAULT.
+        errno = 0;
+        (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
+        if (errno == ENOTTY) {
+            LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY.  Falling back to "
+                         "session keyring";
+            supported = false;
+        } else {
+            if (errno != EFAULT) {
+                PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
+            }
+            LOG(INFO) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
+            supported = true;
+            android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
+        }
+        // There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
+        // guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is.  There's
+        // also no need to check for support on external volumes separately from
+        // /data, since either the kernel supports the ioctls on all
+        // fscrypt-capable filesystems or it doesn't.
+
+        initialized = true;
+    }
+    return supported;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generateKeyRef(const uint8_t* key, int length) {
+    SHA512_CTX c;
+
+    SHA512_Init(&c);
+    SHA512_Update(&c, key, length);
+    unsigned char key_ref1[SHA512_DIGEST_LENGTH];
+    SHA512_Final(key_ref1, &c);
+
+    SHA512_Init(&c);
+    SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
+    unsigned char key_ref2[SHA512_DIGEST_LENGTH];
+    SHA512_Final(key_ref2, &c);
+
+    static_assert(FSCRYPT_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
+                  "Hash too short for descriptor");
+    return std::string((char*)key_ref2, FSCRYPT_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
+    if (key.size() != FSCRYPT_MAX_KEY_SIZE) {
+        LOG(ERROR) << "Wrong size key " << key.size();
+        return false;
+    }
+    static_assert(FSCRYPT_MAX_KEY_SIZE == sizeof(fs_key->raw), "Mismatch of max key sizes");
+    fs_key->mode = 0;  // unused by kernel
+    memcpy(fs_key->raw, key.data(), key.size());
+    fs_key->size = key.size();
+    return true;
+}
+
+static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
+
+static std::string keyrefstring(const std::string& raw_ref) {
+    std::ostringstream o;
+    for (unsigned char i : raw_ref) {
+        o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+    }
+    return o.str();
+}
+
+static std::string buildLegacyKeyName(const std::string& prefix, const std::string& raw_ref) {
+    return prefix + ":" + keyrefstring(raw_ref);
+}
+
+// Get the ID of the keyring we store all fscrypt keys in when the kernel is too
+// old to support FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY.
+static bool fscryptKeyring(key_serial_t* device_keyring) {
+    *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
+    if (*device_keyring == -1) {
+        PLOG(ERROR) << "Unable to find device keyring";
+        return false;
+    }
+    return true;
+}
+
+// Add an encryption key of type "logon" to the global session keyring.
+static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) {
+    // Place fscrypt_key into automatically zeroing buffer.
+    KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
+    fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
+
+    if (!fillKey(key, &fs_key)) return false;
+    key_serial_t device_keyring;
+    if (!fscryptKeyring(&device_keyring)) return false;
+    for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+        auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
+        key_serial_t key_id =
+            add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
+        if (key_id == -1) {
+            PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
+            return false;
+        }
+        LOG(INFO) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+                   << " in process " << getpid();
+    }
+    return true;
+}
+
+// Installs fscrypt-provisioning key into session level kernel keyring.
+// This allows for the given key to be installed back into filesystem keyring.
+// For more context see reloadKeyFromSessionKeyring.
+static bool installProvisioningKey(const KeyBuffer& key, const std::string& ref,
+                                   const fscrypt_key_specifier& key_spec) {
+    key_serial_t device_keyring;
+    if (!fscryptKeyring(&device_keyring)) return false;
+
+    // Place fscrypt_provisioning_key_payload into automatically zeroing buffer.
+    KeyBuffer buf(sizeof(fscrypt_provisioning_key_payload) + key.size(), 0);
+    fscrypt_provisioning_key_payload& provisioning_key =
+            *reinterpret_cast<fscrypt_provisioning_key_payload*>(buf.data());
+    memcpy(provisioning_key.raw, key.data(), key.size());
+    provisioning_key.type = key_spec.type;
+
+    key_serial_t key_id = add_key("fscrypt-provisioning", ref.c_str(), (void*)&provisioning_key,
+                                  buf.size(), device_keyring);
+    if (key_id == -1) {
+        PLOG(ERROR) << "Failed to insert fscrypt-provisioning key for " << ref
+                    << " into session keyring";
+        return false;
+    }
+    LOG(INFO) << "Added fscrypt-provisioning key for " << ref << " to session keyring";
+    return true;
+}
+
+// Build a struct fscrypt_key_specifier for use in the key management ioctls.
+static bool buildKeySpecifier(fscrypt_key_specifier* spec, const EncryptionPolicy& policy) {
+    switch (policy.options.version) {
+        case 1:
+            if (policy.key_raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
+                LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: "
+                           << policy.key_raw_ref.size();
+                return false;
+            }
+            spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
+            memcpy(spec->u.descriptor, policy.key_raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
+            return true;
+        case 2:
+            if (policy.key_raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) {
+                LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: "
+                           << policy.key_raw_ref.size();
+                return false;
+            }
+            spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+            memcpy(spec->u.identifier, policy.key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE);
+            return true;
+        default:
+            LOG(ERROR) << "Invalid encryption policy version: " << policy.options.version;
+            return false;
+    }
+}
+
+// Installs key into keyring of a filesystem mounted on |mountpoint|.
+//
+// It's callers responsibility to fill key specifier, and either arg->raw or arg->key_id.
+//
+// In case arg->key_spec.type equals to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER
+// arg->key_spec.u.identifier will be populated with raw key reference generated
+// by kernel.
+//
+// For documentation on difference between arg->raw and arg->key_id see
+// https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#fs-ioc-add-encryption-key
+static bool installFsKeyringKey(const std::string& mountpoint, const EncryptionOptions& options,
+                                fscrypt_add_key_arg* arg) {
+    if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED;
+
+    android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << mountpoint << " to install key";
+        return false;
+    }
+
+    if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) {
+        PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint;
+        return false;
+    }
+
+    return true;
+}
+
+bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
+                const KeyBuffer& key, EncryptionPolicy* policy) {
+    policy->options = options;
+    // Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
+    // have to copy the raw key into it.
+    KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);
+    struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();
+
+    // Initialize the "key specifier", which is like a name for the key.
+    switch (options.version) {
+        case 1:
+            // A key for a v1 policy is specified by an arbitrary 8-byte
+            // "descriptor", which must be provided by userspace.  We use the
+            // first 8 bytes from the double SHA-512 of the key itself.
+            if (options.use_hw_wrapped_key) {
+                // When wrapped key is supported, only the first 32 bytes are
+                // the same per boot. The second 32 bytes can change as the ephemeral
+                // key is different.
+                policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size()/2);
+            } else {
+                policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
+            }
+            if (!isFsKeyringSupported()) {
+                return installKeyLegacy(key, policy->key_raw_ref);
+            }
+            if (!buildKeySpecifier(&arg->key_spec, *policy)) {
+                return false;
+            }
+            break;
+        case 2:
+            // A key for a v2 policy is specified by an 16-byte "identifier",
+            // which is a cryptographic hash of the key itself which the kernel
+            // computes and returns.  Any user-provided value is ignored; we
+            // just need to set the specifier type to indicate that we're adding
+            // this type of key.
+            arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+            break;
+        default:
+            LOG(ERROR) << "Invalid encryption policy version: " << options.version;
+            return false;
+    }
+
+    arg->raw_size = key.size();
+    memcpy(arg->raw, key.data(), key.size());
+
+    if (!installFsKeyringKey(mountpoint, options, arg)) return false;
+
+    if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+        // Retrieve the key identifier that the kernel computed.
+        policy->key_raw_ref =
+                std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);
+    }
+    std::string ref = keyrefstring(policy->key_raw_ref);
+    LOG(INFO) << "Installed fscrypt key with ref " << ref << " to " << mountpoint;
+
+    if (!installProvisioningKey(key, ref, arg->key_spec)) return false;
+    return true;
+}
+
+// Remove an encryption key of type "logon" from the global session keyring.
+static bool evictKeyLegacy(const std::string& raw_ref) {
+    key_serial_t device_keyring;
+    if (!fscryptKeyring(&device_keyring)) return false;
+    bool success = true;
+    for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+        auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
+        auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
+
+        // Unlink the key from the keyring.  Prefer unlinking to revoking or
+        // invalidating, since unlinking is actually no less secure currently, and
+        // it avoids bugs in certain kernel versions where the keyring key is
+        // referenced from places it shouldn't be.
+        if (keyctl_unlink(key_serial, device_keyring) != 0) {
+            PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
+            success = false;
+        } else {
+            LOG(ERROR) << "Unlinked key with serial " << key_serial << " ref " << ref;
+        }
+    }
+    return success;
+}
+
+static bool evictProvisioningKey(const std::string& ref) {
+    key_serial_t device_keyring;
+    if (!fscryptKeyring(&device_keyring)) {
+        return false;
+    }
+
+    auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
+    if (key_serial == -1 && errno != ENOKEY) {
+        PLOG(ERROR) << "Error searching session keyring for fscrypt-provisioning key for " << ref;
+        return false;
+    }
+
+    if (key_serial != -1 && keyctl_unlink(key_serial, device_keyring) != 0) {
+        PLOG(ERROR) << "Failed to unlink fscrypt-provisioning key for " << ref
+                    << " from session keyring";
+        return false;
+    }
+    return true;
+}
+
+bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy) {
+    if (policy.options.version == 1 && !isFsKeyringSupported()) {
+        return evictKeyLegacy(policy.key_raw_ref);
+    }
+
+    android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << mountpoint << " to evict key";
+        return false;
+    }
+
+    struct fscrypt_remove_key_arg arg;
+    memset(&arg, 0, sizeof(arg));
+
+    if (!buildKeySpecifier(&arg.key_spec, policy)) {
+        return false;
+    }
+
+    std::string ref = keyrefstring(policy.key_raw_ref);
+
+    if (ioctl(fd, FS_IOC_REMOVE_ENCRYPTION_KEY, &arg) != 0) {
+        PLOG(ERROR) << "Failed to evict fscrypt key with ref " << ref << " from " << mountpoint;
+        return false;
+    }
+
+    LOG(ERROR) << "Evicted fscrypt key with ref " << ref << " from " << mountpoint;
+    if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS) {
+        // Should never happen because keys are only added/removed as root.
+        LOG(ERROR) << "Unexpected case: key with ref " << ref << " is still added by other users!";
+    } else if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY) {
+        LOG(ERROR) << "Files still open after removing key with ref " << ref
+                   << ".  These files were not locked!";
+    }
+
+    if (!evictProvisioningKey(ref)) return false;
+    return true;
+}
+
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+                           const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+                           KeyBuffer* key, bool keepOld) {
+    if (pathExists(key_path)) {
+        LOG(INFO) << "Key exists, using: " << key_path;
+        if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
+    } else {
+        if (!gen.allow_gen) {
+            LOG(ERROR) << "No key found in " << key_path;
+            return false;
+        }
+        LOG(INFO) << "Creating new key in " << key_path;
+        if (!::generateStorageKey(gen, key)) return false;
+        if (!storeKeyAtomically(key_path, tmp_path, key_authentication, *key)) return false;
+    }
+    return true;
+}
+
+bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy) {
+    key_serial_t device_keyring;
+    if (!fscryptKeyring(&device_keyring)) {
+        return false;
+    }
+
+    std::string ref = keyrefstring(policy.key_raw_ref);
+    auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
+    if (key_serial == -1) {
+        PLOG(ERROR) << "Failed to find fscrypt-provisioning key for " << ref
+                    << " in session keyring";
+        return false;
+    }
+
+    LOG(INFO) << "Installing fscrypt-provisioning key for " << ref << " back into " << mountpoint
+               << " fs-keyring";
+
+    struct fscrypt_add_key_arg arg;
+    memset(&arg, 0, sizeof(arg));
+    if (!buildKeySpecifier(&arg.key_spec, policy)) return false;
+    arg.key_id = key_serial;
+    if (!installFsKeyringKey(mountpoint, policy.options, &arg)) return false;
+
+    return true;
+}
diff --git a/crypto/fscrypt/KeyUtil.h b/crypto/fscrypt/KeyUtil.h
new file mode 100755
index 0000000..dcfcde8
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYUTIL_H
+#define ANDROID_VOLD_KEYUTIL_H
+
+#include "KeyBuffer.h"
+#include "KeyStorage.h"
+
+#include <fscrypt/fscrypt.h>
+
+#include <memory>
+#include <string>
+
+
+using namespace android::fscrypt;
+
+// Description of how to generate a key when needed.
+struct KeyGeneration {
+    size_t keysize;
+    bool allow_gen;
+    bool use_hw_wrapped_key;
+};
+
+// Generate a key as specified in KeyGeneration
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key);
+
+// Returns a key with allow_gen false so generateStorageKey returns false;
+// this is used to indicate to retrieveOrGenerateKey that a key should not
+// be generated.
+const KeyGeneration neverGen();
+
+bool isFsKeyringSupported(void);
+
+// Install a file-based encryption key to the kernel, for use by encrypted files
+// on the specified filesystem using the specified encryption policy version.
+//
+// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.
+// Otherwise we add the key to the global session keyring as a "logon" key.
+//
+// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way
+// the kernel supports.
+//
+// If kernel supports FS_IOC_ADD_ENCRYPTION_KEY, also installs key of
+// fscrypt-provisioning type to the global session keyring. This makes it
+// possible to unmount and then remount mountpoint without losing the file-based
+// key.
+//
+// Returns %true on success, %false on failure.  On success also sets *policy
+// to the EncryptionPolicy used to refer to this key.
+bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
+                const KeyBuffer& key, EncryptionPolicy* policy);
+
+// Evict a file-based encryption key from the kernel.
+//
+// This undoes the effect of installKey().
+//
+// If the kernel doesn't support the filesystem-level keyring, the caller is
+// responsible for dropping caches.
+bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
+
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+                           const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+                           KeyBuffer* key, bool keepOld = true);
+
+// Re-installs a file-based encryption key of fscrypt-provisioning type from the
+// global session keyring back into fs keyring of the mountpoint.
+bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy);
+
+#endif
diff --git a/crypto/fscrypt/Keymaster.cpp b/crypto/fscrypt/Keymaster.cpp
new file mode 100755
index 0000000..c5b7bc3
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at  
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster.h"
+
+#include <android-base/logging.h>
+#include <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+
+KeymasterOperation::~KeymasterOperation() {
+    if (mDevice) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
+                                          const std::function<void(const char*, size_t)> consumer) {
+    uint32_t inputConsumed = 0;
+
+    km::ErrorCode km_error;
+    auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
+                      const hidl_vec<km::KeyParameter>& /*ignored*/,
+                      const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        inputConsumed += inputConsumedDelta;
+        consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+
+    while (inputConsumed != inputLen) {
+        size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
+        auto inputBlob = km::support::blob2hidlVec(
+            reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
+        auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
+                                     km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
+        if (!error.isOk()) {
+            LOG(ERROR) << "update failed: " << error.description();
+            mDevice = nullptr;
+            return false;
+        }
+        if (km_error != km::ErrorCode::OK) {
+            LOG(ERROR) << "update failed, code " << int32_t(km_error);
+            mDevice = nullptr;
+            return false;
+        }
+        if (inputConsumed > inputLen) {
+            LOG(ERROR) << "update reported too much input consumed";
+            mDevice = nullptr;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
+                      const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+    auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
+                                 hidl_vec<uint8_t>(), km::HardwareAuthToken(),
+                                 km::VerificationToken(), hidlCb);
+    mDevice = nullptr;
+    if (!error.isOk()) {
+        LOG(ERROR) << "finish failed: " << error.description();
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}
+
+/* static */ bool Keymaster::hmacKeyGenerated = false;
+
+Keymaster::Keymaster() {
+    auto devices = KmDevice::enumerateAvailableDevices();
+    if (!hmacKeyGenerated) {
+        KmDevice::performHmacKeyAgreement(devices);
+        hmacKeyGenerated = true;
+    }
+    for (auto& dev : devices) {
+        // Do not use StrongBox for device encryption / credential encryption.  If a security chip
+        // is present it will have Weaver, which already strengthens CE.  We get no additional
+        // benefit from using StrongBox here, so skip it.
+        if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
+            mDevice = std::move(dev);
+            break;
+        }
+    }
+    if (!mDevice) return;
+    auto& version = mDevice->halVersion();
+    LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
+              << " for encryption.  Security level: " << toString(version.securityLevel)
+              << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName();
+}
+
+bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+                      const km::KeyCharacteristics& /*ignored*/) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+    };
+
+    auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "generate_key failed: " << error.description();
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}
+
+km::ErrorCode Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) {
+    auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (key)
+            key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]), exportedKeyBlob.size());
+    };
+    auto error = mDevice->exportKey(km::KeyFormat::RAW, kmKeyBlob, {}, {}, hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "export_key failed: " << error.description();
+        return km::ErrorCode::UNKNOWN_ERROR;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
+        return km_error;
+    }
+    return km::ErrorCode::OK;
+}
+
+bool Keymaster::deleteKey(const std::string& key) {
+    auto keyBlob = km::support::blob2hidlVec(key);
+    auto error = mDevice->deleteKey(keyBlob);
+    if (!error.isOk()) {
+        LOG(ERROR) << "delete_key failed: " << error.description();
+        return false;
+    }
+    if (error != km::ErrorCode::OK) {
+        LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
+        return false;
+    }
+    return true;
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+                           std::string* newKey) {
+    auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (newKey)
+            newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+                           upgradedKeyBlob.size());
+    };
+    auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "upgrade_key failed: " << error.description();
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}
+
+KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
+                                    const km::AuthorizationSet& inParams,
+                                    const km::HardwareAuthToken& authToken,
+                                    km::AuthorizationSet* outParams) {
+    auto keyBlob = km::support::blob2hidlVec(key);
+    uint64_t mOpHandle;
+    km::ErrorCode km_error;
+
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
+                      uint64_t operationHandle) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (outParams) *outParams = _outParams;
+        mOpHandle = operationHandle;
+    };
+
+    auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "begin failed: " << error.description();
+        return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+        return KeymasterOperation(km_error);
+    }
+    return KeymasterOperation(mDevice.get(), mOpHandle);
+}
+
+bool Keymaster::isSecure() {
+    return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+}
+
+void Keymaster::earlyBootEnded() {
+    auto devices = KmDevice::enumerateAvailableDevices();
+    for (auto& dev : devices) {
+        auto error = dev->earlyBootEnded();
+        if (!error.isOk()) {
+            LOG(ERROR) << "earlyBootEnded call failed: " << error.description() << " for "
+                       << dev->halVersion().keymasterName;
+        }
+        km::V4_1_ErrorCode km_error = error;
+        if (km_error != km::V4_1_ErrorCode::OK && km_error != km::V4_1_ErrorCode::UNIMPLEMENTED) {
+            LOG(ERROR) << "Error reporting early boot ending to keymaster: "
+                       << static_cast<int32_t>(km_error) << " for "
+                       << dev->halVersion().keymasterName;
+        }
+    }
+}
+
+int keymaster_compatibility_cryptfs_scrypt() {
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    return dev.isSecure();
+}
+
+static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
+                                uint32_t* out_size) {
+    if (!buffer || !out_size) {
+        LOG(ERROR) << "Missing target pointers";
+        return false;
+    }
+    *out_size = towrite.size();
+    if (buffer_size < towrite.size()) {
+        LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
+        return false;
+    }
+    memset(buffer, '\0', buffer_size);
+    std::copy(towrite.begin(), towrite.end(), buffer);
+    return true;
+}
+
+static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                      uint32_t ratelimit) {
+    return km::AuthorizationSetBuilder()
+        .RsaSigningKey(rsa_key_size, rsa_exponent)
+        .NoDigestOrPadding()
+        .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
+        .Authorization(km::TAG_NO_AUTH_REQUIRED)
+        .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+}
+
+int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                            uint32_t ratelimit, uint8_t* key_buffer,
+                                            uint32_t key_buffer_size, uint32_t* key_out_size) {
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    std::string key;
+    if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
+    if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
+    return 0;
+}
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                             uint32_t ratelimit, const uint8_t* key_blob,
+                                             size_t key_blob_size, uint8_t* key_buffer,
+                                             uint32_t key_buffer_size, uint32_t* key_out_size) {
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+    std::string new_key;
+    if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
+        return -1;
+    if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
+    return 0;
+}
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return KeymasterSignResult::error;
+    }
+    if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+        return KeymasterSignResult::error;
+    }
+
+    km::AuthorizationSet outParams;
+    std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+    std::string input(reinterpret_cast<const char*>(object), object_size);
+    std::string output;
+    KeymasterOperation op;
+
+    auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
+    while (true) {
+        op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
+        if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+            sleep(ratelimit);
+            continue;
+        } else
+            break;
+    }
+
+    if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+        LOG(ERROR) << "Keymaster key requires upgrade";
+        return KeymasterSignResult::upgrade;
+    }
+
+    if (op.errorCode() != km::ErrorCode::OK) {
+        LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+        return KeymasterSignResult::error;
+    }
+
+    if (!op.updateCompletely(input, &output)) {
+        LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+                   << uint32_t(op.errorCode());
+        return KeymasterSignResult::error;
+    }
+
+    if (!op.finish(&output)) {
+        LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+                   << int32_t(op.errorCode());
+        return KeymasterSignResult::error;
+    }
+
+    *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+    if (*signature_buffer == nullptr) {
+        LOG(ERROR) << "Error allocation buffer for keymaster signature";
+        return KeymasterSignResult::error;
+    }
+    *signature_buffer_size = output.size();
+    std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+    return KeymasterSignResult::ok;
+}
diff --git a/crypto/fscrypt/Keymaster.h b/crypto/fscrypt/Keymaster.h
new file mode 100644
index 0000000..6f74db4
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#include "KeyBuffer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace km {
+
+using namespace ::android::hardware::keymaster::V4_1;
+
+// Surprisingly -- to me, at least -- this is totally fine.  You can re-define symbols that were
+// brought in via a using directive (the "using namespace") above.  In general this seems like a
+// dangerous thing to rely on, but in this case its implications are simple and straightforward:
+// km::ErrorCode refers to the 4.0 ErrorCode, though we pull everything else from 4.1.
+using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using V4_1_ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode;
+
+}  // namespace km
+
+using KmDevice = km::support::Keymaster;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle representing an
+// ongoing Keymaster operation.  Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+  public:
+    ~KeymasterOperation();
+    // Is this instance valid? This is false if creation fails, and becomes
+    // false on finish or if an update fails.
+    explicit operator bool() const { return mError == km::ErrorCode::OK; }
+    km::ErrorCode errorCode() const { return mError; }
+    // Call "update" repeatedly until all of the input is consumed, and
+    // concatenate the output. Return true on success.
+    template <class TI, class TO>
+    bool updateCompletely(TI& input, TO* output) {
+        if (output) output->clear();
+        return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
+            if (output) std::copy(b, b + n, std::back_inserter(*output));
+        });
+    }
+
+    // Finish and write the output to this string, unless pointer is null.
+    bool finish(std::string* output);
+    // Move constructor
+    KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
+    // Construct an object in an error state for error returns
+    KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
+    // Move Assignment
+    KeymasterOperation& operator=(KeymasterOperation&& rhs) {
+        mDevice = rhs.mDevice;
+        rhs.mDevice = nullptr;
+
+        mOpHandle = rhs.mOpHandle;
+        rhs.mOpHandle = 0;
+
+        mError = rhs.mError;
+        rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
+
+        return *this;
+    }
+
+  private:
+    KeymasterOperation(KmDevice* d, uint64_t h)
+        : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
+    KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
+
+    bool updateCompletely(const char* input, size_t inputLen,
+                          const std::function<void(const char*, size_t)> consumer);
+
+    KmDevice* mDevice;
+    uint64_t mOpHandle;
+    km::ErrorCode mError;
+    DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+    friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+  public:
+    Keymaster();
+    // false if we failed to open the keymaster device.
+    explicit operator bool() { return mDevice.get() != nullptr; }
+    // Generate a key in the keymaster from the given params.
+    bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
+    // Exports a keymaster key with STORAGE_KEY tag wrapped with a per-boot ephemeral key
+    km::ErrorCode exportKey(const KeyBuffer& kmKey, std::string* key);
+    // If the keymaster supports it, permanently delete a key.
+    bool deleteKey(const std::string& key);
+    // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+    bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+                    std::string* newKey);
+    // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+    KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
+                             const km::AuthorizationSet& inParams,
+                             const km::HardwareAuthToken& authToken,
+                             km::AuthorizationSet* outParams);
+    bool isSecure();
+
+    // Tell all Keymaster instances that early boot has ended and early boot-only keys can no longer
+    // be created or used.
+    static void earlyBootEnded();
+
+  private:
+    android::sp<KmDevice> mDevice;
+    DISALLOW_COPY_AND_ASSIGN(Keymaster);
+    static bool hmacKeyGenerated;
+};
+
+// FIXME no longer needed now cryptfs is in C++.
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+
+/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
+
+enum class KeymasterSignResult {
+    ok = 0,
+    error = -1,
+    upgrade = -2,
+};
+
+int keymaster_compatibility_cryptfs_scrypt();
+int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                            uint32_t ratelimit, uint8_t* key_buffer,
+                                            uint32_t key_buffer_size, uint32_t* key_out_size);
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                             uint32_t ratelimit, const uint8_t* key_blob,
+                                             size_t key_blob_size, uint8_t* key_buffer,
+                                             uint32_t key_buffer_size, uint32_t* key_out_size);
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
+
+#endif
diff --git a/crypto/fscrypt/MetadataCrypt.cpp b/crypto/fscrypt/MetadataCrypt.cpp
new file mode 100755
index 0000000..853e81e
--- /dev/null
+++ b/crypto/fscrypt/MetadataCrypt.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MetadataCrypt.h"
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <fs_mgr.h>
+#include <libdm/dm.h>
+
+#include "Checkpoint.h"
+#include "CryptoType.h"
+#include "EncryptInplace.h"
+#include "FsCrypt.h"
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Keymaster.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#define TABLE_LOAD_RETRIES 10
+
+
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetEntryForMountPoint;
+using ::KeyBuffer;
+using namespace android::dm;
+
+// Parsed from metadata options
+struct CryptoOptions {
+    struct CryptoType cipher = invalid_crypto_type;
+    bool use_legacy_options_format = false;
+    bool set_dun = true;  // Non-legacy driver always sets DUN
+    bool use_hw_wrapped_key = false;
+};
+
+static const std::string kDmNameUserdata = "userdata";
+
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+
+// The first entry in this table is the default crypto type.
+constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
+
+static_assert(validateSupportedCryptoTypes(64, supported_crypto_types,
+                                           array_length(supported_crypto_types)),
+              "We have a CryptoType which was incompletely constructed.");
+
+constexpr CryptoType legacy_aes_256_xts =
+        CryptoType().set_config_name("aes-256-xts").set_kernel_name("AES-256-XTS").set_keysize(64);
+
+static_assert(isValidCryptoType(64, legacy_aes_256_xts),
+              "We have a CryptoType which was incompletely constructed.");
+
+// Returns KeyGeneration suitable for key as described in CryptoOptions
+const KeyGeneration makeGen(const CryptoOptions& options) {
+    return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key};
+}
+
+static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
+    // We're about to mount data not verified by verified boot.  Tell Keymaster instances that early
+    // boot has ended.
+    ::Keymaster::earlyBootEnded();
+
+    // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
+    // partitions in the fsck domain.
+    if (setexeccon(::sFsckContext)) {
+        PLOG(ERROR) << "Failed to setexeccon";
+        return false;
+    }
+
+    if (fstab_default.empty()) {
+        if (!ReadDefaultFstab(&fstab_default)) {
+            PLOG(ERROR) << "Failed to open default fstab";
+            return -1;
+        }
+    }
+    auto mount_rc = fs_mgr_do_mount(&fstab_default, const_cast<char*>(mount_point),
+                                    const_cast<char*>(blk_device), nullptr,
+                                    ::cp_needsCheckpoint(), true);
+    if (setexeccon(nullptr)) {
+        PLOG(ERROR) << "Failed to clear setexeccon";
+        return false;
+    }
+    if (mount_rc != 0) {
+        LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
+        return false;
+    }
+    LOG(INFO) << "mount_via_fs_mgr::Mounted " << mount_point;
+    return true;
+}
+
+// Note: It is possible to orphan a key if it is removed before deleting
+// Update this once keymaster APIs change, and we have a proper commit.
+static void commit_key(const std::string& dir) {
+    while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+        LOG(ERROR) << "Wait for boot timed out";
+    }
+    Keymaster keymaster;
+    auto keyPath = dir + "/" + kFn_keymaster_key_blob;
+    auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+    std::string key;
+
+    if (!android::base::ReadFileToString(keyPath, &key)) {
+        LOG(ERROR) << "Failed to read old key: " << dir;
+        return;
+    }
+    if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
+        PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
+        return;
+    }
+    if (!keymaster.deleteKey(key)) {
+        LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+    }
+    LOG(INFO) << "Old Key deleted: " << dir;
+}
+
+static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
+                     KeyBuffer* key) {
+    if (metadata_key_dir.empty()) {
+        LOG(ERROR) << "Failed to get metadata_key_dir";
+        return false;
+    }
+    std::string sKey;
+    auto dir = metadata_key_dir + "/key";
+    LOG(INFO) << "metadata_key_dir/key: " << dir;
+    if (fs_mkdirs(dir.c_str(), 0700)) {
+        PLOG(ERROR) << "Creating directories: " << dir;
+        return false;
+    }
+    auto temp = metadata_key_dir + "/tmp";
+    auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+    /* If we have a leftover upgraded key, delete it.
+     * We either failed an update and must return to the old key,
+     * or we rebooted before commiting the keys in a freak accident.
+     * Either way, we can re-upgrade the key if we need to.
+     */
+
+    Keymaster keymaster;
+    if (pathExists(newKeyPath)) {
+        if (!android::base::ReadFileToString(newKeyPath, &sKey))
+            LOG(ERROR) << "Failed to read incomplete key: " << dir;
+        else if (!keymaster.deleteKey(sKey))
+            LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
+        else
+            unlink(newKeyPath.c_str());
+    }
+    bool needs_cp = cp_needsCheckpoint();
+    if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, true)) return false;
+    if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
+    return true;
+}
+
+static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
+    if (::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
+        PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
+        return false;
+    }
+    return true;
+}
+
+static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device,
+                                  const KeyBuffer& key, const CryptoOptions& options,
+                                  std::string* crypto_blkdev, uint64_t* nr_sec) {
+    if (!get_number_of_sectors(blk_device, nr_sec)) return false;
+    // TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte
+    // sectors
+    *nr_sec &= ~7;
+
+    KeyBuffer module_key;
+    if (options.use_hw_wrapped_key) {
+        if (!exportWrappedStorageKey(key, &module_key)) {
+            LOG(ERROR) << "Failed to get ephemeral wrapped key";
+            return false;
+        }
+    } else {
+        module_key = key;
+    }
+
+    KeyBuffer hex_key_buffer;
+    if (::StrToHex(module_key, hex_key_buffer) != android::OK) {
+        LOG(ERROR) << "Failed to turn key to hex";
+        return false;
+    }
+    std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size());
+
+    auto target = std::make_unique<DmTargetDefaultKey>(0, *nr_sec, options.cipher.get_kernel_name(),
+                                                       hex_key, blk_device, 0);
+    if (options.use_legacy_options_format) target->SetUseLegacyOptionsFormat();
+    if (options.set_dun) target->SetSetDun();
+    if (options.use_hw_wrapped_key) target->SetWrappedKeyV0();
+
+    DmTable table;
+    table.AddTarget(std::move(target));
+
+    auto& dm = DeviceMapper::Instance();
+    for (int i = 0;; i++) {
+        if (dm.CreateDevice(dm_name, table)) {
+            break;
+        }
+        if (i + 1 >= TABLE_LOAD_RETRIES) {
+            PLOG(ERROR) << "Could not create default-key device " << dm_name;
+            return false;
+        }
+        PLOG(INFO) << "Could not create default-key device, retrying";
+        usleep(500000);
+    }
+
+    if (!dm.GetDmDevicePathByName(dm_name, crypto_blkdev)) {
+        LOG(ERROR) << "Cannot retrieve default-key device status " << dm_name;
+        return false;
+    }
+    std::stringstream ss;
+    ss << *crypto_blkdev;
+    LOG(INFO) << "Created device: " << ss.str();
+    return true;
+}
+
+static const CryptoType& lookup_cipher(const std::string& cipher_name) {
+    if (cipher_name.empty()) return supported_crypto_types[0];
+    for (size_t i = 0; i < array_length(supported_crypto_types); i++) {
+        if (cipher_name == supported_crypto_types[i].get_config_name()) {
+            return supported_crypto_types[i];
+        }
+    }
+    return invalid_crypto_type;
+}
+
+static bool parse_options(const std::string& options_string, CryptoOptions* options) {
+    auto parts = android::base::Split(options_string, ":");
+    if (parts.size() < 1 || parts.size() > 2) {
+        LOG(ERROR) << "Invalid metadata encryption option: " << options_string;
+        return false;
+    }
+    std::string cipher_name = parts[0];
+    options->cipher = lookup_cipher(cipher_name);
+    if (options->cipher.get_kernel_name() == nullptr) {
+        LOG(ERROR) << "No metadata cipher named " << cipher_name << " found";
+        return false;
+    }
+
+    if (parts.size() == 2) {
+        if (parts[1] == "wrappedkey_v0") {
+            options->use_hw_wrapped_key = true;
+        } else {
+            LOG(ERROR) << "Invalid metadata encryption flag: " << parts[1];
+            return false;
+        }
+    }
+    return true;
+}
+
+bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
+                                      bool needs_encrypt) {
+    LOG(INFO) << "fscrypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
+    auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
+    if (encrypted_state != "" && encrypted_state != "encrypted") {
+        LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
+        return false;
+    }
+    if (fstab_default.empty()) {
+        if (!ReadDefaultFstab(&fstab_default)) {
+            PLOG(ERROR) << "Failed to open default fstab";
+            return -1;
+        }
+    }
+    auto data_rec = GetEntryForMountPoint(&fstab_default, mount_point);
+    if (!data_rec) {
+        LOG(ERROR) << "Failed to get data_rec for " << mount_point;
+        return false;
+    }
+
+    constexpr unsigned int pre_gki_level = 29;
+    unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
+            "ro.crypto.dm_default_key.options_format.version",
+            (GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+
+    CryptoOptions options;
+    if (options_format_version == 1) {
+        if (!data_rec->metadata_encryption.empty()) {
+            LOG(ERROR) << "metadata_encryption options cannot be set in legacy mode";
+            return false;
+        }
+        options.cipher = legacy_aes_256_xts;
+        options.use_legacy_options_format = true;
+        if (is_metadata_wrapped_key_supported()) {
+            options.use_hw_wrapped_key = true;
+            LOG(INFO) << "metadata_wrapped_key_true";
+        }
+        options.set_dun = android::base::GetBoolProperty("ro.crypto.set_dun", false);
+        if (!options.set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
+            LOG(ERROR)
+                    << "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
+            return false;
+        }
+    } else if (options_format_version == 2) {
+        if (!parse_options(data_rec->metadata_encryption, &options)) return false;
+    } else {
+        LOG(ERROR) << "Unknown options_format_version: " << options_format_version;
+        return false;
+    }
+    auto gen = needs_encrypt ? makeGen(options) : neverGen();
+    KeyBuffer key;
+    if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false;
+
+    std::string crypto_blkdev;
+    uint64_t nr_sec;
+    if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev, &nr_sec))
+        return false;
+
+    // FIXME handle the corrupt case
+    if (needs_encrypt) {
+        LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
+        off64_t size_already_done = 0;
+        auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec,
+                                         &size_already_done, nr_sec, 0, false);
+        if (rc != 0) {
+            LOG(ERROR) << "Inplace crypto failed with code: " << rc;
+            return false;
+        }
+        if (static_cast<uint64_t>(size_already_done) != nr_sec) {
+            LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
+            return false;
+        }
+        LOG(INFO) << "Inplace encryption complete";
+    }
+
+    LOG(INFO) << "Mounting metadata-encrypted filesystem:" << mount_point;
+    mount_via_fs_mgr(mount_point.c_str(), crypto_blkdev.c_str());
+    android::base::SetProperty("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+    // Record that there's at least one fstab entry with metadata encryption
+    if (!android::base::SetProperty("ro.crypto.metadata.enabled", "true")) {
+        LOG(WARNING) << "failed to set ro.crypto.metadata.enabled";  // This isn't fatal
+    }
+    return true;
+}
+
+static bool get_volume_options(CryptoOptions* options) {
+    return parse_options(android::base::GetProperty("ro.crypto.volume.metadata.encryption", ""),
+                         options);
+}
+
+bool defaultkey_volume_keygen(KeyGeneration* gen) {
+    CryptoOptions options;
+    if (!get_volume_options(&options)) return false;
+    *gen = makeGen(options);
+    return true;
+}
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+                                 const KeyBuffer& key, std::string* out_crypto_blkdev) {
+    LOG(ERROR) << "defaultkey_setup_ext_volume: " << label << " " << blk_device;
+
+    CryptoOptions options;
+    if (!get_volume_options(&options)) return false;
+    uint64_t nr_sec;
+    return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
+}
diff --git a/crypto/fscrypt/MetadataCrypt.h b/crypto/fscrypt/MetadataCrypt.h
new file mode 100644
index 0000000..0370664
--- /dev/null
+++ b/crypto/fscrypt/MetadataCrypt.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _METADATA_CRYPT_H
+#define _METADATA_CRYPT_H
+
+#include <string>
+
+#include "KeyBuffer.h"
+#include "KeyUtil.h"
+
+
+bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
+                                      const std::string& mount_point, bool needs_encrypt);
+
+bool defaultkey_volume_keygen(KeyGeneration* gen);
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+                                 const KeyBuffer& key,
+                                 std::string* out_crypto_blkdev);
+
+#endif
diff --git a/crypto/fscrypt/Process.cpp b/crypto/fscrypt/Process.cpp
new file mode 100644
index 0000000..277d6a3
--- /dev/null
+++ b/crypto/fscrypt/Process.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <mntent.h>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "Process.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static bool checkMaps(const std::string& path, const std::string& prefix) {
+    bool found = false;
+    auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (!file) {
+        return false;
+    }
+
+    char* buf = nullptr;
+    size_t len = 0;
+    while (getline(&buf, &len, file.get()) != -1) {
+        std::string line(buf);
+        std::string::size_type pos = line.find('/');
+        if (pos != std::string::npos) {
+            line = line.substr(pos);
+            if (android::base::StartsWith(line, prefix)) {
+                LOG(WARNING) << "Found map " << path << " referencing " << line;
+                found = true;
+                break;
+            }
+        }
+    }
+    free(buf);
+
+    return found;
+}
+
+static bool checkSymlink(const std::string& path, const std::string& prefix) {
+    std::string res;
+    if (android::base::Readlink(path, &res)) {
+        if (android::base::StartsWith(res, prefix)) {
+            LOG(WARNING) << "Found symlink " << path << " referencing " << res;
+            return true;
+        }
+    }
+    return false;
+}
+
+// TODO: Refactor the code with KillProcessesWithOpenFiles().
+int KillProcessesWithMounts(const std::string& prefix, int signal) {
+    std::unordered_set<pid_t> pids;
+
+    auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+    if (!proc_d) {
+        PLOG(ERROR) << "Failed to open proc";
+        return -1;
+    }
+
+    struct dirent* proc_de;
+    while ((proc_de = readdir(proc_d.get())) != nullptr) {
+        // We only care about valid PIDs
+        pid_t pid;
+        if (proc_de->d_type != DT_DIR) continue;
+        if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+        // Look for references to prefix
+        std::string mounts_file(StringPrintf("/proc/%d/mounts", pid));
+        auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(
+                setmntent(mounts_file.c_str(), "r"), endmntent);
+        if (!fp) {
+            PLOG(WARNING) << "Failed to open " << mounts_file;
+            continue;
+        }
+
+        // Check if obb directory is mounted, and get all packages of mounted app data directory.
+        mntent* mentry;
+        while ((mentry = getmntent(fp.get())) != nullptr) {
+            if (android::base::StartsWith(mentry->mnt_dir, prefix)) {
+                pids.insert(pid);
+                break;
+            }
+        }
+    }
+    if (signal != 0) {
+        for (const auto& pid : pids) {
+            LOG(WARNING) << "Killing pid "<< pid << " with signal " << strsignal(signal) <<
+                    " because it has a mount with prefix " << prefix;
+            kill(pid, signal);
+        }
+    }
+    return pids.size();
+}
+
+int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
+    std::unordered_set<pid_t> pids;
+
+    auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+    if (!proc_d) {
+        PLOG(ERROR) << "Failed to open proc";
+        return -1;
+    }
+
+    struct dirent* proc_de;
+    while ((proc_de = readdir(proc_d.get())) != nullptr) {
+        // We only care about valid PIDs
+        pid_t pid;
+        if (proc_de->d_type != DT_DIR) continue;
+        if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+        // Look for references to prefix
+        bool found = false;
+        auto path = StringPrintf("/proc/%d", pid);
+        found |= checkMaps(path + "/maps", prefix);
+        found |= checkSymlink(path + "/cwd", prefix);
+        found |= checkSymlink(path + "/root", prefix);
+        found |= checkSymlink(path + "/exe", prefix);
+
+        auto fd_path = path + "/fd";
+        auto fd_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(fd_path.c_str()), closedir);
+        if (!fd_d) {
+            PLOG(WARNING) << "Failed to open " << fd_path;
+        } else {
+            struct dirent* fd_de;
+            while ((fd_de = readdir(fd_d.get())) != nullptr) {
+                if (fd_de->d_type != DT_LNK) continue;
+                found |= checkSymlink(fd_path + "/" + fd_de->d_name, prefix);
+            }
+        }
+
+        if (found) {
+            pids.insert(pid);
+        }
+    }
+    if (signal != 0) {
+        for (const auto& pid : pids) {
+            LOG(WARNING) << "Sending " << strsignal(signal) << " to " << pid;
+            kill(pid, signal);
+        }
+    }
+    return pids.size();
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/crypto/fscrypt/Process.h b/crypto/fscrypt/Process.h
new file mode 100644
index 0000000..1c59812
--- /dev/null
+++ b/crypto/fscrypt/Process.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+namespace android {
+namespace vold {
+
+int KillProcessesWithOpenFiles(const std::string& path, int signal);
+int KillProcessesWithMounts(const std::string& path, int signal);
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/crypto/fscrypt/ScryptParameters.cpp b/crypto/fscrypt/ScryptParameters.cpp
new file mode 100644
index 0000000..669809b
--- /dev/null
+++ b/crypto/fscrypt/ScryptParameters.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScryptParameters.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+    int params[3];
+    char *token;
+    char *saveptr;
+    int i;
+
+    /*
+     * The token we're looking for should be three integers separated by
+     * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+     */
+    for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+            token != nullptr && i < 3;
+            i++, token = strtok_r(nullptr, ":", &saveptr)) {
+        char *endptr;
+        params[i] = strtol(token, &endptr, 10);
+
+        /*
+         * Check that there was a valid number and it's 8-bit.
+         */
+        if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+            return false;
+        }
+    }
+    if (token != nullptr) {
+        return false;
+    }
+    *Nf = params[0]; *rf = params[1]; *pf = params[2];
+    return true;
+}
diff --git a/crypto/fscrypt/ScryptParameters.h b/crypto/fscrypt/ScryptParameters.h
new file mode 100644
index 0000000..1b43ea5
--- /dev/null
+++ b/crypto/fscrypt/ScryptParameters.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
+#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+__BEGIN_DECLS
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/fscrypt/Utils.cpp b/crypto/fscrypt/Utils.cpp
new file mode 100755
index 0000000..3b0eda0
--- /dev/null
+++ b/crypto/fscrypt/Utils.cpp
@@ -0,0 +1,1622 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include "Process.h"
+#include "sehandle.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_projectid_config.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <list>
+#include <mutex>
+#include <regex>
+#include <thread>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using namespace std::chrono_literals;
+using android::base::EndsWith;
+using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+struct selabel_handle* sehandle;
+
+security_context_t sBlkidContext = nullptr;
+security_context_t sBlkidUntrustedContext = nullptr;
+security_context_t sFsckContext = nullptr;
+security_context_t sFsckUntrustedContext = nullptr;
+
+bool sSleepOnUnmount = true;
+
+static const char* kBlkidPath = "/system/bin/blkid";
+static const char* kKeyPath = "/data/misc/vold";
+
+static const char* kProcDevices = "/proc/devices";
+static const char* kProcFilesystems = "/proc/filesystems";
+
+static const char* kAndroidDir = "/Android/";
+static const char* kAppDataDir = "/Android/data/";
+static const char* kAppMediaDir = "/Android/media/";
+static const char* kAppObbDir = "/Android/obb/";
+
+static const char* kMediaProviderCtx = "u:r:mediaprovider:";
+static const char* kMediaProviderAppCtx = "u:r:mediaprovider_app:";
+
+// Lock used to protect process-level SELinux changes from racing with each
+// other between multiple threads.
+static std::mutex kSecurityLock;
+
+std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path) {
+    return StringPrintf("/mnt/user/%d/%s", user_id, relative_upper_path.c_str());
+}
+
+android::status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
+    const char* cpath = path.c_str();
+    android::status_t res = 0;
+
+    char* secontext = nullptr;
+    if (sehandle) {
+        if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
+            setfscreatecon(secontext);
+        }
+    }
+
+    mode_t mode = 0660 | S_IFBLK;
+    if (mknod(cpath, mode, dev) < 0) {
+        if (errno != EEXIST) {
+            PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev)
+                        << " at " << path;
+            res = -errno;
+        }
+    }
+
+    if (secontext) {
+        setfscreatecon(nullptr);
+        freecon(secontext);
+    }
+
+    return res;
+}
+
+android::status_t DestroyDeviceNode(const std::string& path) {
+    const char* cpath = path.c_str();
+    if (TEMP_FAILURE_RETRY(unlink(cpath))) {
+        return -errno;
+    } else {
+        return android::OK;
+    }
+}
+
+// Sets a default ACL on the directory.
+int SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                  std::vector<gid_t> additionalGids) {
+    if (IsSdcardfsUsed()) {
+        // sdcardfs magically takes care of this
+        return android::OK;
+    }
+
+    size_t num_entries = 3 + (additionalGids.size() > 0 ? additionalGids.size() + 1 : 0);
+    size_t size = sizeof(posix_acl_xattr_header) + num_entries * sizeof(posix_acl_xattr_entry);
+    auto buf = std::make_unique<uint8_t[]>(size);
+
+    posix_acl_xattr_header* acl_header = reinterpret_cast<posix_acl_xattr_header*>(buf.get());
+    acl_header->a_version = POSIX_ACL_XATTR_VERSION;
+
+    posix_acl_xattr_entry* entry =
+            reinterpret_cast<posix_acl_xattr_entry*>(buf.get() + sizeof(posix_acl_xattr_header));
+
+    int tag_index = 0;
+
+    entry[tag_index].e_tag = ACL_USER_OBJ;
+    // The existing mode_t mask has the ACL in the lower 9 bits:
+    // the lowest 3 for "other", the next 3 the group, the next 3 for the owner
+    // Use the mode_t masks to get these bits out, and shift them to get the
+    // correct value per entity.
+    //
+    // Eg if mode_t = 0700, rwx for the owner, then & S_IRWXU >> 6 results in 7
+    entry[tag_index].e_perm = (mode & S_IRWXU) >> 6;
+    entry[tag_index].e_id = uid;
+    tag_index++;
+
+    entry[tag_index].e_tag = ACL_GROUP_OBJ;
+    entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+    entry[tag_index].e_id = gid;
+    tag_index++;
+
+    if (additionalGids.size() > 0) {
+        for (gid_t additional_gid : additionalGids) {
+            entry[tag_index].e_tag = ACL_GROUP;
+            entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+            entry[tag_index].e_id = additional_gid;
+            tag_index++;
+        }
+
+        entry[tag_index].e_tag = ACL_MASK;
+        entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+        entry[tag_index].e_id = 0;
+        tag_index++;
+    }
+
+    entry[tag_index].e_tag = ACL_OTHER;
+    entry[tag_index].e_perm = mode & S_IRWXO;
+    entry[tag_index].e_id = 0;
+
+    int ret = setxattr(path.c_str(), XATTR_NAME_POSIX_ACL_DEFAULT, acl_header, size, 0);
+
+    if (ret != 0) {
+        PLOG(ERROR) << "Failed to set default ACL on " << path;
+    }
+
+    return ret;
+}
+
+int SetQuotaInherit(const std::string& path) {
+    unsigned long flags;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << path << " to set project id inheritance.";
+        return -1;
+    }
+
+    int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+    if (ret == -1) {
+        PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance.";
+        return ret;
+    }
+
+    flags |= FS_PROJINHERIT_FL;
+
+    ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
+    if (ret == -1) {
+        PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance.";
+        return ret;
+    }
+
+    return 0;
+}
+
+int SetQuotaProjectId(const std::string& path, long projectId) {
+    struct fsxattr fsx;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << path << " to set project id.";
+        return -1;
+    }
+
+    int ret = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
+    if (ret == -1) {
+        PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id.";
+        return ret;
+    }
+
+    fsx.fsx_projid = projectId;
+    return ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
+}
+
+int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                            long projectId) {
+    int ret = fs_prepare_dir(path.c_str(), mode, uid, gid);
+
+    if (ret != 0) {
+        return ret;
+    }
+
+    if (!IsSdcardfsUsed()) {
+        ret = SetQuotaProjectId(path, projectId);
+    }
+
+    return ret;
+}
+
+static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId) {
+    namespace fs = std::filesystem;
+
+    // Setup the directory itself correctly
+    int ret = PrepareDirWithProjectId(path, mode, uid, gid, projectId);
+    if (ret != android::OK) {
+        return ret;
+    }
+
+    // Fixup all of its file entries
+    for (const auto& itEntry : fs::directory_iterator(path)) {
+        ret = lchown(itEntry.path().c_str(), uid, gid);
+        if (ret != 0) {
+            return ret;
+        }
+
+        ret = chmod(itEntry.path().c_str(), mode);
+        if (ret != 0) {
+            return ret;
+        }
+
+        if (!IsSdcardfsUsed()) {
+            ret = SetQuotaProjectId(itEntry.path(), projectId);
+            if (ret != 0) {
+                return ret;
+            }
+        }
+    }
+
+    return android::OK;
+}
+
+int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
+                          bool fixupExisting) {
+    long projectId;
+    size_t pos;
+    int ret = 0;
+    bool sdcardfsSupport = IsSdcardfsUsed();
+
+    // Make sure the Android/ directories exist and are setup correctly
+    ret = PrepareAndroidDirs(root);
+    if (ret != 0) {
+        LOG(ERROR) << "Failed to prepare Android/ directories.";
+        return ret;
+    }
+
+    // Now create the application-specific subdir(s)
+    // path is something like /data/media/0/Android/data/com.foo/files
+    // First, chop off the volume root, eg /data/media/0
+    std::string pathFromRoot = path.substr(root.length());
+
+    uid_t uid = appUid;
+    gid_t gid = AID_MEDIA_RW;
+    std::vector<gid_t> additionalGids;
+    std::string appDir;
+
+    // Check that the next part matches one of the allowed Android/ dirs
+    if (StartsWith(pathFromRoot, kAppDataDir)) {
+        appDir = kAppDataDir;
+        if (!sdcardfsSupport) {
+            gid = AID_EXT_DATA_RW;
+            // Also add the app's own UID as a group; since apps belong to a group
+            // that matches their UID, this ensures that they will always have access to
+            // the files created in these dirs, even if they are created by other processes
+            additionalGids.push_back(uid);
+        }
+    } else if (StartsWith(pathFromRoot, kAppMediaDir)) {
+        appDir = kAppMediaDir;
+        if (!sdcardfsSupport) {
+            gid = AID_MEDIA_RW;
+        }
+    } else if (StartsWith(pathFromRoot, kAppObbDir)) {
+        appDir = kAppObbDir;
+        if (!sdcardfsSupport) {
+            gid = AID_EXT_OBB_RW;
+            // See comments for kAppDataDir above
+            additionalGids.push_back(uid);
+        }
+    } else {
+        LOG(ERROR) << "Invalid application directory: " << path;
+        return -EINVAL;
+    }
+
+    // mode = 770, plus sticky bit on directory to inherit GID when apps
+    // create subdirs
+    mode_t mode = S_IRWXU | S_IRWXG | S_ISGID;
+    // the project ID for application-specific directories is directly
+    // derived from their uid
+
+    // Chop off the generic application-specific part, eg /Android/data/
+    // this leaves us with something like com.foo/files/
+    std::string leftToCreate = pathFromRoot.substr(appDir.length());
+    if (!EndsWith(leftToCreate, "/")) {
+        leftToCreate += "/";
+    }
+    std::string pathToCreate = root + appDir;
+    int depth = 0;
+    // Derive initial project ID
+    if (appDir == kAppDataDir || appDir == kAppMediaDir) {
+        projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START;
+    } else if (appDir == kAppObbDir) {
+        projectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
+    }
+
+    while ((pos = leftToCreate.find('/')) != std::string::npos) {
+        std::string component = leftToCreate.substr(0, pos + 1);
+        leftToCreate = leftToCreate.erase(0, pos + 1);
+        pathToCreate = pathToCreate + component;
+
+        if (appDir == kAppDataDir && depth == 1 && component == "cache/") {
+            // All dirs use the "app" project ID, except for the cache dirs in
+            // Android/data, eg Android/data/com.foo/cache
+            // Note that this "sticks" - eg subdirs of this dir need the same
+            // project ID.
+            projectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START;
+        }
+
+        if (fixupExisting && access(pathToCreate.c_str(), F_OK) == 0) {
+            // Fixup all files in this existing directory with the correct UID/GID
+            // and project ID.
+            ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId);
+        } else {
+            ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId);
+        }
+
+        if (ret != 0) {
+            return ret;
+        }
+
+        if (depth == 0) {
+            // Set the default ACL on the top-level application-specific directories,
+            // to ensure that even if applications run with a umask of 0077,
+            // new directories within these directories will allow the GID
+            // specified here to write; this is necessary for apps like
+            // installers and MTP, that require access here.
+            //
+            // See man (5) acl for more details.
+            ret = SetDefaultAcl(pathToCreate, mode, uid, gid, additionalGids);
+            if (ret != 0) {
+                return ret;
+            }
+
+            if (!sdcardfsSupport) {
+                // Set project ID inheritance, so that future subdirectories inherit the
+                // same project ID
+                ret = SetQuotaInherit(pathToCreate);
+                if (ret != 0) {
+                    return ret;
+                }
+            }
+        }
+
+        depth++;
+    }
+
+    return android::OK;
+}
+
+android::status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
+    const char* cpath = path.c_str();
+
+    char* secontext = nullptr;
+    if (sehandle) {
+        if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
+            setfscreatecon(secontext);
+        }
+    }
+
+    int res = fs_prepare_dir(cpath, mode, uid, gid);
+
+    if (secontext) {
+        setfscreatecon(nullptr);
+        freecon(secontext);
+    }
+
+    if (res == 0) {
+        return android::OK;
+    } else {
+        return -errno;
+    }
+}
+
+android::status_t ForceUnmount(const std::string& path) {
+    const char* cpath = path.c_str();
+    if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+        return android::OK;
+    }
+    // Apps might still be handling eject request, so wait before
+    // we start sending signals
+    if (sSleepOnUnmount) sleep(5);
+
+    android::vold::KillProcessesWithOpenFiles(path, SIGINT);
+    if (sSleepOnUnmount) sleep(5);
+    if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+        return android::OK;
+    }
+
+    android::vold::KillProcessesWithOpenFiles(path, SIGTERM);
+    if (sSleepOnUnmount) sleep(5);
+    if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+        return android::OK;
+    }
+
+    android::vold::KillProcessesWithOpenFiles(path, SIGKILL);
+    if (sSleepOnUnmount) sleep(5);
+    if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+        return android::OK;
+    }
+    PLOG(INFO) << "ForceUnmount failed";
+    return -errno;
+}
+
+android::status_t KillProcessesWithMountPrefix(const std::string& path) {
+    if (android::vold::KillProcessesWithMounts(path, SIGINT) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    if (android::vold::KillProcessesWithMounts(path, SIGTERM) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    if (android::vold::KillProcessesWithMounts(path, SIGKILL) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    // Send SIGKILL a second time to determine if we've
+    // actually killed everyone mount
+    if (android::vold::KillProcessesWithMounts(path, SIGKILL) == 0) {
+        return android::OK;
+    }
+    PLOG(ERROR) << "Failed to kill processes using " << path;
+    return -EBUSY;
+}
+
+android::status_t KillProcessesUsingPath(const std::string& path) {
+    if (android::vold::KillProcessesWithOpenFiles(path, SIGINT) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    if (android::vold::KillProcessesWithOpenFiles(path, SIGTERM) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    if (android::vold::KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+        return android::OK;
+    }
+    if (sSleepOnUnmount) sleep(5);
+
+    // Send SIGKILL a second time to determine if we've
+    // actually killed everyone with open files
+    if (android::vold::KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+        return android::OK;
+    }
+    PLOG(ERROR) << "Failed to kill processes using " << path;
+    return -EBUSY;
+}
+
+android::status_t BindMount(const std::string& source, const std::string& target) {
+    if (UnmountTree(target) < 0) {
+        return -errno;
+    }
+    if (TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr)) < 0) {
+        PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
+        return -errno;
+    }
+    return android::OK;
+}
+
+android::status_t Symlink(const std::string& target, const std::string& linkpath) {
+    if (Unlink(linkpath) < 0) {
+        return -errno;
+    }
+    if (TEMP_FAILURE_RETRY(symlink(target.c_str(), linkpath.c_str())) < 0) {
+        PLOG(ERROR) << "Failed to create symlink " << linkpath << " to " << target;
+        return -errno;
+    }
+    return android::OK;
+}
+
+android::status_t Unlink(const std::string& linkpath) {
+    if (TEMP_FAILURE_RETRY(unlink(linkpath.c_str())) < 0 && errno != EINVAL && errno != ENOENT) {
+        PLOG(ERROR) << "Failed to unlink " << linkpath;
+        return -errno;
+    }
+    return android::OK;
+}
+
+android::status_t CreateDir(const std::string& dir, mode_t mode) {
+    struct stat sb;
+    if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &sb)) == 0) {
+        if (S_ISDIR(sb.st_mode)) {
+            return android::OK;
+        } else if (TEMP_FAILURE_RETRY(unlink(dir.c_str())) == -1) {
+            PLOG(ERROR) << "Failed to unlink " << dir;
+            return -errno;
+        }
+    } else if (errno != ENOENT) {
+        PLOG(ERROR) << "Failed to stat " << dir;
+        return -errno;
+    }
+    if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), mode)) == -1 && errno != EEXIST) {
+        PLOG(ERROR) << "Failed to mkdir " << dir;
+        return -errno;
+    }
+    return android::OK;
+}
+
+bool FindValue(const std::string& raw, const std::string& key, std::string* value) {
+    auto qual = key + "=\"";
+    size_t start = 0;
+    while (true) {
+        start = raw.find(qual, start);
+        if (start == std::string::npos) return false;
+        if (start == 0 || raw[start - 1] == ' ') {
+            break;
+        }
+        start += 1;
+    }
+    start += qual.length