vold_decrypt: Add android 9.0 support

* build modified vdc_pie binary with 'checkpw' command support
  if building with Android 9.0 platform. That command and others
  we don't care about, are removed from Pie vdc. Our vdc_pie will
  run if system sdk version is > 27, otherwise system vdc is used.
  Code adapted from Android 9.0 system/vold/vdc.

* include prebuilt vdc_pie(arm, arm64) binary if building with lower
  than Android 9.0 platform - vdc_pie cannot be build from source
  with those platforms without additional imports from Android 9.0

* skip vdc "getpwtype" command for Pie - vds communicates with vold
  directly, no need for connection retries first

* add /system/bin/servicemanager to required services

* mount per-devive additional partitions needed for decryption
  listed with device BoardConfig.mk TW_CRYPTO_SYSTEM_VOLD_MOUNT
  flag like(space separated):
  TW_CRYPTO_SYSTEM_VOLD_MOUNT := vendor cust odm

* add function to backup crypto footer before running vdc commands
  and restore it after - on Xiaomi Mi Max 3 both Oreo and Pie stock
  roms vold alters cripto footer when decrypting data in recovery
  which causes system to ask for crypto password at next reboot
  although password stays unchanged. Crypto footer backup/restore
  added as workaround for systems whit ro.build.version.sdk > 25.
  Also to preserve crypto footer integrity decryption attempts are
  skipped if footer backup fails to ensure no data loss.
  Code adapted from
  https://gerrit.omnirom.org/#/c/android_bootable_recovery/+/31206/

Change-Id: I0a383f3843578fa55595cfea3b7c9c4431646a1a
diff --git a/crypto/vold_decrypt/vold_decrypt.cpp b/crypto/vold_decrypt/vold_decrypt.cpp
index 707466e..ac872ea 100644
--- a/crypto/vold_decrypt/vold_decrypt.cpp
+++ b/crypto/vold_decrypt/vold_decrypt.cpp
@@ -74,6 +74,7 @@
 #define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
 
 FILE *fp_kmsg = NULL;
+int sdkver = 20;
 
 
 /* Debugging Functions */
@@ -223,7 +224,7 @@
 				break;
 			LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str());
 			utimeout -= SLEEP_MIN_USEC;
-			usleep(SLEEP_MIN_USEC);;
+			usleep(SLEEP_MIN_USEC);
 		}
 	}
 	property_get(property_name.c_str(), prop_value, "error");
@@ -766,7 +767,6 @@
 void Set_Needed_Properties(void) {
 	// vold won't start without ro.storage_structure on Kitkat
 	string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
-	int sdkver = 20;
 	if (!sdkverstr.empty()) {
 		sdkver = atoi(sdkverstr.c_str());
 	}
@@ -775,8 +775,79 @@
 		if (!ro_storage_structure.empty())
 			property_set("ro.storage_structure", ro_storage_structure.c_str());
 	}
+	property_set("hwservicemanager.ready", "false");
+	property_set("sys.listeners.registered", "false");
+	property_set("vendor.sys.listeners.registered", "false");
 }
 
+static unsigned int get_blkdev_size(int fd) {
+	unsigned long nr_sec;
+
+	if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+		nr_sec = 0;
+	}
+
+	return (unsigned int) nr_sec;
+}
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+static char footer[16 * 1024];
+const char* userdata_path;
+static off64_t offset;
+
+int footer_br(const string& command) {
+	int fd;
+
+	if (command == "backup") {
+		unsigned int nr_sec;
+		TWPartition* userdata = PartitionManager.Find_Partition_By_Path("/data");
+		userdata_path = userdata->Actual_Block_Device.c_str();
+		fd = open(userdata_path, O_RDONLY);
+		if (fd < 0) {
+			LOGERROR("E:footer_backup: Cannot open '%s': %s\n", userdata_path, strerror(errno));
+			return -1;
+		}
+		if ((nr_sec = get_blkdev_size(fd))) {
+			offset = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+		} else {
+			LOGERROR("E:footer_br: Failed to get offset\n");
+			close(fd);
+			return -1;
+		}
+		if (lseek64(fd, offset, SEEK_SET) == -1) {
+			LOGERROR("E:footer_backup: Failed to lseek64\n");
+			close(fd);
+			return -1;
+		}
+		if (read(fd, footer, sizeof(footer)) != sizeof(footer)) {
+			LOGERROR("E:footer_br: Failed to read: %s\n", strerror(errno));
+			close(fd);
+			return -1;
+		}
+		close(fd);
+	} else if (command == "restore") {
+		fd = open(userdata_path, O_WRONLY);
+		if (fd < 0) {
+			LOGERROR("E:footer_restore: Cannot open '%s': %s\n", userdata_path, strerror(errno));
+			return -1;
+		}
+		if (lseek64(fd, offset, SEEK_SET) == -1) {
+			LOGERROR("E:footer_restore: Failed to lseek64\n");
+			close(fd);
+			return -1;
+		}
+		if (write(fd, footer, sizeof(footer)) != sizeof(footer)) {
+			LOGERROR("E:footer_br: Failed to write: %s\n", strerror(errno));
+			close(fd);
+			return -1;
+		}
+		close(fd);
+	} else {
+		LOGERROR("E:footer_br: wrong command argument: %s\n", command.c_str());
+		return -1;
+	}
+	return 0;
+}
 
 /* vdc Functions */
 typedef struct {
@@ -801,9 +872,14 @@
 		}
 	}
 
