| /* |
| 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 |
| 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 <string> |
| |
| #include "data.hpp" |
| #include "partitions.hpp" |
| #include "twrp-functions.hpp" |
| #include "twrpRepacker.hpp" |
| #include "twcommon.h" |
| #include "variables.h" |
| #include "gui/gui.hpp" |
| |
| bool twrpRepacker::Prepare_Empty_Folder(const std::string& Folder) { |
| if (TWFunc::Path_Exists(Folder)) |
| TWFunc::removeDir(Folder, false); |
| return TWFunc::Recursive_Mkdir(Folder); |
| } |
| |
| bool twrpRepacker::Backup_Image_For_Repack(TWPartition* Part, const std::string& Temp_Folder_Destination, |
| const bool Create_Backup, const std::string& Backup_Name) { |
| if (!Part) { |
| LOGERR("Partition was null!\n"); |
| return false; |
| } |
| if (!Prepare_Empty_Folder(Temp_Folder_Destination)) |
| return false; |
| std::string target_image = Temp_Folder_Destination + "boot.img"; |
| PartitionSettings part_settings; |
| part_settings.Part = Part; |
| if (Create_Backup) { |
| if (PartitionManager.Check_Backup_Name(Backup_Name, true, false) != 0) |
| return false; |
| DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, part_settings.Backup_Folder); |
| part_settings.Backup_Folder = part_settings.Backup_Folder + "/" + TWFunc::Get_Current_Date() + " " + Backup_Name + "/"; |
| if (!TWFunc::Recursive_Mkdir(part_settings.Backup_Folder)) |
| return false; |
| } else |
| part_settings.Backup_Folder = Temp_Folder_Destination; |
| part_settings.adbbackup = false; |
| part_settings.generate_digest = false; |
| part_settings.generate_md5 = false; |
| part_settings.PM_Method = PM_BACKUP; |
| part_settings.progress = NULL; |
| pid_t not_a_pid = 0; |
| if (!Part->Backup(&part_settings, ¬_a_pid)) |
| return false; |
| std::string backed_up_image = part_settings.Backup_Folder; |
| backed_up_image += Part->Get_Backup_FileName(); |
| target_image = Temp_Folder_Destination + "boot.img"; |
| if (Create_Backup) { |
| std::string source = part_settings.Backup_Folder + Part->Get_Backup_FileName(); |
| if (TWFunc::copy_file(source, target_image, 0644) != 0) { |
| LOGERR("Failed to copy backup file '%s' to temp folder target '%s'\n", source.c_str(), target_image.c_str()); |
| return false; |
| } |
| } else { |
| if (rename(backed_up_image.c_str(), target_image.c_str()) != 0) { |
| LOGERR("Failed to rename '%s' to '%s'\n", backed_up_image.c_str(), target_image.c_str()); |
| return false; |
| } |
| } |
| original_ramdisk_format = Unpack_Image(target_image, Temp_Folder_Destination, false, false); |
| return !original_ramdisk_format.empty(); |
| } |
| |
| std::string twrpRepacker::Unpack_Image(const std::string& Source_Path, const std::string& Temp_Folder_Destination, |
| const bool Copy_Source, const bool Create_Destination) { |
| std::string txt_to_find = "RAMDISK_FMT"; |
| if (Create_Destination) { |
| if (!Prepare_Empty_Folder(Temp_Folder_Destination)) |
| return std::string(); |
| } |
| if (Copy_Source) { |
| std::string destination = Temp_Folder_Destination + "/boot.img"; |
| if (TWFunc::copy_file(Source_Path, destination, 0644)) |
| return std::string(); |
| } |
| std::string command = "cd " + Temp_Folder_Destination + " && /system/bin/magiskboot unpack -h -n "; |
| command = command + "'" + Source_Path +"'"; |
| |
| std::string magisk_unpack_output; |
| int ret; |
| if ((ret = TWFunc::Exec_Cmd(command, magisk_unpack_output, true)) != 0) { |
| LOGINFO("Error unpacking %s, ret: %d!\n", Source_Path.c_str(), ret); |
| gui_msg(Msg(msg::kError, "unpack_error=Error unpacking image.")); |
| return std::string(); |
| } |
| size_t pos = magisk_unpack_output.find(txt_to_find) + txt_to_find.size(); |
| std::string ramdisk_format = magisk_unpack_output.substr(pos, magisk_unpack_output.size() - 1); |
| ramdisk_format.erase(std::remove(ramdisk_format.begin(), ramdisk_format.end(), '['), ramdisk_format.end()); |
| ramdisk_format.erase(std::remove(ramdisk_format.begin(), ramdisk_format.end(), ']'), ramdisk_format.end()); |
| ramdisk_format.erase(std::remove(ramdisk_format.begin(), ramdisk_format.end(), ' '), ramdisk_format.end()); |
| ramdisk_format.erase(std::remove(ramdisk_format.begin(), ramdisk_format.end(), '\n'), ramdisk_format.end()); |
| return ramdisk_format; |
| } |
| |
| static bool is_AB_for_repacker() { |
| std::string slot = android::base::GetProperty("ro.boot.slot_suffix", ""); |
| if (slot.empty()) |
| slot = android::base::GetProperty("ro.boot.slot", ""); |
| return !slot.empty(); |
| } |
| |
| bool twrpRepacker::Repack_Image_And_Flash(const std::string& Target_Image, const struct Repack_Options_struct& Repack_Options) { |
| if (!TWFunc::Path_Exists("/system/bin/magiskboot")) { |
| LOGERR("Image repacking tool not present in this TWRP build!"); |
| return false; |
| } |
| |
| bool recompress = false; |
| bool is_vendor_boot = false; |
| bool is_vendor_boot_v4 = false; |
| std::string dest_partition = "/boot"; |
| std::string ramdisk_cpio = "ramdisk.cpio"; |
| |
| #ifdef BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT |
| dest_partition = "/vendor_boot"; |
| is_vendor_boot = true; |
| if (DataManager::GetIntValue("tw_boot_header_version") == 4) { |
| is_vendor_boot_v4 = true; |
| ramdisk_cpio = "vendor_ramdisk_recovery.cpio"; |
| LOGINFO("Vendor_boot with v4 header\n"); |
| } else { |
| LOGINFO("Vendor_boot with v3 header\n"); |
| } |
| #else |
| // we shouldn't reach here, because of the code in twrpRepacker::Flash_Current_Twrp(); but if we do, then handle it |
| if (PartitionManager.Find_Partition_By_Path("/recovery") && is_AB_for_repacker()) { |
| dest_partition = "/recovery"; |
| } |
| #endif |
| |
| if (is_vendor_boot || is_vendor_boot_v4) { |
| // placeholder for any specific vendor_boot stuff; |
| // in the meantime, stop the compiler's complaints about unused variables |
| } |
| |
| DataManager::SetProgress(0); |
| PartitionManager.Update_System_Details(); |
| TWPartition* part = PartitionManager.Find_Partition_By_Path(dest_partition); |
| if (part) |
| gui_msg(Msg("unpacking_image=Unpacking {1}...")(part->Get_Display_Name())); |
| else { |
| gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")(dest_partition.c_str())); |
| return false; |
| } |
| if (!Backup_Image_For_Repack(part, REPACK_ORIG_DIR, Repack_Options.Backup_First, gui_lookup("repack", "Repack"))) |
| return false; |
| DataManager::SetProgress(.25); |
| if (Repack_Options.Type == REPLACE_RAMDISK_UNPACKED) { |
| if (!Prepare_Empty_Folder(REPACK_NEW_DIR)) |
| return false; |
| image_ramdisk_format = "gzip"; |
| } else { |
| gui_msg(Msg("unpacking_image=Unpacking {1}...")(Target_Image)); |
| image_ramdisk_format = Unpack_Image(Target_Image, REPACK_NEW_DIR, true); |
| } |
| if (image_ramdisk_format.empty()) |
| return false; |
| DataManager::SetProgress(.5); |
| gui_msg(Msg("repacking_image=Repacking {1}...")(part->Get_Display_Name())); |
| std::string path = REPACK_NEW_DIR; |
| if (Repack_Options.Type == REPLACE_KERNEL) { |
| // When we replace the kernel, what we really do is copy the boot partition ramdisk into the new image's folder |
| if (TWFunc::copy_file(REPACK_ORIG_DIR + ramdisk_cpio, REPACK_NEW_DIR + ramdisk_cpio, 0644)) { |
| LOGERR("Failed to copy ramdisk\n"); |
| return false; |
| } |
| } else if (Repack_Options.Type == REPLACE_RAMDISK_UNPACKED) { |
| if (TWFunc::copy_file(Target_Image, REPACK_ORIG_DIR + ramdisk_cpio, 0644)) { |
| LOGERR("Failed to copy ramdisk\n"); |
| return false; |
| } |
| if (TWFunc::copy_file(Target_Image, REPACK_NEW_DIR + ramdisk_cpio, 0644)) { |
| LOGERR("Failed to copy ramdisk\n"); |
| return false; |
| } |
| path = REPACK_ORIG_DIR; |
| } else if (Repack_Options.Type == REPLACE_RAMDISK) { |
| // Repack the ramdisk |
| if (TWFunc::copy_file(REPACK_NEW_DIR + ramdisk_cpio, REPACK_ORIG_DIR + ramdisk_cpio, 0644)) { |
| LOGERR("Failed to copy ramdisk\n"); |
| return false; |
| } |
| path = REPACK_ORIG_DIR; |
| } else { |
| LOGERR("Invalid repacking options specified\n"); |
| return false; |
| } |
| if (Repack_Options.Disable_Verity) |
| LOGERR("Disabling verity is not implemented yet\n"); |
| if (Repack_Options.Disable_Force_Encrypt) |
| LOGERR("Disabling force encrypt is not implemented yet\n"); |
| std::string command = "cd " + path + " && /system/bin/magiskboot repack "; |
| if (original_ramdisk_format != image_ramdisk_format) { |
| recompress = true; |
| } |
| |
| command += path + "boot.img"; |
| |
| std::string orig_compressed_image(REPACK_ORIG_DIR); |
| orig_compressed_image += ramdisk_cpio; |
| std::string copy_compressed_image(REPACK_ORIG_DIR); |
| copy_compressed_image += "ramdisk-1.cpio"; |
| |
| if (recompress) { |
| std::string decompress_cmd = "/system/bin/magiskboot decompress " + orig_compressed_image + " " + copy_compressed_image; |
| if (TWFunc::Exec_Cmd(decompress_cmd) != 0) { |
| gui_msg(Msg(msg::kError, "repack_error=Error repacking image.")); |
| return false; |
| } |
| std::rename(copy_compressed_image.c_str(), orig_compressed_image.c_str()); |
| } |
| |
| if (TWFunc::Exec_Cmd(command) != 0) { |
| gui_msg(Msg(msg::kError, "repack_error=Error repacking image.")); |
| return false; |
| } |
| |
| DataManager::SetProgress(.75); |
| std::string file = "new-boot.img"; |
| DataManager::SetValue("tw_flash_partition", dest_partition + ";"); |
| if (!PartitionManager.Flash_Image(path, file)) { |
| LOGINFO("Error flashing new image\n"); |
| return false; |
| } |
| DataManager::SetProgress(1); |
| TWFunc::removeDir(REPACK_ORIG_DIR, false); |
| if (part->Is_SlotSelect()) { |
| if (Repack_Options.Type == REPLACE_RAMDISK || Repack_Options.Type == REPLACE_RAMDISK_UNPACKED) { |
| LOGINFO("Switching slots to flash ramdisk to both partitions\n"); |
| string Current_Slot = PartitionManager.Get_Active_Slot_Display(); |
| if (Current_Slot == "A") |
| PartitionManager.Override_Active_Slot("B"); |
| else |
| PartitionManager.Override_Active_Slot("A"); |
| DataManager::SetProgress(.25); |
| if (!Backup_Image_For_Repack(part, REPACK_ORIG_DIR, Repack_Options.Backup_First, gui_lookup("repack", "Repack"))) |
| return false; |
| if (TWFunc::copy_file(REPACK_NEW_DIR + ramdisk_cpio, REPACK_ORIG_DIR + ramdisk_cpio, 0644)) { |
| LOGERR("Failed to copy ramdisk\n"); |
| return false; |
| } |
| path = REPACK_ORIG_DIR; |
| std::string command = "cd " + path + " && /system/bin/magiskboot repack "; |
| |
| if (original_ramdisk_format != image_ramdisk_format) { |
| recompress = true; |
| } |
| command += path + "boot.img"; |
| |
| if (recompress) { |
| std::string decompress_cmd = "/system/bin/magiskboot decompress " + orig_compressed_image + " " + copy_compressed_image; |
| if (TWFunc::Exec_Cmd(decompress_cmd) != 0) { |
| gui_msg(Msg(msg::kError, "repack_error=Error repacking image.")); |
| return false; |
| } |
| std::rename(copy_compressed_image.c_str(), orig_compressed_image.c_str()); |
| } |
| |
| if (TWFunc::Exec_Cmd(command) != 0) { |
| gui_msg(Msg(msg::kError, "repack_error=Error repacking image.")); |
| return false; |
| } |
| DataManager::SetProgress(.75); |
| std::string file = "new-boot.img"; |
| DataManager::SetValue("tw_flash_partition", dest_partition + ";"); |
| if (!PartitionManager.Flash_Image(path, file)) { |
| LOGINFO("Error flashing new image\n"); |
| return false; |
| } |
| DataManager::SetProgress(1); |
| TWFunc::removeDir(REPACK_ORIG_DIR, false); |
| } |
| } |
| TWFunc::removeDir(REPACK_NEW_DIR, false); |
| if (dest_partition == "/boot") |
| gui_msg(Msg(msg::kWarning, "repack_overwrite_warning=If device was previously rooted, then root has been overwritten and will need to be reinstalled.")); |
| string Current_Slot = PartitionManager.Get_Active_Slot_Display(); |
| if (Current_Slot == "A") |
| PartitionManager.Override_Active_Slot("B"); |
| else |
| PartitionManager.Override_Active_Slot("A"); |
| return true; |
| } |
| |
| bool twrpRepacker::Flash_Current_Twrp() { |
| // A/B with dedicated recovery partition |
| std::string slot = android::base::GetProperty("ro.boot.slot_suffix", ""); |
| if (slot.empty()) |
| slot = android::base::GetProperty("ro.boot.slot", ""); |
| if (!slot.empty() && PartitionManager.Find_Partition_By_Path("/recovery")) { |
| std::string root,src, dest; |
| std::string dest_partition = "/recovery"; |
| root = "/dev/block/bootdevice/by-name" + dest_partition; |
| if (slot == "_a" || slot == "a") { |
| src = root + "_a"; |
| dest= root + "_b"; |
| } |
| else { |
| src = root + "_b"; |
| dest= root + "_a"; |
| } |
| PartitionManager.Unlock_Block_Partitions(); |
| |
| // only copy the relevant active slot to the inactive slot, on the basis that the recovery currently running |
| // in the active slot can simply be copied over to the inactive slot, so that both have the same recovery image |
| std::string command = "dd bs=1048576 if=" + src + " of=" + dest; |
| LOGINFO("Command=%s\n", command.c_str()); |
| |
| if (TWFunc::Exec_Cmd(command) != 0) { |
| LOGERR("Failed to flash the %s image\n", dest_partition.c_str()); |
| return false; |
| } |
| else { |
| gui_print("Finished flashing the %s image\n", dest_partition.c_str()); |
| return true; |
| } |
| // if we reach here, something is awry - bale out |
| return false; |
| } |
| |
| if (!TWFunc::Path_Exists("/ramdisk-files.txt")) { |
| LOGERR("can not find ramdisk-files.txt"); |
| return false; |
| } |
| Repack_Options_struct Repack_Options; |
| Repack_Options.Disable_Verity = false; |
| Repack_Options.Disable_Force_Encrypt = false; |
| Repack_Options.Type = REPLACE_RAMDISK_UNPACKED; |
| Repack_Options.Backup_First = DataManager::GetIntValue("tw_repack_backup_first") != 0; |
| std::string verifyfiles = "cd / && sha256sum --status -c ramdisk-files.sha256sum"; |
| if (TWFunc::Exec_Cmd(verifyfiles) != 0) { |
| gui_msg(Msg(msg::kError, "modified_ramdisk_error=ramdisk files have been modified, unable to create ramdisk to flash, fastboot boot twrp and try this option again or use the Install Recovery Ramdisk option.")); |
| return false; |
| } |
| std::string command = "cd / && /system/bin/cpio -H newc -o < ramdisk-files.txt > /tmp/currentramdisk.cpio && /system/bin/gzip -f /tmp/currentramdisk.cpio"; |
| if (TWFunc::Exec_Cmd(command) != 0) { |
| gui_msg(Msg(msg::kError, "create_ramdisk_error=failed to create ramdisk to flash.")); |
| return false; |
| } |
| if (!Repack_Image_And_Flash("/tmp/currentramdisk.cpio.gz", Repack_Options)) |
| return false; |
| else |
| return true; |
| } |