ADB: Add adb backup for TWRP.

Functionality for client side to backup
tar and image streams over adbd to the client under backup.ab.

Using adb backup on the client side you can backup the partitions
TWRP knows about.

On the client side you can do the following:
adb backup -f <filename> --twrp <options> where options are
--compress: compress data
system: backup system
cache: backup cache
data: backup data
boot: backup boot
etc for each partition.

You can string multiple options,
i.e. adb backup -f <filename> --twrp --compress cache system data

adb backup in TWRP will take any option corresponding
to TWRP fstab partitions, e.g. efs boot as well.

If you do not specify the filename with the -f option,
adb will backup your data to a filename backup.ab on the client.
You can then rename the file and encrypt it with desktop tools.

If you don't want to use command line arguments:
adb backup --twrp

will bring up the gui and allow you to choose partitions
from the backup page.

To restore the backup use the following convention:
adb restore <filename>

Structures are used to store metadata in binary inside
of the file itself. If the metadata structure is modified,
update the adb version so that it will invalidate older
backups and not cause issues on restore. When restoring,
we currently do not support picking specific partitions.
It's all or nothing.

Change-Id: Idb92c37fc9801dc8d89ed2a4570e9d12e76facf8
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
index 75db902..0d2268c 100644
--- a/openrecoveryscript.cpp
+++ b/openrecoveryscript.cpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	Copyright 2016 TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -16,6 +16,7 @@
 	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#define __STDC_FORMAT_MACROS 1
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,21 +27,31 @@
 #include <dirent.h>
 #include <time.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <iostream>
 #include <fstream>
+#include <sstream>
+#include <string>
+#include <iterator>
+#include <algorithm>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <zlib.h>
 
 #include "twrp-functions.hpp"
 #include "partitions.hpp"
 #include "twcommon.h"
 #include "openrecoveryscript.hpp"
+#include "progresstracking.hpp"
 #include "variables.h"
 #include "adb_install.h"
 #include "data.hpp"
 #include "adb_install.h"
 #include "fuse_sideload.h"
 #include "gui/gui.hpp"
+#include "gui/pages.hpp"
+#include "orscmd/orscmd.h"
+#include "adbbu/libtwadbbu.hpp"
 extern "C" {
 	#include "twinstall.h"
 	#include "gui/gui.h"
@@ -139,6 +150,8 @@
 				// Wipe
 				if (strcmp(value, "cache") == 0 || strcmp(value, "/cache") == 0) {
 					PartitionManager.Wipe_By_Path("/cache");
+				} else if (strcmp(value, "system") == 0 || strcmp(value, "/system") == 0) {
+					PartitionManager.Wipe_By_Path("/system");
 				} else if (strcmp(value, "dalvik") == 0 || strcmp(value, "dalvick") == 0 || strcmp(value, "dalvikcache") == 0 || strcmp(value, "dalvickcache") == 0) {
 					PartitionManager.Wipe_Dalvik_Cache();
 				} else if (strcmp(value, "data") == 0 || strcmp(value, "/data") == 0 || strcmp(value, "factory") == 0 || strcmp(value, "factoryreset") == 0) {
@@ -280,6 +293,17 @@
 					ret_val = 1;
 				else
 					gui_msg("done=Done.");
+			} else if (strncmp(command, "adbbackup", 9) == 0) {
+				ret_val = Backup_ADB_Command(value);
+				if (ret_val == 1) {
+					twadbbu::Write_TWERROR();
+					gui_err("adbbackup_error=Error with ADB Backup. Quitting...");
+				}
+			} else if (strcmp(command, "adbrestore") == 0) {
+				LOGINFO("running adb restore\n");
+				ret_val = Restore_ADB_Backup();
+			} else if (strcmp(command, "remountrw") == 0) {
+				ret_val = remountrw();
 			} else if (strcmp(command, "mount") == 0) {
 				// Mount
 				DataManager::SetValue("tw_action_text2", gui_parse_text("{@mounting}"));
@@ -400,6 +424,7 @@
 		gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(SCRIPT_FILE_TMP)(strerror(errno)));
 		return 1;
 	}