-	const char *cmd[] = { "/system/bin/vdc", "cryptfs" };
+	// getpwtype and checkpw commands are removed from Pie vdc, using modified vdc_pie
+	const char *cmd[] = { "/sbin/vdc_pie", "cryptfs" };
+	if (sdkver < 28)
+		cmd[0] = "/system/bin/vdc";
 	const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL };
 
+	LOGINFO("sdkver: %d, using %s\n", sdkver, cmd[0]);
+
 #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
 	string log_name = "/tmp/strace_vdc_" + command;
 #endif
@@ -918,6 +994,9 @@
 					return -1;
 				}
 			}
+			if (sdkver >= 28) {
+				return WEXITSTATUS(status);
+			}
 			return 0;
 		}
 	}
@@ -930,33 +1009,35 @@
 
 	LOGINFO("About to run vdc...\n");
 
-	// Wait for vold connection
-	gettimeofday(&t1, NULL);
-	t2 = t1;
-	while ((t2.tv_sec - t1.tv_sec) < 5) {
-		// cryptfs getpwtype returns: R1=213(PasswordTypeResult)   R2=?   R3="password", "pattern", "pin", "default"
-		res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult);
-		if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) {
-			res = 0;
-			break;
+	// Pie vdc communicates with vold directly, no socket so lets not waste time
+	if (sdkver < 28) {
+		// Wait for vold connection
+		gettimeofday(&t1, NULL);
+		t2 = t1;
+		while ((t2.tv_sec - t1.tv_sec) < 5) {
+			// cryptfs getpwtype returns: R1=213(PasswordTypeResult)   R2=?   R3="password", "pattern", "pin", "default"
+			res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult);
+			if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) {
+				res = 0;
+				break;
+			}
+			LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str());
+			usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
+			gettimeofday(&t2, NULL);
 		}
-		LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str());
-		usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
-		gettimeofday(&t2, NULL);
+
+		if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5)
+			LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str());
+		else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
+			return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype
+		else if (res)
+			return VD_ERR_FORK_EXECL_ERROR;
+		else if (vdcResult.ResponseCode != -1)
+			return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
+		else
+			return VD_ERR_VDC_FAILED_TO_CONNECT;
 	}
 
-	if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5)
-		LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str());
-	else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
-		return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype
-	else if (res)
-		return VD_ERR_FORK_EXECL_ERROR;
-	else if (vdcResult.ResponseCode != -1)
-		return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
-	else
-		return VD_ERR_VDC_FAILED_TO_CONNECT;
-
-
 	// Input password from GUI, or default password
 	res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult);
 	if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
@@ -970,6 +1051,12 @@
 		return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
 	*/
 
+	// our vdc returns vold binder op status,
+    // we care about status.ok() only which is 0
+	if (sdkver >= 28) {
+		vdcResult.Message = res;
+	}
+
 	if (vdcResult.Message != 0) {
 		// try falling back to Lollipop hex passwords
 		string hexPassword = convert_key_to_hex_ascii(Password);
@@ -986,8 +1073,9 @@
 		*/
 	}
 
-	// vdc's return value is dependant upon source origin, it will either
+	// sdk < 28 vdc's return value is dependant upon source origin, it will either
 	// return 0 or ResponseCode, so disregard and focus on decryption instead
