Fix the long broken format function

 * For EXT2/3:
   * Align mke2fs options with Android 9.0 fs_mgr_format.cpp.
 * For EXT4:
   * Note that make_ext4fs is still in use on Android 8.1 to workaround
     a FDE encryption bug even though mke2fs has become the default
     choice. The bug is fixed in Android 9.0+.
 * For F2FS:
   * Drop the broken "-r" option, it's never ported to O, explicitly
     pass number of sectors instead.
   * Keep all options aligned with Pie fs_mgr.
   * Check existence before start wiping, as Wipe_EXT4 does.
 * For all:
   * Calculate block size at runtime, and reserve the space specified
     via negative "length" option from fstab. Note that positive length
     will be ignored.
   * If the partition has crypto footer, and "length" is not specified
     in fstab, automatically reverse CRYPT_FOOTER_OFFSET (16384).
   * Remove the default crypto key location: footer, this is a bad
     assumption since most partitions don't use encryption! We need to
     know the real situation to decide whether to reserve crypto footer
     on format.
   * If the current action is "wipe" or "change filesystem", wipe crypto
     footer or block device after mkfs if the partition is
     unencrypted or undecrypted (e.g., wrong password).
   * If the current action is "format data", unconditionally wipe crypto
     footer or block device.

Change-Id: I7304a8ee703131ea4a08ab7c60334af28cac28b3
diff --git a/partition.cpp b/partition.cpp
index b96b969..b421598 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -258,7 +258,7 @@
 	Format_Block_Size = 0;
 	Ignore_Blkid = false;
 	Retain_Layout_Version = false;
-	Crypto_Key_Location = "footer";
+	Crypto_Key_Location = "";
 	MTP_Storage_ID = 0;
 	Can_Flash_Img = false;
 	Mount_Read_Only = false;
@@ -1603,7 +1603,7 @@
 		else if (New_File_System == "ext4")
 			wiped = Wipe_EXT4();
 		else if (New_File_System == "ext2" || New_File_System == "ext3")
-			wiped = Wipe_EXT23(New_File_System);
+			wiped = Wipe_EXTFS(New_File_System);
 		else if (New_File_System == "vfat")
 			wiped = Wipe_FAT();
 		else if (New_File_System == "exfat")
@@ -1931,68 +1931,26 @@
 	if (!base_partition->PreWipeEncryption())
 		goto exit;
 
+	Find_Actual_Block_Device();
+	if (!Is_Present) {
+		LOGINFO("Block device not present, cannot format %s.\n", Display_Name.c_str());
+		gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
+		return false;
+	}
 	if (!UnMount(true))
 		goto exit;
 
-	Has_Data_Media = false;
 #ifdef TW_INCLUDE_CRYPTO
 	if (Is_Decrypted && !Decrypted_Block_Device.empty()) {
-		if (!UnMount(true))
-			goto exit;
 		if (delete_crypto_blk_dev((char*)("userdata")) != 0) {
 			LOGERR("Error deleting crypto block device, continuing anyway.\n");
 		}
 	}
 #endif
+	Has_Data_Media = false;
 	Decrypted_Block_Device = "";
 	Is_Decrypted = false;
 	Is_Encrypted = false;