+
 	if (install_cmd && DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
 		gui_msg("injecttwrp=Injecting TWRP into boot image...");
 		TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
@@ -485,6 +510,72 @@
 	return ret_val;
 }
 
+int OpenRecoveryScript::Backup_ADB_Command(std::string Options) {
+	std::vector<std::string> args;
+	std::string Backup_List;
+	bool adbbackup = true, ret = false;
+	std::string rmopt = "--";
+
+	std::replace(Options.begin(), Options.end(), ':', ' ');
+	args = TWFunc::Split_String(Options, " ");
+
+	DataManager::SetValue(TW_USE_COMPRESSION_VAR, 0);
+	DataManager::SetValue(TW_SKIP_MD5_GENERATE_VAR, 0);
+
+	if (args[1].compare("--twrp") != 0) {
+		gui_err("twrp_adbbu_option=--twrp option is required to enable twrp adb backup");
+		sleep(2);
+		return 1;
+	}
+
+	for (unsigned i = 2; i < args.size(); i++) {
+		int compress;
+
+		std::string::size_type size = args[i].find(rmopt);
+		if (size != std::string::npos)
+			args[i].erase(size, rmopt.length());
+
+		if (args[i].compare("compress") == 0) {
+			gui_msg("compression_on=Compression is on");
+			DataManager::SetValue(TW_USE_COMPRESSION_VAR, 1);
+			continue;
+		}
+		DataManager::GetValue(TW_USE_COMPRESSION_VAR, compress);
+		gui_print("%s\n", args[i].c_str());
+		std::string path;
+		path = "/" + args[i];
+		TWPartition* part = PartitionManager.Find_Partition_By_Path(path);
+		if (part) {
+			Backup_List += path;
+			Backup_List += ";";
+		}
+		else {
+			gui_msg(Msg(msg::kError, "partition_not_found=path: {1} not found in partition list")(path));
+			return 1;
+		}
+	}
+
+	if (Backup_List.empty()) {
+		DataManager::GetValue("tw_backup_list", Backup_List);
+		if (Backup_List.empty()) {
+			gui_err("no_partition_selected=No partitions selected for backup.");
+			return 1;
+		}
+	}
+	else
+		DataManager::SetValue("tw_backup_list", Backup_List);
+
+	ret = PartitionManager.Run_Backup(adbbackup);
+	DataManager::SetValue(TW_BACKUP_NAME, gui_lookup("auto_generate", "(Auto Generate)"));
+	if (!ret) {
+		gui_err("backup_fail=Backup failed");
+		return 1;
+	}
+	gui_msg("backup_complete=Backup Complete");
+	sleep(2); //give time for user to see messages on console
+	return 0;
+}
+
 string OpenRecoveryScript::Locate_Zip_File(string Zip, string Storage_Root) {
 	string Path = TWFunc::Get_Path(Zip);
 	string File = TWFunc::Get_Filename(Zip);
@@ -513,6 +604,7 @@
 	char value1[SCRIPT_COMMAND_SIZE];
 	int line_len, i;
 	string Backup_List;
+	bool adbbackup = false;
 
 	strcpy(value1, Options.c_str());
 
@@ -558,7 +650,7 @@
 		}
 	}
 	DataManager::SetValue("tw_backup_list", Backup_List);
-	if (!PartitionManager.Run_Backup()) {
+	if (!PartitionManager.Run_Backup(false)) {
 		gui_err("backup_fail=Backup Failed");
 		return 1;
 	}
@@ -651,3 +743,196 @@
 	call_after_cli_command();
 	LOGINFO("Done reading ORS command from command line\n");
 }
