factory reset: restore ext4 policy on /data/cache

If ext4 policy is not restored, system won't boot properly
after a factory reset.

When /data is formatted by the user, we need to make sure we
do not create the /data/cache directory so that Android can
create it with the new policy for /data.

This also removes extraneous umount calls, and places them
in the specific wipe function for each filesystem.

Change-Id: I71ff39d8660fbf4aa6fe8a8309e291166359da72
diff --git a/.gitignore b/.gitignore
index e03babb..480f547 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 .*.swp
 *~
 tags
+.vscode
diff --git a/Android.mk b/Android.mk
index 26a5227..81a2f5d 100755
--- a/Android.mk
+++ b/Android.mk
@@ -177,7 +177,11 @@
 ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
     ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0)
         LOCAL_CFLAGS += -DUSE_EXT4
-        LOCAL_C_INCLUDES += system/extras/ext4_utils
+    endif
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -le 28; echo $$?),0)
+        LOCAL_C_INCLUDES += system/extras/ext4_utils \
+            system/extras/ext4_utils/include \
+            bootable/recovery/crypto/ext4crypt
         LOCAL_SHARED_LIBRARIES += libext4_utils
         ifneq ($(wildcard external/lz4/Android.mk),)
             #LOCAL_STATIC_LIBRARIES += liblz4
@@ -206,8 +210,6 @@
     LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_TWRP_LIB)
 endif
 
-LOCAL_C_INCLUDES += system/extras/ext4_utils
-
 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
diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp
old mode 100644
new mode 100755
index 05de86f..9211347
--- a/crypto/ext4crypt/e4policyget.cpp
+++ b/crypto/ext4crypt/e4policyget.cpp
@@ -23,13 +23,12 @@
 #define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
 
 int main(int argc, char *argv[]) {
-	bool ret = false;
 	if (argc != 2) {
 		printf("Must specify a path\n");
 		return -1;
 	} else  {
 		ext4_encryption_policy eep;
-		if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
+		if (e4crypt_policy_get_struct(argv[1], &eep)) {
 			char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
 			policy_to_hex(eep.master_key_descriptor, policy_hex);
 			printf("%s\n", policy_hex);
diff --git a/data.cpp b/data.cpp
index b1e8404..83fb601 100755
--- a/data.cpp
+++ b/data.cpp
@@ -1064,13 +1064,7 @@
 			return;
 		}
 	}
-	if (!TWFunc::Path_Exists(recoveryCacheDir)) {
-		LOGINFO("Recreating %s folder.\n", recoveryCacheDir.c_str());
-		if (!TWFunc::Create_Dir_Recursive(recoveryCacheDir.c_str(), S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, 0, 0)) {
-			LOGERR("DataManager::Output_Version -- Unable to make %s: %s\n", recoveryCacheDir.c_str(), strerror(errno));
-			return;
-		}
-	}
+
 	std::string verPath = recoveryCacheDir + ".version";
 	if (TWFunc::Path_Exists(verPath)) {
 		unlink(verPath.c_str());
diff --git a/gui/Android.mk b/gui/Android.mk
old mode 100644
new mode 100755
index 22b5347..60ba325
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -46,6 +46,15 @@
     LOCAL_SHARED_LIBRARIES += libminzip
     LOCAL_CFLAGS += -DUSE_MINZIP
 endif
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -le 28; echo $$?),0)
+        LOCAL_C_INCLUDES += system/extras/ext4_utils \
+            system/extras/ext4_utils/include \
+            bootable/recovery/crypto/ext4crypt
+        LOCAL_SHARED_LIBRARIES += libext4_utils
+    endif
+endif
+
 LOCAL_MODULE := libguitwrp
 
 #TWRP_EVENT_LOGGING := true
diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml
index 3769da5..e26ccb7 100755
--- a/gui/theme/common/languages/en.xml
+++ b/gui/theme/common/languages/en.xml
@@ -722,5 +722,6 @@
 		<string name="unmount_sys_install">Unmount System before installing a ZIP</string>
 		<string name="unmount_system">Unmounting System...</string>
 		<string name="unmount_system_err">Failed unmounting System</string>
+		<string name="fbe_wipe_msg">WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.</string>
 	</resources>
 </language>
diff --git a/partition.cpp b/partition.cpp
old mode 100644
new mode 100755
index faffcd3..b47c294
--- a/partition.cpp
+++ b/partition.cpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2013 to 2017 TeamWin
+	Copyright 2013 to 2020 TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -19,17 +19,20 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/stat.h>
-#include <sys/vfs.h>
 #include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 #include <dirent.h>
-#include <libgen.h>
-#include <zlib.h>
-#include <iostream>
-#include <sstream>
-#include <sys/param.h>
 #include <fcntl.h>