-	Find_Actual_Block_Device();
-	if (Crypto_Key_Location == "footer") {
-		int newlen, fd;
-		if (Length != 0) {
-			newlen = Length;
-			if (newlen < 0)
-				newlen = newlen * -1;
-		} else {
-			newlen = CRYPT_FOOTER_OFFSET;
-		}
-		if ((fd = open(Actual_Block_Device.c_str(), O_RDWR)) < 0) {
-			gui_print_color("warning", "Unable to open '%s' to wipe crypto key\n", Actual_Block_Device.c_str());
-		} else {
-			unsigned int block_count;
-			if ((ioctl(fd, BLKGETSIZE, &block_count)) == -1) {
-				gui_print_color("warning", "Unable to get block size for wiping crypto footer.\n");
-			} else {
-				off64_t offset = ((off64_t)block_count * 512) - newlen;
-				if (lseek64(fd, offset, SEEK_SET) == -1) {
-					gui_print_color("warning", "Unable to lseek64 for wiping crypto footer.\n");
-				} else {
-					void* buffer = malloc(newlen);
-					if (!buffer) {
-						gui_print_color("warning", "Failed to malloc for wiping crypto footer.\n");
-					} else {
-						memset(buffer, 0, newlen);
-						int ret = write(fd, buffer, newlen);
-						if (ret != newlen) {
-							gui_print_color("warning", "Failed to wipe crypto footer.\n");
-						} else {
-							LOGINFO("Successfully wiped crypto footer.\n");
-						}
-						free(buffer);
-					}
-				}
-			}
-			close(fd);
-		}
-	} else {
-		if (TWFunc::IOCTL_Get_Block_Size(Crypto_Key_Location.c_str()) >= 16384LLU) {
-			string Command = "dd of='" + Crypto_Key_Location + "' if=/dev/zero bs=16384 count=1";
-			TWFunc::Exec_Cmd(Command);
-		} else {
-			LOGINFO("Crypto key location reports size < 16K so not wiping crypto footer.\n");
-		}
-	}
 	if (Wipe(Fstab_File_System)) {
 		Has_Data_Media = Save_Data_Media;
 		if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
@@ -2067,33 +2025,17 @@
 	}
 }
 