+
+int OpenRecoveryScript::Restore_ADB_Backup(void) {
+	bool breakloop = false;
+	int partition_count = 0;
+	std::string Restore_Name;
+	std::size_t pos = 0;
+	struct AdbBackupFileTrailer adbmd5;
+	struct PartitionSettings part_settings;
+	int adb_control_twrp_fd, adb_write_fd, systemro;
+	int adb_control_bu_fd, ret = 0;
+	char cmd[512];
+	int orsfd = open(ORS_OUTPUT_FILE, O_WRONLY);
+
+	part_settings.total_restore_size = 0;
+
+	PartitionManager.Mount_All_Storage();
+	DataManager::SetValue(TW_SKIP_MD5_CHECK_VAR, 0);
+	LOGINFO("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	LOGINFO("opening TW_ADB_TWRP_CONTROL\n");
+	adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_RDONLY | O_NONBLOCK);
+	memset(&adbmd5, 0, sizeof(adbmd5));
+
+	while (!breakloop) {
+		memset(&cmd, 0, sizeof(cmd));
+		if (read(adb_control_twrp_fd, cmd, sizeof(cmd)) > 0) {
+			struct AdbBackupControlType cmdstruct;
+
+			memset(&cmdstruct, 0, sizeof(cmdstruct));
+			memcpy(&cmdstruct, cmd, sizeof(cmdstruct));
+			std::string cmdstr(cmdstruct.type);
+			std::string cmdtype = cmdstr.substr(0, sizeof(cmdstruct.type) - 1);
+			if (cmdstr.substr(0, sizeof(cmdstruct.type) - 1) == TWSTREAMHDR) {
+				struct AdbBackupStreamHeader twhdr;
+				memcpy(&twhdr, cmd, sizeof(cmd));
+				LOGINFO("ADB Partition count: %" PRIu64 "\n", twhdr.partition_count);
+				LOGINFO("ADB version: %" PRIu64 "\n", twhdr.version);
+				if (twhdr.version != ADB_BACKUP_VERSION) {
+					LOGERR("Incompatible adb backup version!\n");
+					breakloop = false;
+					break;
+				}
+				partition_count = twhdr.partition_count;
+			}
+			else if (cmdtype == MD5TRAILER) {
+				LOGINFO("Restoring MD5TRAILER\n");
+				memcpy(&adbmd5, cmd, sizeof(cmd));
+			}
+			else if (cmdtype == TWMD5) {
+				struct AdbBackupFileTrailer md5check;
+				LOGINFO("Restoring TWMD5\n");
+
+				memset(&md5check, 0, sizeof(md5check));
+				memcpy(&md5check, cmd, sizeof(cmd));
+				if (strcmp(md5check.md5, adbmd5.md5) != 0) {
+					LOGERR("md5 doesn't match!\n");
+					LOGERR("file md5: %s\n", adbmd5.md5);
+					LOGERR("check md5: %s\n", md5check.md5);
+					breakloop = true;
+					ret = 1;
+					break;
+				}
+				else {
+					LOGINFO("adbrestore md5 matches\n");
+					LOGINFO("adbmd5.md5: %s\n", adbmd5.md5);
+					LOGINFO("md5check.md5: %s\n", md5check.md5);
+				}
+			}
+			else if (cmdtype == TWENDADB) {
+				LOGINFO("received TWENDADB\n");
+				breakloop = true;
+				break;
+			}
+			else {
+				struct twfilehdr twimghdr;
+				memcpy(&twimghdr, cmd, sizeof(cmd));
+				std::string cmdstr(twimghdr.type);
+				Restore_Name = twimghdr.name;
+				part_settings.total_restore_size = twimghdr.size;
+				if (cmdtype == TWIMG) {
+					LOGINFO("ADB Type: %s\n", twimghdr.type);
+					LOGINFO("ADB Restore_Name: %s\n", Restore_Name.c_str());
+					LOGINFO("ADB Restore_size: %" PRIu64 "\n", part_settings.total_restore_size);
+					string compression = (twimghdr.compressed == 1) ? "compressed" : "uncompressed";
+					LOGINFO("ADB compression: %s\n", compression.c_str());
+					std::string Backup_FileName;
+					std::size_t pos = Restore_Name.find_last_of("/");
+					std::string path = "/" + Restore_Name.substr(pos, Restore_Name.size());
+					pos = path.find_first_of(".");
+					path = path.substr(0, pos);
+					if (path.substr(0,1).compare("//")) {
+						path = path.substr(1, path.size());
+					}
+
+					pos = Restore_Name.find_last_of("/");
+					Backup_FileName = Restore_Name.substr(pos + 1, Restore_Name.size());
+					part_settings.Part = PartitionManager.Find_Partition_By_Path(path);
+					part_settings.Restore_Name = path;
+					part_settings.partition_count = partition_count;
+					part_settings.adbbackup = true;
+					part_settings.adb_compression = twimghdr.compressed;
+					part_settings.Backup_FileName = Backup_FileName;
+					part_settings.PM_Method = PM_RESTORE;
+					ProgressTracking progress(part_settings.total_restore_size);
+					part_settings.progress = &progress;
+					if (!PartitionManager.Restore_Partition(&part_settings)) {
+						LOGERR("ADB Restore failed.\n");
+						return 1;
+					}
+				}
+				else if (cmdtype == TWFN) {
+					LOGINFO("ADB Type: %s\n", twimghdr.type);
+					LOGINFO("ADB Restore_Name: %s\n", Restore_Name.c_str());
+					LOGINFO("ADB Restore_size: %" PRIi64 "\n", part_settings.total_restore_size);
+					string compression = (twimghdr.compressed == 1) ? "compressed" : "uncompressed";
+					LOGINFO("ADB compression: %s\n", compression.c_str());
+					std::string Backup_FileName;
+					std::size_t pos = Restore_Name.find_last_of("/");
+					std::string path = "/" + Restore_Name.substr(pos, Restore_Name.size());
+					pos = path.find_first_of(".");
+					path = path.substr(0, pos);
+					if (path.substr(0,1).compare("//")) {
+						path = path.substr(1, path.size());
+					}
+
+					pos = Restore_Name.find_last_of("/");
+					Backup_FileName = Restore_Name.substr(pos + 1, Restore_Name.size());
+					pos = Restore_Name.find_last_of("/");
+					part_settings.Restore_Name = Restore_Name.substr(0, pos);
+					part_settings.Part = PartitionManager.Find_Partition_By_Path(path);
+
+					if (path.compare("/system") == 0) {
+						if (part_settings.Part->Is_Read_Only()) {
+							struct AdbBackupControlType twerror;
+							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) {
+								LOGERR("Cannot write to ADB_CONTROL_BU_FD: %s\n", strerror(errno));
+							}
+							gui_msg(Msg(msg::kError, "restore_read_only=Cannot restore {1} -- mounted read only.")(part_settings.Part->Backup_Display_Name));
+							return 1;
+
+						}
+					}
+					part_settings.partition_count = partition_count;
+					part_settings.adbbackup = true;
+					part_settings.adb_compression = twimghdr.compressed;
+					part_settings.Backup_FileName = Backup_FileName;
+					part_settings.total_restore_size += part_settings.Part->Get_Restore_Size(&part_settings);
+					part_settings.PM_Method = PM_RESTORE;
+					ProgressTracking progress(part_settings.total_restore_size);
+					part_settings.progress = &progress;
+					if (!PartitionManager.Restore_Partition(&part_settings)) {
+						LOGERR("ADB Restore failed.\n");
+						return 1;
+					}
+				}
+			}
+		}
+	}
+	gui_msg("restore_complete=Restore Complete");
+
+	if (!twadbbu::Write_TWENDADB())
+		ret = 1;
+	sleep(2); //give time for user to see messages on console
+	return ret;
+}
+
+int OpenRecoveryScript::remountrw(void)
+{
+	bool remount_system = PartitionManager.Is_Mounted_By_Path("/system");
+	int op_status;
+	TWPartition* Part;
+
+	if (!PartitionManager.UnMount_By_Path("/system", true)) {
+		op_status = 1; // fail
+	} else {
+		Part = PartitionManager.Find_Partition_By_Path("/system");
+		if (Part) {
+			DataManager::SetValue("tw_mount_system_ro", 0);
+			Part->Change_Mount_Read_Only(false);
+		}
+		if (remount_system) {
+			Part->Mount(true);
+		}
+		op_status = 0; // success
+	}
+
+	return op_status;
+}