+#include <grp.h>
+#include <iostream>
+#include <libgen.h>
+#include <pwd.h>
+#include <zlib.h>
+#include <sstream>
 
 #include "cutils/properties.h"
 #include "libblkid/include/blkid.h"
@@ -1632,6 +1635,11 @@
 	bool wiped = false, update_crypt = false, recreate_media = true;
 	int check;
 	string Layout_Filename = Mount_Point + "/.layout_version";
+	ext4_encryption_policy policy;
+
+	if (Mount_Point == "/data" && TWFunc::get_cache_dir() == AB_CACHE_DIR && Is_Decrypted) {
+		TWFunc::Get_Encryption_Policy(policy, AB_CACHE_DIR);
+	}
 
 	if (!Can_Be_Wiped) {
 		gui_msg(Msg(msg::kError, "cannot_wipe=Partition {1} cannot be wiped.")(Display_Name));
@@ -1648,6 +1656,11 @@
 
 	if (Has_Data_Media && Current_File_System == New_File_System) {
 		wiped = Wipe_Data_Without_Wiping_Media();
+		if (Mount_Point == "/data" && TWFunc::get_cache_dir() == AB_CACHE_DIR) {
+			bool created = Recreate_AB_Cache_Dir(policy);
+			if (created)
+				gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir()));
+		}
 		recreate_media = false;
 	} else {
 		DataManager::GetValue(TW_RM_RF_VAR, check);
@@ -1677,9 +1690,6 @@
 	}
 
 	if (wiped) {
-		if (Mount_Point == "/cache")
-			DataManager::Output_Version();
-
 		if (TWFunc::Path_Exists("/.layout_version") && Mount(false))
 			TWFunc::copy_file("/.layout_version", Layout_Filename, 0600);
 
@@ -1991,10 +2001,10 @@
 		gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
 		return false;
 	}
-	if (!UnMount(true))
-		goto exit;
 
 #ifdef TW_INCLUDE_CRYPTO
+	if (!UnMount(true))
+		return false;
 	if (Is_Decrypted && !Decrypted_Block_Device.empty()) {
 		if (delete_crypto_blk_dev((char*)("userdata")) != 0) {
 			LOGERR("Error deleting crypto block device, continuing anyway.\n");
@@ -2014,7 +2024,10 @@
 		}
 		DataManager::SetValue(TW_IS_ENCRYPTED, 0);
 #ifndef TW_OEM_BUILD
-		gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again.");
+		if (Is_FBE)
+			gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir()));
+		else
+			gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again.");
 #endif
 		ret = true;
 		if (!Key_Directory.empty())