-bool TWPartition::Wipe_EXT23(string File_System) {
-	if (!UnMount(true))
-		return false;
-
-	if (TWFunc::Path_Exists("/sbin/mke2fs")) {
-		string command;
-
-		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mke2fs"));
-		Find_Actual_Block_Device();
-		command = "mke2fs -t " + File_System + " -m 0 " + Actual_Block_Device;
-		LOGINFO("mke2fs command: %s\n", command.c_str());
-		if (TWFunc::Exec_Cmd(command) == 0) {
-			Current_File_System = File_System;
-			Recreate_AndSec_Folder();
-			gui_msg("done=Done.");
-			return true;
-		} else {
-			gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
-			return false;
-		}
-	} else
+bool TWPartition::Wipe_EXTFS(string File_System) {
+#if PLATFORM_SDK_VERSION < 28
+	if (!TWFunc::Path_Exists("/sbin/mke2fs"))
+#else
+	if (!TWFunc::Path_Exists("/sbin/mke2fs") || !TWFunc::Path_Exists("/sbin/e2fsdroid"))
+#endif
 		return Wipe_RMRF();
 
-	return false;
-}
+	int ret;
+	bool NeedPreserveFooter = true;
 
-bool TWPartition::Wipe_EXT4() {
 	Find_Actual_Block_Device();
 	if (!Is_Present) {
 		LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
@@ -2103,22 +2045,103 @@
 	if (!UnMount(true))
 		return false;
 
-#if defined(USE_EXT4)
+	/**
+	 * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
+	 * so there's no need to preserve footer.
+	 */
+	if ((Is_Decrypted && !Decrypted_Block_Device.empty()) ||
+			Crypto_Key_Location != "footer") {
+		NeedPreserveFooter = false;
+	}
+
+	unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
+	if (!dev_sz)
+		return false;
+
+	if (NeedPreserveFooter)
+		Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET;
+
+	string size_str = to_string(dev_sz / 4096);
+	string Command;
+
+	gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mke2fs"));
+
+	// Execute mke2fs to create empty ext4 filesystem
+	Command = "mke2fs -t " + File_System + " -b 4096 " + Actual_Block_Device + " " + size_str;
+	LOGINFO("mke2fs command: %s\n", Command.c_str());
+	ret = TWFunc::Exec_Cmd(Command);
+	if (ret) {
+		gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
+		return false;
+	}
+
+	if (TWFunc::Path_Exists("/sbin/e2fsdroid")) {
+		// Execute e2fsdroid to initialize selinux context
+		Command = "e2fsdroid -e -a " + Mount_Point + " " + Actual_Block_Device;
+		LOGINFO("e2fsdroid command: %s\n", Command.c_str());
+		ret = TWFunc::Exec_Cmd(Command);
+		if (ret) {
+			gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
+			return false;
+		}
+	} else {
+		LOGINFO("e2fsdroid not present\n");
+	}
+
+	if (NeedPreserveFooter)
+		Wipe_Crypto_Key();
+	Current_File_System = File_System;
+	Recreate_AndSec_Folder();
+	gui_msg("done=Done.");
+	return true;
+}
+
+bool TWPartition::Wipe_EXT4() {
+#ifdef USE_EXT4
 	int ret;
+	bool NeedPreserveFooter = true;
+
+	Find_Actual_Block_Device();
+	if (!Is_Present) {
+		LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
+		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,
+	 * so there's no need to preserve footer.
+	 */
+	if ((Is_Decrypted && !Decrypted_Block_Device.empty()) ||
+			Crypto_Key_Location != "footer") {
+		NeedPreserveFooter = false;
+	}
+
+	unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
+	if (!dev_sz)
+		return false;
+
+	if (NeedPreserveFooter)
+		Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET;
+
 	char *secontext = NULL;
 
 	gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("make_ext4fs"));
 
 	if (!selinux_handle || selabel_lookup(selinux_handle, &secontext, Mount_Point.c_str(), S_IFDIR) < 0) {
 		LOGINFO("Cannot lookup security context for '%s'\n", Mount_Point.c_str());
-		ret = make_ext4fs(Actual_Block_Device.c_str(), Length, Mount_Point.c_str(), NULL);
+		ret = make_ext4fs(Actual_Block_Device.c_str(), dev_sz, Mount_Point.c_str(), NULL);
 	} else {
-		ret = make_ext4fs(Actual_Block_Device.c_str(), Length, Mount_Point.c_str(), selinux_handle);
+		ret = make_ext4fs(Actual_Block_Device.c_str(), dev_sz, Mount_Point.c_str(), selinux_handle);
 	}
 	if (ret != 0) {
 		gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
 		return false;
 	} else {
+		if (NeedPreserveFooter)
+			Wipe_Crypto_Key();
 		string sedir = Mount_Point + "/lost+found";
 		PartitionManager.Mount_By_Path(sedir.c_str(), true);
 		rmdir(sedir.c_str());
@@ -2126,37 +2149,8 @@
 		return true;
 	}
 #else
-	if (TWFunc::Path_Exists("/sbin/make_ext4fs")) {
-		string Command;
-
-		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("make_ext4fs"));
-		Find_Actual_Block_Device();
-		Command = "make_ext4fs";
-		if (!Is_Decrypted && Length != 0) {
-			// Only use length if we're not decrypted
-			char len[32];
-			sprintf(len, "%i", Length);
-			Command += " -l ";
-			Command += len;
-		}
-		if (TWFunc::Path_Exists("/file_contexts")) {
-			Command += " -S /file_contexts";
-		}
-		Command += " -a " + Mount_Point + " " + Actual_Block_Device;
-		LOGINFO("make_ext4fs command: %s\n", Command.c_str());
-		if (TWFunc::Exec_Cmd(Command) == 0) {
-			Current_File_System = "ext4";
-			Recreate_AndSec_Folder();
-			gui_msg("done=Done.");
-			return true;
-		} else {
-			gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
-			return false;
-		}
-	} else
-		return Wipe_EXT23("ext4");
+	return Wipe_EXTFS("ext4");
 #endif
-	return false;
 }
 
 bool TWPartition::Wipe_FAT() {
@@ -2261,29 +2255,42 @@
 	string command;
 
 	if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) {
+		bool NeedPreserveFooter = true;
+
+		Find_Actual_Block_Device();
+		if (!Is_Present) {
+			LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
+			gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
+			return false;
+		}
 		if (!UnMount(true))
 			return false;
 
-		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.f2fs"));
-		Find_Actual_Block_Device();
-		if (!TWFunc::Path_Exists("/sbin/sload.f2fs")) {
-			command = "mkfs.f2fs -t 0";
-			if (!Is_Decrypted && Length != 0) {
-				// Only use length if we're not decrypted
-				char len[32];
-				int mod_length = Length;
-				if (Length < 0)
-					mod_length *= -1;
-				sprintf(len, "%i", mod_length);
-				command += " -r ";
-				command += len;
-			}
-			command += " " + Actual_Block_Device;
-		} else {
-			unsigned long long size = IOCTL_Get_Block_Size() + Length;
-			command = "mkfs.f2fs -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + std::to_string(size / 4096) + " && sload.f2fs -t /data " + Actual_Block_Device;
+		/**
+		 * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
+		 * so there's no need to preserve footer.
+		 */
+		if ((Is_Decrypted && !Decrypted_Block_Device.empty()) ||
+				Crypto_Key_Location != "footer") {
+			NeedPreserveFooter = false;
 		}
+
+		unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
+		if (!dev_sz)
+			return false;
+
+		if (NeedPreserveFooter)
+			Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET;
+
+		gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.f2fs"));
+		command = "mkfs.f2fs -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + to_string(dev_sz / 4096);
+		if (TWFunc::Path_Exists("/sbin/sload.f2fs")) {
+			command += " && sload.f2fs -t /data " + Actual_Block_Device;
+		}
+		LOGINFO("mkfs.f2fs command: %s\n", command.c_str());
 		if (TWFunc::Exec_Cmd(command) == 0) {
+			if (NeedPreserveFooter)
+				Wipe_Crypto_Key();
 			Recreate_AndSec_Folder();
 			gui_msg("done=Done.");
 			return true;
@@ -2381,6 +2388,52 @@
 	return false;
 }
 
+void TWPartition::Wipe_Crypto_Key() {
+	Find_Actual_Block_Device();
+	if (Crypto_Key_Location.empty())
+		return;
+	else if (Crypto_Key_Location == "footer") {
+		int fd = open(Actual_Block_Device.c_str(), O_RDWR);
+		if (fd < 0) {
+			gui_print_color("warning", "Unable to open '%s' to wipe crypto key\n", Actual_Block_Device.c_str());
+			return;
+		}
+
+		unsigned int block_count;
+		if ((ioctl(fd, BLKGETSIZE, &block_count)) == -1) {
+			gui_print_color("warning", "Unable to get block size for wiping crypto footer.\n");
+		} else {
+			int newlen = Length < 0 ? -Length : CRYPT_FOOTER_OFFSET;
+			off64_t offset = ((off64_t)block_count * 512) - newlen;
+			if (lseek64(fd, offset, SEEK_SET) == -1) {
+				gui_print_color("warning", "Unable to lseek64 for wiping crypto footer.\n");
+			} else {
+				void* buffer = malloc(newlen);
+				if (!buffer) {
+					gui_print_color("warning", "Failed to malloc for wiping crypto footer.\n");
+				} else {
+					memset(buffer, 0, newlen);
+					int ret = write(fd, buffer, newlen);
+					if (ret != newlen) {
+						gui_print_color("warning", "Failed to wipe crypto footer.\n");
+					} else {
+						LOGINFO("Successfully wiped crypto footer.\n");
+					}
+					free(buffer);
+				}
+			}
+		}
+		close(fd);
+	} else {
+		if (TWFunc::IOCTL_Get_Block_Size(Crypto_Key_Location.c_str()) >= 16384LLU) {
+			string Command = "dd of='" + Crypto_Key_Location + "' if=/dev/zero bs=16384 count=1";
+			TWFunc::Exec_Cmd(Command);
+		} else {
+			LOGINFO("Crypto key location reports size < 16K so not wiping crypto footer.\n");
+		}
+	}
+}
+
 bool TWPartition::Backup_Tar(PartitionSettings *part_settings, pid_t *tar_fork_pid) {
 	string Full_FileName;
 	twrpTar tar;