+	// our vdc always returns 0 on success
 	if (vdcResult.Message == 0) {
 		// Decryption successful wait for crypto blk dev
 		Wait_For_Property("ro.crypto.fs_crypto_blkdev");
@@ -1024,6 +1112,20 @@
 		return VD_ERR_MISSING_VDC;
 	}
 
+#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
+	vector<string> partitions = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_MOUNT, " ");
+	for (size_t i = 0; i < partitions.size(); ++i) {
+		string mnt_point = "/" + partitions[i];
+		if(PartitionManager.Find_Partition_By_Path(mnt_point)) {
+			if (!PartitionManager.Mount_By_Path(mnt_point, true)) {
+				LOGERROR("Unable to mount %s\n", mnt_point.c_str());
+				return VD_ERR_UNABLE_TO_MOUNT_EXTRA;
+			}
+			LOGINFO("%s partition mounted\n", partitions[i].c_str());
+		}
+	}
+#endif
+
 	fp_kmsg = fopen("/dev/kmsg", "a");
 
 	LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n");
@@ -1064,8 +1166,20 @@
 	LOGINFO("Starting services...\n");
 #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
 	for (size_t i = 0; i < Services.size(); ++i) {
-		if (Services[i].bin_exists)
+		if (Services[i].bin_exists) {
+			if (Services[i].Service_Binary.find("keymaster") != string::npos) {
+				Wait_For_Property("hwservicemanager.ready", 500000, "true");
+				LOGINFO("    hwservicemanager is ready.\n");
+			}
+
 			Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name);
+
+			if (Services[i].Service_Binary == "qseecomd") {
+				if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
+						|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
+					LOGINFO("    qseecomd listeners registered.\n");
+			}
+		}
 	}
 #endif
 	is_vold_running = Start_Service("sys_vold");
@@ -1080,7 +1194,29 @@
 			}
 		}
 #endif
-		res = Run_vdc(Password);
+
+		/*
+		* Oreo and Pie vold on some devices alters footer causing
+		* system to ask for decryption password at next boot although
+		* password haven't changed so we save footer before and restore it
+		* after vold operations
+		*/
+		if (sdkver > 25) {
+			if (footer_br("backup") == 0) {
+				LOGINFO("footer_br: crypto footer backed up\n");
+				res = Run_vdc(Password);
+				if (footer_br("restore") == 0)
+					LOGINFO("footer_br: crypto footer restored\n");
+				else
+					LOGERROR("footer_br: Failed to restore crypto footer\n");
+			} else {
+				LOGERROR("footer_br: Failed to backup crypto footer, \
+					skipping decrypt to prevent data loss. Reboot recovery to try again...\n");
+				res = -1;
+			}
+		} else {
+			res = Run_vdc(Password);
+		}
 
 		if (res != 0) {
 			LOGINFO("Decryption failed\n");
@@ -1116,13 +1252,38 @@
 		umount2(PartitionManager.Get_Android_Root_Path().c_str(), MNT_DETACH);
 	}
 
+#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
+	for (size_t i = 0; i < partitions.size(); ++i) {
+		string mnt_point = "/" + partitions[i];
+		if(PartitionManager.Is_Mounted_By_Path(mnt_point)) {
+			if (!PartitionManager.UnMount_By_Path(mnt_point, true)) {
+				LOGINFO("WARNING: %s partition could not be unmounted normally!\n", partitions[i].c_str());
+				umount2(mnt_point.c_str(), MNT_DETACH);
+			}
+		}
+	}
+#endif
+
 	LOGINFO("Finished.\n");
 
 #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+	Set_Needed_Properties();
 	// Restart previously running services
 	for (size_t i = 0; i < Services.size(); ++i) {
-		if (Services[i].resume)
+		if (Services[i].resume) {
+			if (Services[i].Service_Binary.find("keymaster") != string::npos) {
+				Wait_For_Property("hwservicemanager.ready", 500000, "true");
+				LOGINFO("    hwservicemanager is ready.\n");
+			}
+
 			Start_Service(Services[i].TWRP_Service_Name);
+
+			if (Services[i].Service_Binary == "qseecomd") {
+				if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
+						|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
+					LOGINFO("    qseecomd listeners registered.\n");
+			}
+		}
 	}
 #endif