@@ -2080,6 +2093,9 @@
 }
 
 bool TWPartition::Wipe_EXTFS(string File_System) {
+	if (!UnMount(true))
+		return false;
+
 #if PLATFORM_SDK_VERSION < 28
 	if (!TWFunc::Path_Exists("/sbin/mke2fs"))
 #else
@@ -2219,11 +2235,10 @@
 
 bool TWPartition::Wipe_FAT() {
 	string command;
+	if (!UnMount(true))
+		return false;
 
 	if (TWFunc::Path_Exists("/sbin/mkfs.fat")) {
-		if (!UnMount(true))
-			return false;
-
 		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.fat"));
 		Find_Actual_Block_Device();
 		command = "mkfs.fat " + Actual_Block_Device;
@@ -2246,11 +2261,10 @@
 
 bool TWPartition::Wipe_EXFAT() {
 	string command;
+	if (!UnMount(true))
+		return false;
 
 	if (TWFunc::Path_Exists("/sbin/mkexfatfs")) {
-		if (!UnMount(true))
-			return false;
-
 		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkexfatfs"));
 		Find_Actual_Block_Device();
 		command = "mkexfatfs " + Actual_Block_Device;
@@ -2317,6 +2331,8 @@
 
 bool TWPartition::Wipe_F2FS() {
 	string command;
+	if (!UnMount(true))
+		return false;
 
 	if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) {
 		bool NeedPreserveFooter = true;
@@ -2327,8 +2343,6 @@
 			gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
 			return false;
 		}
-		if (!UnMount(true))
-			return false;
 
 		/**
 		 * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
@@ -2402,6 +2416,9 @@
 	string command;
 	string Ntfsmake_Binary;
 
+	if (!UnMount(true))
+		return false;
+
 	if (TWFunc::Path_Exists("/sbin/mkntfs"))
 		Ntfsmake_Binary = "mkntfs";
 	else if (TWFunc::Path_Exists("/sbin/mkfs.ntfs"))
@@ -2409,9 +2426,6 @@
 	else
 		return false;
 
-	if (!UnMount(true))
-		return false;
-
 	gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)(Ntfsmake_Binary));
 	Find_Actual_Block_Device();
 	command = "/sbin/" + Ntfsmake_Binary + " " + Actual_Block_Device;
@@ -2435,7 +2449,6 @@
 
 	if (!Mount(true))
 		return false;
-
 	gui_msg("wiping_data=Wiping data without wiping /data/media ...");
 	ret = Wipe_Data_Without_Wiping_Media_Func(Mount_Point + "/");
 	if (ret)
@@ -2444,6 +2457,59 @@
 #endif // ifdef TW_OEM_BUILD
 }
 
+bool TWPartition::Recreate_AB_Cache_Dir(const ext4_encryption_policy &policy) {
+	struct passwd pd;
+	struct passwd *pwdptr = &pd;
+	struct passwd *tempPd;
+	char pwdBuf[512];
+	int uid = 0, gid = 0;
+
+	if ((getpwnam_r("system", pwdptr, pwdBuf, sizeof(pwdBuf), &tempPd)) != 0) {
+		LOGERR("unable to get system user id\n");
+		return false;
+	} else {
+		struct group grp;
+		struct group *grpptr = &grp;
+		struct group *tempGrp;
+		char grpBuf[512];
+
+		if ((getgrnam_r("cache", grpptr, grpBuf, sizeof(grpBuf), &tempGrp)) != 0) {
+			LOGERR("unable to get cache group id\n");
+			return false;
+		} else {
+			uid = pd.pw_uid;
+			gid = grp.gr_gid;
+
+			if (!TWFunc::Create_Dir_Recursive(AB_CACHE_DIR, S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, uid, gid)) {
+				LOGERR("Unable to recreate %s\n", AB_CACHE_DIR);
+				return false;
+			}
+			if (setfilecon(AB_CACHE_DIR, "u:object_r:cache_file:s0") != 0) {	
+				LOGERR("Unable to set contexts for %s\n", AB_CACHE_DIR);
+				return false;
+			}
+			char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+			policy_to_hex(policy.master_key_descriptor, policy_hex);
+			LOGINFO("setting policy for %s: %s\n", policy_hex, AB_CACHE_DIR);
+			if (sizeof(policy.master_key_descriptor) > 0) {
+				if (!TWFunc::Set_Encryption_Policy(AB_CACHE_DIR, policy)) {
+					LOGERR("Unable to set encryption policy for %s\n", AB_CACHE_DIR);
+					LOGINFO("Removing %s\n", AB_CACHE_DIR);
+					int ret = TWFunc::removeDir(AB_CACHE_DIR, true);
+					if (ret == -1) {
+						LOGERR("Unable to remove %s\n", AB_CACHE_DIR);
+					}
+					return false;
+				}
+			} else {
+				LOGERR("Not setting empty policy to %s\n", AB_CACHE_DIR);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
 bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unused) {
 	string dir;
 
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 64f39e4..f9adb02 100755
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -1487,9 +1487,6 @@
 	TWPartition* dat = Find_Partition_By_Path("/data");
 
 	if (dat != NULL) {
-		if (!dat->UnMount(true))
-			return false;
-
 		return dat->Wipe_Encryption();
 	} else {
 		gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/data"));
diff --git a/partitions.hpp b/partitions.hpp
old mode 100644
new mode 100755
index 92970d9..5a52fa3
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -26,6 +26,7 @@
 #include "exclude.hpp"
 #include "tw_atomic.hpp"
 #include "progresstracking.hpp"
+#include "ext4crypt_tar.h"
 
 #define MAX_FSTAB_LINE_LENGTH 2048
 
@@ -207,6 +208,7 @@
 	bool Wipe_NTFS();                                                         // Uses mkntfs to wipe
 	bool Wipe_Data_Without_Wiping_Media();                                    // Uses rm -rf to wipe but does not wipe /data/media
 	bool Wipe_Data_Without_Wiping_Media_Func(const string& parent);           // Uses rm -rf to wipe but does not wipe /data/media
+	bool Recreate_AB_Cache_Dir(const ext4_encryption_policy &policy);				  // Recreate AB_CACHE_DIR after wipe
 	void Wipe_Crypto_Key();                                                   // Wipe crypto key from either footer or block device
 	bool Backup_Tar(PartitionSettings *part_settings, pid_t *tar_fork_pid);   // Backs up using tar for file systems
 	bool Backup_Image(PartitionSettings *part_settings);                      // Backs up using raw read/write for emmc memory types
@@ -281,7 +283,7 @@
 	bool SlotSelect;                                                          // Partition has A/B slots
 	TWExclude backup_exclusions;                                              // Exclusions for file based backups
 	TWExclude wipe_exclusions;                                                // Exclusions for file based wipes (data/media devices only)
-	string Key_Directory;                                                      // Metadata key directory needed for mounting FBE encrypted data partitions using metadata encryption
+	string Key_Directory;                                                     // Metadata key directory needed for mounting FBE encrypted data partitions using metadata encryption
 
 	struct partition_fs_flags_struct {                                        // This struct is used to store mount flags and options for different file systems for the same partition
 		string File_System;
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index 593ad9d..2f563d9 100755
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -640,7 +640,9 @@
 int TWFunc::tw_reboot(RebootCommand command)
 {
 	DataManager::Flush();
-	Update_Log_File();
+	if (!Is_Data_Wiped("/data"))
+		Update_Log_File();
+
 	// Always force a sync before we reboot
 	sync();
 
@@ -1333,4 +1335,50 @@
 #endif
 }
 
+bool TWFunc::Get_Encryption_Policy(ext4_encryption_policy &policy, std::string path) {
+	if (!TWFunc::Path_Exists(path)) {
+		LOGERR("Unable to find %s to get policy\n", path.c_str());
+		return false;
+	}
+	if (!e4crypt_policy_get_struct(path.c_str(), &policy)) {
+		LOGERR("No policy set for path %s\n", path.c_str());
+		return false;
+	}
+	return true;
+}
+
+bool TWFunc::Set_Encryption_Policy(std::string path, const ext4_encryption_policy &policy) {
+	if (!TWFunc::Path_Exists(path)) {
+		LOGERR("unable to find %s to set policy\n", path.c_str());
+		return false;
+	}
+	char binary_policy[EXT4_KEY_DESCRIPTOR_SIZE];
+	char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+	policy_to_hex(binary_policy, policy_hex);
+	if (!e4crypt_policy_set_struct(path.c_str(), &policy)) {
+		LOGERR("unable to set policy for path: %s\n", path.c_str());
+		return false;
+	}
+	return true;
+}
+
+bool TWFunc::Is_Data_Wiped(std::string path) {
+	DIR* d = opendir(path.c_str());
+	size_t file_count = 0;
+	if (d != NULL) {
+		struct dirent* de;
+		while ((de = readdir(d)) != NULL) {
+			LOGINFO("file: %s\n", de->d_name);
+			if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+				continue;
+			if (strncmp(de->d_name, "lost+found", 10) == 0 || strncmp(de->d_name, "media", 5) == 0)
+				continue;
+			file_count++;
+
+		}
+		closedir(d);
+	}
+	LOGINFO("file_count: %zu\n", file_count);
+	return file_count == 0;
+}
 #endif // ndef BUILD_TWRPTAR_MAIN
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
index 9d53ab6..9c83788 100755
--- a/twrp-functions.hpp
+++ b/twrp-functions.hpp
@@ -21,8 +21,10 @@
 
 #include <string>
 #include <vector>
+#include <ext4_utils/ext4_crypt.h>
 
 #include "twrpDigest/twrpDigest.hpp"
+#include "ext4crypt_tar.h"
 
 #ifndef BUILD_TWRPTAR_MAIN
 #include "partitions.hpp"
@@ -52,6 +54,7 @@
 	COMPRESSED_ENCRYPTED
 };
 
+
 // Partition class
 class TWFunc
 {
@@ -113,6 +116,9 @@
 	static void check_selinux_support(); // print whether selinux support is enabled to console
 	static bool Is_TWRP_App_In_System(); // Check if the TWRP app is installed in the system partition
 	static int Property_Override(string Prop_Name, string Prop_Value); // Override properties (including ro. properties)
+	static bool Get_Encryption_Policy(ext4_encryption_policy &policy, std::string path); // return encryption policy for path
+	static bool Set_Encryption_Policy(std::string path, const ext4_encryption_policy &policy); // set encryption policy for path
+	static bool Is_Data_Wiped(std::string path); // check if directory has been wiped
 
 private:
 	static void Copy_Log(string Source, string Destination);
diff --git a/twrp.cpp b/twrp.cpp
index 7689c25..036a237 100755
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -401,7 +401,8 @@
 	// Reboot
 	TWFunc::Update_Intent_File(Send_Intent);
 	delete adb_bu_fifo;
-	TWFunc::Update_Log_File();
+	if (!TWFunc::Is_Data_Wiped("/data"))
+		TWFunc::Update_Log_File();
 	gui_msg(Msg("rebooting=Rebooting..."));
 	string Reboot_Arg;
 	DataManager::GetValue("tw_reboot_arg", Reboot_Arg);