/* Partition class for TWRP * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * The code was written from scratch by Dees_Troy dees_troy at * yahoo * * Copyright (c) 2012 */ #include #include #include #include #include #include #include #include #ifdef TW_INCLUDE_CRYPTO #include "cutils/properties.h" #endif #include "variables.h" #include "common.h" #include "partitions.hpp" #include "data.hpp" extern "C" { #include "extra-functions.h" int __system(const char *command); } TWPartition::TWPartition(void) { Can_Be_Mounted = false; Can_Be_Wiped = false; Wipe_During_Factory_Reset = false; Wipe_Available_in_GUI = false; Is_SubPartition = false; SubPartition_Of = ""; Symlink_Path = ""; Symlink_Mount_Point = ""; Mount_Point = ""; Block_Device = ""; Alternate_Block_Device = ""; Removable = false; Is_Present = false; Length = 0; Size = 0; Used = 0; Free = 0; Backup_Size = 0; Can_Be_Encrypted = false; Is_Encrypted = false; Is_Decrypted = false; Decrypted_Block_Device = ""; Display_Name = ""; Backup_Name = ""; Backup_FileName = ""; Backup_Method = NONE; Has_Data_Media = false; Is_Storage = false; Storage_Path = ""; Current_File_System = ""; Fstab_File_System = ""; Format_Block_Size = 0; } TWPartition::~TWPartition(void) { // Do nothing } bool TWPartition::Process_Fstab_Line(string Line, bool Display_Error) { char full_line[MAX_FSTAB_LINE_LENGTH], item[MAX_FSTAB_LINE_LENGTH]; int line_len = Line.size(), index = 0, item_index = 0; char* ptr; string Flags; strncpy(full_line, Line.c_str(), line_len); for (index = 0; index < line_len; index++) { if (full_line[index] <= 32) full_line[index] = '\0'; } string mount_pt(full_line); Mount_Point = mount_pt; index = Mount_Point.size(); while (index < line_len) { while (index < line_len && full_line[index] == '\0') index++; if (index >= line_len) continue; ptr = full_line + index; if (item_index == 0) { // File System Fstab_File_System = ptr; Current_File_System = ptr; item_index++; } else if (item_index == 1) { // Primary Block Device if (*ptr != '/') { if (Display_Error) LOGE("Invalid block device on '%s', '%s', %i\n", Line.c_str(), ptr, index); else LOGI("Invalid block device on '%s', '%s', %i\n", Line.c_str(), ptr, index); return 0; } Block_Device = ptr; Find_Real_Block_Device(Block_Device, Display_Error); item_index++; } else if (item_index > 1) { if (*ptr == '/') { // Alternate Block Device Alternate_Block_Device = ptr; Find_Real_Block_Device(Alternate_Block_Device, Display_Error); } else if (strlen(ptr) > 7 && strncmp(ptr, "length=", 7) == 0) { // Partition length ptr += 7; Length = atoi(ptr); } else if (strlen(ptr) > 6 && strncmp(ptr, "flags=", 6) == 0) { // Custom flags, save for later so that new values aren't overwritten by defaults ptr += 6; Flags = ptr; } else { // Unhandled data LOGI("Unhandled fstab information: '%s', %i\n", ptr, index); } } while (index < line_len && full_line[index] != '\0') index++; } if (!Is_File_System(Fstab_File_System) && !Is_Image(Fstab_File_System)) { if (Display_Error) LOGE("Unknown File System: '%s'\n", Fstab_File_System.c_str()); else LOGI("Unknown File System: '%s'\n", Fstab_File_System.c_str()); return 0; } else if (Is_File_System(Fstab_File_System)) { Setup_File_System(Display_Error); if (Mount_Point == "/system") { Display_Name = "System"; Wipe_Available_in_GUI = true; } else if (Mount_Point == "/data") { Display_Name = "Data"; Wipe_Available_in_GUI = true; Wipe_During_Factory_Reset = true; #ifdef RECOVERY_SDCARD_ON_DATA Has_Data_Media = true; Is_Storage = true; Storage_Path = "/data/media"; if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) { Make_Dir("/emmc", Display_Error); Symlink_Path = "/data/media"; Symlink_Mount_Point = "/emmc"; } else { Make_Dir("/sdcard", Display_Error); Symlink_Path = "/data/media"; Symlink_Mount_Point = "/sdcard"; } #endif #ifdef TW_INCLUDE_CRYPTO Can_Be_Encrypted = true; char crypto_blkdev[255]; property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error"); if (strcmp(crypto_blkdev, "error") != 0) { DataManager::SetValue(TW_DATA_BLK_DEVICE, Block_Device); DataManager::SetValue(TW_IS_DECRYPTED, 1); Is_Encrypted = true; Is_Decrypted = true; Decrypted_Block_Device = crypto_blkdev; LOGI("Data already decrypted, new block device: '%s'\n", crypto_blkdev); } else if (!Mount(false)) { Is_Encrypted = true; Is_Decrypted = false; DataManager::SetValue(TW_IS_ENCRYPTED, 1); DataManager::SetValue(TW_CRYPTO_PASSWORD, ""); DataManager::SetValue("tw_crypto_display", ""); } #endif } else if (Mount_Point == "/cache") { Display_Name = "Cache"; Wipe_Available_in_GUI = true; Wipe_During_Factory_Reset = true; Update_Size(Display_Error); } else if (Mount_Point == "/datadata") { Wipe_During_Factory_Reset = true; Display_Name = "DataData"; Is_SubPartition = true; SubPartition_Of = "/data"; DataManager::SetValue(TW_HAS_DATADATA, 1); } else if (Mount_Point == "/sd-ext") { Wipe_During_Factory_Reset = true; Display_Name = "SD-Ext"; Wipe_Available_in_GUI = true; } else Update_Size(Display_Error); } else if (Is_Image(Fstab_File_System)) { Setup_Image(Display_Error); if (Mount_Point == "/boot") { int backup_display_size = (int)(Backup_Size / 1048576LLU); DataManager::SetValue(TW_BACKUP_BOOT_SIZE, backup_display_size); if (Backup_Size == 0) { DataManager::SetValue(TW_HAS_BOOT_PARTITION, 0); DataManager::SetValue(TW_BACKUP_BOOT_VAR, 0); } else DataManager::SetValue(TW_HAS_BOOT_PARTITION, 1); } else if (Mount_Point == "/recovery") { int backup_display_size = (int)(Backup_Size / 1048576LLU); DataManager::SetValue(TW_BACKUP_RECOVERY_SIZE, backup_display_size); if (Backup_Size == 0) { DataManager::SetValue(TW_HAS_RECOVERY_PARTITION, 0); DataManager::SetValue(TW_BACKUP_RECOVERY_VAR, 0); } else DataManager::SetValue(TW_HAS_RECOVERY_PARTITION, 1); } } // Process any custom flags if (Flags.size() > 0) Process_Flags(Flags, Display_Error); return true; } bool TWPartition::Process_Flags(string Flags, bool Display_Error) { char flags[MAX_FSTAB_LINE_LENGTH]; int flags_len, index = 0; char* ptr; strcpy(flags, Flags.c_str()); flags_len = Flags.size(); for (index = 0; index < flags_len; index++) { if (flags[index] == ';') flags[index] = '\0'; } index = 0; while (index < flags_len) { while (index < flags_len && flags[index] == '\0') index++; if (index >= flags_len) continue; ptr = flags + index; if (strcmp(ptr, "removable") == 0) { Removable = true; } else if (strcmp(ptr, "storage") == 0) { Is_Storage = true; } else if (strcmp(ptr, "canbewiped") == 0) { Can_Be_Wiped = true; } else if (strcmp(ptr, "wipeingui") == 0) { Can_Be_Wiped = true; Wipe_Available_in_GUI = true; } else if (strcmp(ptr, "wipeduringfactoryreset") == 0) { Can_Be_Wiped = true; Wipe_Available_in_GUI = true; Wipe_During_Factory_Reset = true; } else if (strlen(ptr) > 15 && strncmp(ptr, "subpartitionof=", 15) == 0) { ptr += 13; Is_SubPartition = true; SubPartition_Of = ptr; } else if (strlen(ptr) > 8 && strncmp(ptr, "symlink=", 8) == 0) { ptr += 8; Symlink_Path = ptr; } else if (strlen(ptr) > 8 && strncmp(ptr, "display=", 8) == 0) { ptr += 8; Display_Name = ptr; } else if (strlen(ptr) > 10 && strncmp(ptr, "blocksize=", 10) == 0) { ptr += 10; Format_Block_Size = atoi(ptr); } else if (strlen(ptr) > 7 && strncmp(ptr, "length=", 7) == 0) { ptr += 7; Length = atoi(ptr); } else { if (Display_Error) LOGE("Unhandled flag: '%s'\n", ptr); else LOGI("Unhandled flag: '%s'\n", ptr); } while (index < flags_len && flags[index] != '\0') index++; } return true; } bool TWPartition::Is_File_System(string File_System) { if (File_System == "ext2" || File_System == "ext3" || File_System == "ext4" || File_System == "vfat" || File_System == "ntfs" || File_System == "yaffs2" || File_System == "auto") return true; else return false; } bool TWPartition::Is_Image(string File_System) { if (File_System == "emmc" || File_System == "mtd") return true; else return false; } bool TWPartition::Make_Dir(string Path, bool Display_Error) { if (!Path_Exists(Path)) { if (mkdir(Path.c_str(), 0777) == -1) { if (Display_Error) LOGE("Can not create '%s' folder.\n", Path.c_str()); else LOGI("Can not create '%s' folder.\n", Path.c_str()); return false; } else { LOGI("Created '%s' folder.\n", Path.c_str()); return true; } } return true; } void TWPartition::Setup_File_System(bool Display_Error) { struct statfs st; Can_Be_Mounted = true; Can_Be_Wiped = true; // Check to see if the block device exists if (Path_Exists(Block_Device)) { Is_Present = true; } else if (!Alternate_Block_Device.empty() && Path_Exists(Alternate_Block_Device)) { Flip_Block_Device(); Is_Present = true; } // Make the mount point folder if it doesn't exist Make_Dir(Mount_Point, Display_Error); Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1); Backup_Name = Display_Name; Backup_Method = FILES; } void TWPartition::Setup_Image(bool Display_Error) { if (Path_Exists(Block_Device)) { Is_Present = true; } else if (!Alternate_Block_Device.empty() && Path_Exists(Alternate_Block_Device)) { Flip_Block_Device(); Is_Present = true; } Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1); Backup_Name = Display_Name; if (Fstab_File_System == "emmc") Backup_Method = DD; else if (Fstab_File_System == "mtd") Backup_Method = FLASH_UTILS; else LOGI("Unhandled file system '%s' on image '%s'\n", Fstab_File_System.c_str(), Display_Name.c_str()); if (Find_Partition_Size()) { Used = Size; Backup_Size = Size; } else { if (Display_Error) LOGE("Unable to find parition size for '%s'\n", Block_Device.c_str()); else LOGI("Unable to find parition size for '%s'\n", Block_Device.c_str()); } } void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) { char device[512], realDevice[512]; strcpy(device, Block.c_str()); memset(realDevice, 0, sizeof(realDevice)); while (readlink(device, realDevice, sizeof(realDevice)) > 0) { strcpy(device, realDevice); memset(realDevice, 0, sizeof(realDevice)); } if (device[0] != '/') { if (Display_Error) LOGE("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str()); else LOGI("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str()); return; } else { Block = device; return; } } bool TWPartition::Get_Size_Via_statfs(bool Display_Error) { struct statfs st; string Local_Path = Mount_Point + "/."; if (!Mount(Display_Error)) return false; if (statfs(Local_Path.c_str(), &st) != 0) { if (!Removable) { if (Display_Error) LOGE("Unable to statfs '%s'\n", Local_Path.c_str()); else LOGI("Unable to statfs '%s'\n", Local_Path.c_str()); } return false; } Size = (st.f_blocks * st.f_bsize); Used = ((st.f_blocks - st.f_bfree) * st.f_bsize); Free = (st.f_bfree * st.f_bsize); Backup_Size = Used; return true; } bool TWPartition::Get_Size_Via_df(bool Display_Error) { FILE* fp; char command[255], line[512]; int include_block = 1; unsigned int min_len; if (!Mount(Display_Error)) return false; min_len = Block_Device.size() + 2; sprintf(command, "df %s > /tmp/dfoutput.txt", Mount_Point.c_str()); __system(command); fp = fopen("/tmp/dfoutput.txt", "rt"); if (fp == NULL) { LOGI("Unable to open /tmp/dfoutput.txt.\n"); return false; } while (fgets(line, sizeof(line), fp) != NULL) { unsigned long blocks, used, available; char device[64]; char tmpString[64]; if (strncmp(line, "Filesystem", 10) == 0) continue; if (strlen(line) < min_len) { include_block = 0; continue; } if (include_block) { sscanf(line, "%s %lu %lu %lu", device, &blocks, &used, &available); } else { // The device block string is so long that the df information is on the next line int space_count = 0; while (tmpString[space_count] == 32) space_count++; sscanf(line + space_count, "%lu %lu %lu", &blocks, &used, &available); } // Adjust block size to byte size Size = blocks * 1024ULL; Used = used * 1024ULL; Free = available * 1024ULL; Backup_Size = Used; } fclose(fp); return true; } unsigned long long TWPartition::Get_Folder_Size(string Path, bool Display_Error) { DIR* d; struct dirent* de; struct stat st; char path2[1024], filename[1024]; unsigned long long dusize = 0; // Make a copy of path in case the data in the pointer gets overwritten later strcpy(path2, Path.c_str()); d = opendir(path2); if (d == NULL) { LOGE("error opening '%s'\n", path2); return 0; } while ((de = readdir(d)) != NULL) { if (de->d_type == DT_DIR && strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { strcpy(filename, path2); strcat(filename, "/"); strcat(filename, de->d_name); dusize += Get_Folder_Size(filename, Display_Error); } else if (de->d_type == DT_REG) { strcpy(filename, path2); strcat(filename, "/"); strcat(filename, de->d_name); stat(filename, &st); dusize += (unsigned long long)(st.st_size); } } closedir(d); return dusize; } bool TWPartition::Find_Partition_Size(void) { FILE* fp; char line[512]; string tmpdevice; // In this case, we'll first get the partitions we care about (with labels) fp = fopen("/proc/partitions", "rt"); if (fp == NULL) return false; while (fgets(line, sizeof(line), fp) != NULL) { unsigned long major, minor, blocks; char device[512]; char tmpString[64]; if (strlen(line) < 7 || line[0] == 'm') continue; sscanf(line + 1, "%lu %lu %lu %s", &major, &minor, &blocks, device); tmpdevice = "/dev/block/"; tmpdevice += device; if (tmpdevice == Block_Device || tmpdevice == Alternate_Block_Device) { // Adjust block size to byte size Size = blocks * 1024ULL; fclose(fp); return true; } } fclose(fp); return false; } bool TWPartition::Path_Exists(string Path) { // Check to see if the Path exists struct statfs st; if (statfs(Path.c_str(), &st) != 0) return false; else return true; } void TWPartition::Flip_Block_Device(void) { string temp; temp = Alternate_Block_Device; Block_Device = Alternate_Block_Device; Alternate_Block_Device = temp; } bool TWPartition::Is_Mounted(void) { if (!Can_Be_Mounted) return false; struct stat st1, st2; string test_path; // Check to see if the mount point directory exists test_path = Mount_Point + "/."; if (stat(test_path.c_str(), &st1) != 0) return false; // Check to see if the directory above the mount point exists test_path = Mount_Point + "/../."; if (stat(test_path.c_str(), &st2) != 0) return false; // Compare the device IDs -- if they match then we're (probably) using tmpfs instead of an actual device int ret = (st1.st_dev != st2.st_dev) ? true : false; return ret; } bool TWPartition::Mount(bool Display_Error) { if (Is_Mounted()) { return true; } else if (!Can_Be_Mounted) { return false; } if (Removable) Check_FS_Type(); if (Is_Decrypted) { if (mount(Decrypted_Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) { Check_FS_Type(); if (mount(Decrypted_Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) { if (Display_Error) LOGE("Unable to mount decrypted block device '%s' to '%s'\n", Decrypted_Block_Device.c_str(), Mount_Point.c_str()); else LOGI("Unable to mount decrypted block device '%s' to '%s'\n", Decrypted_Block_Device.c_str(), Mount_Point.c_str()); return false; } else { if (Removable) Update_Size(Display_Error); return true; } } else { if (Removable) Update_Size(Display_Error); return true; } } if (mount(Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) { Check_FS_Type(); if (mount(Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) { if (!Alternate_Block_Device.empty() && Path_Exists(Alternate_Block_Device)) { Flip_Block_Device(); Check_FS_Type(); if (mount(Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) { if (Display_Error) LOGE("Unable to mount '%s'\n", Mount_Point.c_str()); else LOGI("Unable to mount '%s'\n", Mount_Point.c_str()); return false; } else { if (Removable) Update_Size(Display_Error); return true; } } else return false; } else { if (Removable) Update_Size(Display_Error); return true; } } if (Removable) Update_Size(Display_Error); return true; } bool TWPartition::UnMount(bool Display_Error) { if (Is_Mounted()) { int never_unmount_system; DataManager::GetValue(TW_DONT_UNMOUNT_SYSTEM, never_unmount_system); if (never_unmount_system == 1 && Mount_Point == "/system") return true; // Never unmount system if you're not supposed to unmount it if (umount(Mount_Point.c_str()) != 0) { if (Display_Error) LOGE("Unable to unmount '%s'\n", Mount_Point.c_str()); else LOGI("Unable to unmount '%s'\n", Mount_Point.c_str()); return false; } else return true; } else { return true; } } bool TWPartition::Wipe() { LOGI("STUB TWPartition::Wipe\n"); return 1; } bool TWPartition::Backup(string backup_folder) { LOGI("STUB TWPartition::Backup, backup_folder: '%s'\n", backup_folder.c_str()); return 1; } bool TWPartition::Restore(string restore_folder) { LOGI("STUB TWPartition::Restore, restore_folder: '%s'\n", restore_folder.c_str()); return 1; } string TWPartition::Backup_Method_By_Name() { LOGI("STUB TWPartition::Backup_Method_By_Name\n"); return "STUB"; } bool TWPartition::Decrypt(string Password) { LOGI("STUB TWPartition::Decrypt, password: '%s'\n", Password.c_str()); return 1; } bool TWPartition::Wipe_Encryption() { LOGI("STUB TWPartition::Wipe_Encryption\n"); return 1; } void TWPartition::Check_FS_Type() { FILE *fp; string blkCommand; char blkOutput[255]; char* blk; char* arg; char* ptr; if (Fstab_File_System == "yaffs2" || Fstab_File_System == "mtd") return; // Running blkid on some mtd devices causes a massive crash if (Is_Decrypted) blkCommand = "blkid " + Decrypted_Block_Device + " > /tmp/blkidoutput.txt"; else blkCommand = "blkid " + Block_Device + " > /tmp/blkidoutput.txt"; __system(blkCommand.c_str()); fp = fopen("/tmp/blkidoutput.txt", "rt"); while (fgets(blkOutput, sizeof(blkOutput), fp) != NULL) { blk = blkOutput; ptr = blkOutput; while (*ptr > 32 && *ptr != ':') ptr++; if (*ptr == 0) continue; *ptr = 0; // Increment by two, but verify that we don't hit a NULL ptr++; if (*ptr != 0) ptr++; // Now, find the TYPE field while (1) { arg = ptr; while (*ptr > 32) ptr++; if (*ptr != 0) { *ptr = 0; ptr++; } if (strlen(arg) > 6) { if (memcmp(arg, "TYPE=\"", 6) == 0) break; } if (*ptr == 0) { arg = NULL; break; } } if (arg && strlen(arg) > 7) { arg += 6; // Skip the TYPE=" portion arg[strlen(arg)-1] = '\0'; // Drop the tail quote } else continue; if (strcmp(Current_File_System.c_str(), arg) != 0) { LOGI("'%s' was '%s' now set to '%s'\n", Mount_Point.c_str(), Current_File_System.c_str(), arg); Current_File_System = arg; } } __pclose(fp); return; } bool TWPartition::Wipe_EXT23() { LOGI("STUB TWPartition::Wipe_EXT23\n"); return 1; } bool TWPartition::Wipe_EXT4() { LOGI("STUB TWPartition::Wipe_EXT4\n"); return 1; } bool TWPartition::Wipe_FAT() { LOGI("STUB TWPartition::Wipe_FAT\n"); return 1; } bool TWPartition::Wipe_YAFFS2() { LOGI("STUB TWPartition::Wipe_YAFFS2\n"); return 1; } bool TWPartition::Wipe_RMRF() { LOGI("STUB TWPartition::Wipe_RMRF\n"); return 1; } bool TWPartition::Wipe_Data_Without_Wiping_Media() { LOGI("STUB TWPartition::Wipe_Data_Without_Wiping_Media\n"); return 1; } bool TWPartition::Backup_Tar(string backup_folder) { LOGI("STUB TWPartition::Backup_Tar, backup_folder: '%s'\n", backup_folder.c_str()); return 1; } bool TWPartition::Backup_DD(string backup_folder) { LOGI("STUB TWPartition::Backup_DD, backup_folder: '%s'\n", backup_folder.c_str()); return 1; } bool TWPartition::Backup_Dump_Image(string backup_folder) { LOGI("STUB TWPartition::Backup_Dump_Image, backup_folder: '%s'\n", backup_folder.c_str()); return 1; } bool TWPartition::Restore_Tar(string restore_folder) { LOGI("STUB TWPartition::Restore_Tar, backup_folder: '%s'\n", restore_folder.c_str()); return 1; } bool TWPartition::Restore_DD(string restore_folder) { LOGI("STUB TWPartition::Restore_DD, backup_folder: '%s'\n", restore_folder.c_str()); return 1; } bool TWPartition::Restore_Flash_Image(string restore_folder) { LOGI("STUB TWPartition::Restore_Flash_Image, backup_folder: '%s'\n", restore_folder.c_str()); return 1; } bool TWPartition::Update_Size(bool Display_Error) { bool ret = false; if (!Can_Be_Mounted) return false; if (!Mount(Display_Error)) return false; ret = Get_Size_Via_statfs(Display_Error); if (!ret || Size == 0) if (!Get_Size_Via_df(Display_Error)) return false; if (Has_Data_Media) { if (Mount(Display_Error)) { unsigned long long data_media_used, actual_data; data_media_used = Get_Folder_Size("/data/media", Display_Error); actual_data = Used - data_media_used; Backup_Size = actual_data; int bak = (int)(Backup_Size / 1048576LLU); int total = (int)(Size / 1048576LLU); int us = (int)(Used / 1048576LLU); int fre = (int)(Free / 1048576LLU); int datmed = (int)(data_media_used / 1048576LLU); LOGI("Data backup size is %iMB, size: %iMB, used: %iMB, free: %iMB, in data/media: %iMB.\n", bak, total, us, fre, datmed); } else return false; } return true; }