Add partition list GUI element and update backup, restore, mount, storage selection, and wipe sections of GUI and partition manager code to reflect the new GUI element. Update ORS engine to handle new backup and restore setup. Fix a bug with decrypt. Add 1080x1920 layout. Change-Id: Iaa2f44cb707167e66f935452f076ba00e68a2aa4
1667 lines
48 KiB
C++
1667 lines
48 KiB
C++
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/mount.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#ifdef TW_INCLUDE_CRYPTO
|
|
#include "cutils/properties.h"
|
|
#endif
|
|
|
|
#include "libblkid/blkid.h"
|
|
#include "variables.h"
|
|
#include "common.h"
|
|
#include "partitions.hpp"
|
|
#include "data.hpp"
|
|
#include "twrp-functions.hpp"
|
|
#include "twrpDigest.hpp"
|
|
#include "twrpTar.hpp"
|
|
extern "C" {
|
|
#include "mtdutils/mtdutils.h"
|
|
#include "mtdutils/mounts.h"
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
#include "crypto/libcrypt_samsung/include/libcrypt_samsung.h"
|
|
#endif
|
|
}
|
|
|
|
using namespace std;
|
|
|
|
TWPartition::TWPartition(void) {
|
|
Can_Be_Mounted = false;
|
|
Can_Be_Wiped = false;
|
|
Can_Be_Backed_Up = false;
|
|
Wipe_During_Factory_Reset = false;
|
|
Wipe_Available_in_GUI = false;
|
|
Is_SubPartition = false;
|
|
Has_SubPartition = false;
|
|
SubPartition_Of = "";
|
|
Symlink_Path = "";
|
|
Symlink_Mount_Point = "";
|
|
Mount_Point = "";
|
|
Backup_Path = "";
|
|
Actual_Block_Device = "";
|
|
Primary_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_Display_Name = "";
|
|
Storage_Name = "";
|
|
Backup_Name = "";
|
|
Backup_FileName = "";
|
|
MTD_Name = "";
|
|
Backup_Method = NONE;
|
|
Has_Data_Media = false;
|
|
Has_Android_Secure = false;
|
|
Is_Storage = false;
|
|
Is_Settings_Storage = false;
|
|
Storage_Path = "";
|
|
Current_File_System = "";
|
|
Fstab_File_System = "";
|
|
Format_Block_Size = 0;
|
|
Ignore_Blkid = false;
|
|
Retain_Layout_Version = false;
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
EcryptFS_Password = "";
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
bool skip = false;
|
|
|
|
for (index = 0; index < line_len; index++) {
|
|
if (full_line[index] == 34)
|
|
skip = !skip;
|
|
if (!skip && full_line[index] <= 32)
|
|
full_line[index] = '\0';
|
|
}
|
|
Mount_Point = full_line;
|
|
LOGI("Processing '%s'\n", Mount_Point.c_str());
|
|
Backup_Path = Mount_Point;
|
|
Storage_Path = Mount_Point;
|
|
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 (Fstab_File_System == "mtd" || Fstab_File_System == "yaffs2") {
|
|
MTD_Name = ptr;
|
|
Find_MTD_Block_Device(MTD_Name);
|
|
} else if (Fstab_File_System == "bml") {
|
|
if (Mount_Point == "/boot")
|
|
MTD_Name = "boot";
|
|
else if (Mount_Point == "/recovery")
|
|
MTD_Name = "recovery";
|
|
Primary_Block_Device = ptr;
|
|
if (*ptr != '/')
|
|
LOGE("Until we get better BML support, you will have to find and provide the full block device path to the BML devices e.g. /dev/block/bml9 instead of the partition name\n");
|
|
} else 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;
|
|
} else {
|
|
Primary_Block_Device = ptr;
|
|
Find_Real_Block_Device(Primary_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;
|
|
Process_Flags(Flags, Display_Error);
|
|
} else if (strlen(ptr) == 4 && (strncmp(ptr, "NULL", 4) == 0 || strncmp(ptr, "null", 4) == 0 || strncmp(ptr, "null", 4) == 0)) {
|
|
// Do nothing
|
|
} else {
|
|
// Unhandled data
|
|
LOGI("Unhandled fstab information: '%s', %i, line: '%s'\n", ptr, index, Line.c_str());
|
|
}
|
|
}
|
|
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)) {
|
|
Find_Actual_Block_Device();
|
|
Setup_File_System(Display_Error);
|
|
if (Mount_Point == "/system") {
|
|
Display_Name = "System";
|
|
Backup_Display_Name = Display_Name;
|
|
Storage_Name = Display_Name;
|
|
Wipe_Available_in_GUI = true;
|
|
Can_Be_Backed_Up = true;
|
|
} else if (Mount_Point == "/data") {
|
|
Display_Name = "Data";
|
|
Backup_Display_Name = Display_Name;
|
|
Storage_Name = Display_Name;
|
|
Wipe_Available_in_GUI = true;
|
|
Wipe_During_Factory_Reset = true;
|
|
Can_Be_Backed_Up = true;
|
|
#ifdef RECOVERY_SDCARD_ON_DATA
|
|
Storage_Name = "Internal Storage";
|
|
Has_Data_Media = true;
|
|
Is_Storage = true;
|
|
Is_Settings_Storage = true;
|
|
Storage_Path = "/data/media";
|
|
Symlink_Path = Storage_Path;
|
|
if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
|
|
Make_Dir("/emmc", Display_Error);
|
|
Symlink_Mount_Point = "/emmc";
|
|
} else {
|
|
Make_Dir("/sdcard", Display_Error);
|
|
Symlink_Mount_Point = "/sdcard";
|
|
}
|
|
if (Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
|
|
Storage_Path = "/data/media/0";
|
|
Symlink_Path = Storage_Path;
|
|
DataManager::SetValue(TW_INTERNAL_PATH, "/data/media/0");
|
|
UnMount(true);
|
|
}
|
|
#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, Primary_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;
|
|
Can_Be_Mounted = false;
|
|
Current_File_System = "emmc";
|
|
Setup_Image(Display_Error);
|
|
DataManager::SetValue(TW_IS_ENCRYPTED, 1);
|
|
DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
|
|
DataManager::SetValue("tw_crypto_display", "");
|
|
} else {
|
|
// Filesystem is not encrypted and the mount
|
|
// succeeded, so get it back to the original
|
|
// unmounted state
|
|
UnMount(false);
|
|
}
|
|
#ifdef RECOVERY_SDCARD_ON_DATA
|
|
if (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))
|
|
Recreate_Media_Folder();
|
|
#endif
|
|
#else
|
|
#ifdef RECOVERY_SDCARD_ON_DATA
|
|
Recreate_Media_Folder();
|
|
#endif
|
|
#endif
|
|
} else if (Mount_Point == "/cache") {
|
|
Display_Name = "Cache";
|
|
Backup_Display_Name = Display_Name;
|
|
Storage_Name = Display_Name;
|
|
Wipe_Available_in_GUI = true;
|
|
Wipe_During_Factory_Reset = true;
|
|
Can_Be_Backed_Up = true;
|
|
if (Mount(false) && !TWFunc::Path_Exists("/cache/recovery/.")) {
|
|
LOGI("Recreating /cache/recovery folder.\n");
|
|
if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0)
|
|
return -1;
|
|
}
|
|
} else if (Mount_Point == "/datadata") {
|
|
Wipe_During_Factory_Reset = true;
|
|
Display_Name = "DataData";
|
|
Backup_Display_Name = Display_Name;
|
|
Storage_Name = Display_Name;
|
|
Is_SubPartition = true;
|
|
SubPartition_Of = "/data";
|
|
DataManager::SetValue(TW_HAS_DATADATA, 1);
|
|
Can_Be_Backed_Up = true;
|
|
} else if (Mount_Point == "/sd-ext") {
|
|
Wipe_During_Factory_Reset = true;
|
|
Display_Name = "SD-Ext";
|
|
Backup_Display_Name = Display_Name;
|
|
Storage_Name = Display_Name;
|
|
Wipe_Available_in_GUI = true;
|
|
Removable = true;
|
|
Can_Be_Backed_Up = true;
|
|
} else if (Mount_Point == "/boot") {
|
|
Display_Name = "Boot";
|
|
Backup_Display_Name = Display_Name;
|
|
DataManager::SetValue("tw_boot_is_mountable", 1);
|
|
Can_Be_Backed_Up = true;
|
|
}
|
|
#ifdef TW_EXTERNAL_STORAGE_PATH
|
|
if (Mount_Point == EXPAND(TW_EXTERNAL_STORAGE_PATH)) {
|
|
Is_Storage = true;
|
|
Storage_Path = EXPAND(TW_EXTERNAL_STORAGE_PATH);
|
|
Removable = true;
|
|
Wipe_Available_in_GUI = true;
|
|
#else
|
|
if (Mount_Point == "/sdcard") {
|
|
Is_Storage = true;
|
|
Storage_Path = "/sdcard";
|
|
Removable = true;
|
|
Wipe_Available_in_GUI = true;
|
|
#ifndef RECOVERY_SDCARD_ON_DATA
|
|
Setup_AndSec();
|
|
Mount_Storage_Retry();
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef TW_INTERNAL_STORAGE_PATH
|
|
if (Mount_Point == EXPAND(TW_INTERNAL_STORAGE_PATH)) {
|
|
Is_Storage = true;
|
|
Is_Settings_Storage = true;
|
|
Storage_Path = EXPAND(TW_INTERNAL_STORAGE_PATH);
|
|
Wipe_Available_in_GUI = true;
|
|
#ifndef RECOVERY_SDCARD_ON_DATA
|
|
Setup_AndSec();
|
|
Mount_Storage_Retry();
|
|
#endif
|
|
}
|
|
#else
|
|
if (Mount_Point == "/emmc") {
|
|
Is_Storage = true;
|
|
Is_Settings_Storage = true;
|
|
Storage_Path = "/emmc";
|
|
Wipe_Available_in_GUI = true;
|
|
#ifndef RECOVERY_SDCARD_ON_DATA
|
|
Setup_AndSec();
|
|
Mount_Storage_Retry();
|
|
#endif
|
|
}
|
|
#endif
|
|
} else if (Is_Image(Fstab_File_System)) {
|
|
Find_Actual_Block_Device();
|
|
Setup_Image(Display_Error);
|
|
if (Mount_Point == "/boot") {
|
|
Display_Name = "Boot";
|
|
Backup_Display_Name = Display_Name;
|
|
Can_Be_Backed_Up = true;
|
|
} else if (Mount_Point == "/recovery") {
|
|
Display_Name = "Recovery";
|
|
Backup_Display_Name = Display_Name;
|
|
Can_Be_Backed_Up = true;
|
|
}
|
|
}
|
|
|
|
// 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, ptr_len;
|
|
char* ptr;
|
|
bool skip = false, has_display_name = false, has_storage_name = false, has_backup_name = false;
|
|
|
|
strcpy(flags, Flags.c_str());
|
|
flags_len = Flags.size();
|
|
for (index = 0; index < flags_len; index++) {
|
|
if (flags[index] == 34)
|
|
skip = !skip;
|
|
if (!skip && 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;
|
|
ptr_len = strlen(ptr);
|
|
if (strcmp(ptr, "removable") == 0) {
|
|
Removable = true;
|
|
} else if (strcmp(ptr, "storage") == 0) {
|
|
Is_Storage = true;
|
|
} else if (strcmp(ptr, "settingsstorage") == 0) {
|
|
Is_Storage = true;
|
|
} else if (strcmp(ptr, "canbewiped") == 0) {
|
|
Can_Be_Wiped = true;
|
|
} else if (ptr_len > 7 && strncmp(ptr, "backup=", 7) == 0) {
|
|
ptr += 7;
|
|
if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y')
|
|
Can_Be_Backed_Up = true;
|
|
else
|
|
Can_Be_Backed_Up = false;
|
|
} 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 (ptr_len > 15 && strncmp(ptr, "subpartitionof=", 15) == 0) {
|
|
ptr += 15;
|
|
Is_SubPartition = true;
|
|
SubPartition_Of = ptr;
|
|
} else if (strcmp(ptr, "ignoreblkid") == 0) {
|
|
Ignore_Blkid = true;
|
|
} else if (strcmp(ptr, "retainlayoutversion") == 0) {
|
|
Retain_Layout_Version = true;
|
|
} else if (ptr_len > 8 && strncmp(ptr, "symlink=", 8) == 0) {
|
|
ptr += 8;
|
|
Symlink_Path = ptr;
|
|
} else if (ptr_len > 8 && strncmp(ptr, "display=", 8) == 0) {
|
|
has_display_name = true;
|
|
ptr += 8;
|
|
if (*ptr == '\"') ptr++;
|
|
Display_Name = ptr;
|
|
if (Display_Name.substr(Display_Name.size() - 1, 1) == "\"") {
|
|
Display_Name.resize(Display_Name.size() - 1);
|
|
}
|
|
} else if (ptr_len > 11 && strncmp(ptr, "storagename=", 11) == 0) {
|
|
has_storage_name = true;
|
|
ptr += 11;
|
|
if (*ptr == '\"') ptr++;
|
|
Storage_Name = ptr;
|
|
if (Storage_Name.substr(Storage_Name.size() - 1, 1) == "\"") {
|
|
Storage_Name.resize(Storage_Name.size() - 1);
|
|
}
|
|
} else if (ptr_len > 11 && strncmp(ptr, "backupname=", 10) == 0) {
|
|
has_backup_name = true;
|
|
ptr += 10;
|
|
if (*ptr == '\"') ptr++;
|
|
Backup_Display_Name = ptr;
|
|
if (Backup_Display_Name.substr(Backup_Display_Name.size() - 1, 1) == "\"") {
|
|
Backup_Display_Name.resize(Backup_Display_Name.size() - 1);
|
|
}
|
|
} else if (ptr_len > 10 && strncmp(ptr, "blocksize=", 10) == 0) {
|
|
ptr += 10;
|
|
Format_Block_Size = atoi(ptr);
|
|
} else if (ptr_len > 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++;
|
|
}
|
|
if (has_display_name && !has_storage_name)
|
|
Storage_Name = Display_Name;
|
|
if (!has_display_name && has_storage_name)
|
|
Display_Name = Storage_Name;
|
|
if (has_display_name && !has_backup_name)
|
|
Backup_Display_Name = Display_Name;
|
|
if (!has_display_name && has_backup_name)
|
|
Display_Name = Backup_Display_Name;
|
|
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 == "exfat" ||
|
|
File_System == "auto")
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Is_Image(string File_System) {
|
|
if (File_System == "emmc" || File_System == "mtd" || File_System == "bml")
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Make_Dir(string Path, bool Display_Error) {
|
|
if (!TWFunc::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;
|
|
|
|
// 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) {
|
|
Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
|
|
Backup_Name = Display_Name;
|
|
if (Current_File_System == "emmc")
|
|
Backup_Method = DD;
|
|
else if (Current_File_System == "mtd" || Current_File_System == "bml")
|
|
Backup_Method = FLASH_UTILS;
|
|
else
|
|
LOGI("Unhandled file system '%s' on image '%s'\n", Current_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", Mount_Point.c_str());
|
|
else
|
|
LOGI("Unable to find parition size for '%s'\n", Mount_Point.c_str());
|
|
}
|
|
}
|
|
|
|
void TWPartition::Setup_AndSec(void) {
|
|
Backup_Display_Name = "Android Secure";
|
|
Backup_Name = "and-sec";
|
|
Can_Be_Backed_Up = true;
|
|
Has_Android_Secure = true;
|
|
Symlink_Path = Mount_Point + "/.android_secure";
|
|
Symlink_Mount_Point = "/and-sec";
|
|
Backup_Path = Symlink_Mount_Point;
|
|
Make_Dir("/and-sec", true);
|
|
Recreate_AndSec_Folder();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
void TWPartition::Mount_Storage_Retry(void) {
|
|
// On some devices, storage doesn't want to mount right away, retry and sleep
|
|
if (!Mount(true)) {
|
|
int retry_count = 5;
|
|
while (retry_count > 0 && !Mount(false)) {
|
|
usleep(500000);
|
|
retry_count--;
|
|
}
|
|
Mount(true);
|
|
}
|
|
}
|
|
|
|
bool TWPartition::Find_MTD_Block_Device(string MTD_Name) {
|
|
FILE *fp = NULL;
|
|
char line[255];
|
|
|
|
fp = fopen("/proc/mtd", "rt");
|
|
if (fp == NULL) {
|
|
LOGE("Device does not support /proc/mtd\n");
|
|
return false;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL)
|
|
{
|
|
char device[32], label[32];
|
|
unsigned long size = 0;
|
|
char* fstype = NULL;
|
|
int deviceId;
|
|
|
|
sscanf(line, "%s %lx %*s %*c%s", device, &size, label);
|
|
|
|
// Skip header and blank lines
|
|
if ((strcmp(device, "dev:") == 0) || (strlen(line) < 8))
|
|
continue;
|
|
|
|
// Strip off the trailing " from the label
|
|
label[strlen(label)-1] = '\0';
|
|
|
|
if (strcmp(label, MTD_Name.c_str()) == 0) {
|
|
// We found our device
|
|
// Strip off the trailing : from the device
|
|
device[strlen(device)-1] = '\0';
|
|
if (sscanf(device,"mtd%d", &deviceId) == 1) {
|
|
sprintf(device, "/dev/block/mtdblock%d", deviceId);
|
|
Primary_Block_Device = device;
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
string result;
|
|
|
|
if (!Mount(Display_Error))
|
|
return false;
|
|
|
|
min_len = Actual_Block_Device.size() + 2;
|
|
sprintf(command, "df %s > /tmp/dfoutput.txt", Mount_Point.c_str());
|
|
TWFunc::Exec_Cmd(command, result);
|
|
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;
|
|
sprintf(tmpString, "/dev/block/%s", Actual_Block_Device.c_str());
|
|
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;
|
|
}
|
|
|
|
bool TWPartition::Find_Partition_Size(void) {
|
|
FILE* fp;
|
|
char line[512];
|
|
string tmpdevice;
|
|
|
|
fp = fopen("/proc/dumchar_info", "rt");
|
|
if (fp != NULL) {
|
|
while (fgets(line, sizeof(line), fp) != NULL)
|
|
{
|
|
char label[32], device[32];
|
|
unsigned long size = 0;
|
|
|
|
sscanf(line, "%s %lx %*lx %*lu %s", label, &size, device);
|
|
|
|
// Skip header, annotation and blank lines
|
|
if ((strncmp(device, "/dev/", 5) != 0) || (strlen(line) < 8))
|
|
continue;
|
|
|
|
tmpdevice = "/dev/";
|
|
tmpdevice += label;
|
|
if (tmpdevice == Primary_Block_Device || tmpdevice == Alternate_Block_Device) {
|
|
Size = size;
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 == Primary_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::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) {
|
|
int exfat_mounted = 0;
|
|
|
|
if (Is_Mounted()) {
|
|
return true;
|
|
} else if (!Can_Be_Mounted) {
|
|
return false;
|
|
}
|
|
|
|
Find_Actual_Block_Device();
|
|
|
|
// Check the current file system before mounting
|
|
Check_FS_Type();
|
|
if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sbin/exfat-fuse")) {
|
|
string cmd = "/sbin/exfat-fuse -o big_writes,max_read=131072,max_write=131072 " + Actual_Block_Device + " " + Mount_Point;
|
|
LOGI("cmd: %s\n", cmd.c_str());
|
|
string result;
|
|
if (TWFunc::Exec_Cmd(cmd, result) != 0) {
|
|
LOGI("exfat-fuse failed to mount with result '%s', trying vfat\n", result.c_str());
|
|
Current_File_System = "vfat";
|
|
} else {
|
|
#ifdef TW_NO_EXFAT_FUSE
|
|
UnMount(false);
|
|
// We'll let the kernel handle it but using exfat-fuse to detect if the file system is actually exfat
|
|
// Some kernels let us mount vfat as exfat which doesn't work out too well
|
|
#else
|
|
exfat_mounted = 1;
|
|
#endif
|
|
}
|
|
}
|
|
if (Fstab_File_System == "yaffs2") {
|
|
// mount an MTD partition as a YAFFS2 filesystem.
|
|
mtd_scan_partitions();
|
|
const MtdPartition* partition;
|
|
partition = mtd_find_partition_by_name(MTD_Name.c_str());
|
|
if (partition == NULL) {
|
|
LOGE("Failed to find '%s' partition to mount at '%s'\n",
|
|
MTD_Name.c_str(), Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
if (mtd_mount_partition(partition, Mount_Point.c_str(), Fstab_File_System.c_str(), 0)) {
|
|
if (Display_Error)
|
|
LOGE("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
|
|
else
|
|
LOGI("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
|
|
return false;
|
|
} else
|
|
return true;
|
|
} else if (!exfat_mounted && mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), 0, NULL) != 0) {
|
|
#ifdef TW_NO_EXFAT_FUSE
|
|
if (Current_File_System == "exfat") {
|
|
LOGI("Mounting exfat failed, trying vfat...\n");
|
|
if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), "vfat", 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());
|
|
LOGI("Actual block device: '%s', current file system: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str());
|
|
return false;
|
|
}
|
|
} else {
|
|
#endif
|
|
if (Display_Error)
|
|
LOGE("Unable to mount '%s'\n", Mount_Point.c_str());
|
|
else
|
|
LOGI("Unable to mount '%s'\n", Mount_Point.c_str());
|
|
LOGI("Actual block device: '%s', current file system: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str());
|
|
return false;
|
|
#ifdef TW_NO_EXFAT_FUSE
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
string MetaEcfsFile = EXPAND(TW_EXTERNAL_STORAGE_PATH);
|
|
MetaEcfsFile += "/.MetaEcfsFile";
|
|
if (EcryptFS_Password.size() > 0 && PartitionManager.Mount_By_Path("/data", false) && TWFunc::Path_Exists(MetaEcfsFile)) {
|
|
if (mount_ecryptfs_drive(EcryptFS_Password.c_str(), Mount_Point.c_str(), Mount_Point.c_str(), 0) != 0) {
|
|
if (Display_Error)
|
|
LOGE("Unable to mount ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
else
|
|
LOGI("Unable to mount ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
} else {
|
|
LOGI("Successfully mounted ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
Is_Decrypted = true;
|
|
}
|
|
} else {
|
|
Is_Decrypted = false;
|
|
}
|
|
#endif
|
|
if (Removable)
|
|
Update_Size(Display_Error);
|
|
|
|
if (!Symlink_Mount_Point.empty()) {
|
|
string Command, Result;
|
|
Command = "mount '" + Symlink_Path + "' '" + Symlink_Mount_Point + "'";
|
|
TWFunc::Exec_Cmd(Command, Result);
|
|
}
|
|
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
|
|
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
if (EcryptFS_Password.size() > 0) {
|
|
if (unmount_ecryptfs_drive(Mount_Point.c_str()) != 0) {
|
|
if (Display_Error)
|
|
LOGE("Unable to unmount ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
else
|
|
LOGI("Unable to unmount ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
} else {
|
|
LOGI("Successfully unmounted ecryptfs for '%s'\n", Mount_Point.c_str());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!Symlink_Mount_Point.empty())
|
|
umount(Symlink_Mount_Point.c_str());
|
|
|
|
umount(Mount_Point.c_str());
|
|
if (Is_Mounted()) {
|
|
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(string New_File_System) {
|
|
bool wiped = false, update_crypt = false;
|
|
int check;
|
|
string Layout_Filename = Mount_Point + "/.layout_version";
|
|
|
|
if (!Can_Be_Wiped) {
|
|
LOGE("Partition '%s' cannot be wiped.\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (Mount_Point == "/cache")
|
|
tmplog_offset = 0;
|
|
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
if (Mount_Point == "/data" && Mount(false)) {
|
|
if (TWFunc::Path_Exists("/data/system/edk_p_sd"))
|
|
TWFunc::copy_file("/data/system/edk_p_sd", "/tmp/edk_p_sd", 0600);
|
|
}
|
|
#endif
|
|
|
|
if (Retain_Layout_Version && Mount(false) && TWFunc::Path_Exists(Layout_Filename))
|
|
TWFunc::copy_file(Layout_Filename, "/.layout_version", 0600);
|
|
else
|
|
unlink("/.layout_version");
|
|
|
|
if (Has_Data_Media) {
|
|
wiped = Wipe_Data_Without_Wiping_Media();
|
|
} else {
|
|
|
|
DataManager::GetValue(TW_RM_RF_VAR, check);
|
|
|
|
if (check)
|
|
wiped = Wipe_RMRF();
|
|
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);
|
|
else if (New_File_System == "vfat")
|
|
wiped = Wipe_FAT();
|
|
else if (New_File_System == "exfat")
|
|
wiped = Wipe_EXFAT();
|
|
else if (New_File_System == "yaffs2")
|
|
wiped = Wipe_MTD();
|
|
else {
|
|
LOGE("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str());
|
|
unlink("/.layout_version");
|
|
return false;
|
|
}
|
|
update_crypt = wiped;
|
|
}
|
|
|
|
if (wiped) {
|
|
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
|
|
if (Mount_Point == "/data" && Mount(false)) {
|
|
if (TWFunc::Path_Exists("/tmp/edk_p_sd")) {
|
|
Make_Dir("/data/system", true);
|
|
TWFunc::copy_file("/tmp/edk_p_sd", "/data/system/edk_p_sd", 0600);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (Mount_Point == "/cache")
|
|
DataManager::Output_Version();
|
|
|
|
if (TWFunc::Path_Exists("/.layout_version") && Mount(false))
|
|
TWFunc::copy_file("/.layout_version", Layout_Filename, 0600);
|
|
|
|
if (update_crypt) {
|
|
Setup_File_System(false);
|
|
if (Is_Encrypted && !Is_Decrypted) {
|
|
// just wiped an encrypted partition back to its unencrypted state
|
|
Is_Encrypted = false;
|
|
Is_Decrypted = false;
|
|
Decrypted_Block_Device = "";
|
|
if (Mount_Point == "/data") {
|
|
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
|
DataManager::SetValue(TW_IS_DECRYPTED, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return wiped;
|
|
}
|
|
|
|
bool TWPartition::Wipe() {
|
|
if (Is_File_System(Current_File_System))
|
|
return Wipe(Current_File_System);
|
|
else
|
|
return Wipe(Fstab_File_System);
|
|
}
|
|
|
|
bool TWPartition::Wipe_AndSec(void) {
|
|
if (!Has_Android_Secure)
|
|
return false;
|
|
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
ui_print("Wiping %s\n", Backup_Display_Name.c_str());
|
|
TWFunc::removeDir(Mount_Point + "/.android_secure/", true);
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Backup(string backup_folder) {
|
|
if (Backup_Method == FILES)
|
|
return Backup_Tar(backup_folder);
|
|
else if (Backup_Method == DD)
|
|
return Backup_DD(backup_folder);
|
|
else if (Backup_Method == FLASH_UTILS)
|
|
return Backup_Dump_Image(backup_folder);
|
|
LOGE("Unknown backup method for '%s'\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Check_MD5(string restore_folder) {
|
|
string Full_Filename, md5file;
|
|
char split_filename[512];
|
|
int index = 0;
|
|
twrpDigest md5sum;
|
|
|
|
memset(split_filename, 0, sizeof(split_filename));
|
|
Full_Filename = restore_folder + "/" + Backup_FileName;
|
|
if (!TWFunc::Path_Exists(Full_Filename)) {
|
|
// This is a split archive, we presume
|
|
sprintf(split_filename, "%s%03i", Full_Filename.c_str(), index);
|
|
LOGI("split_filename: %s\n", split_filename);
|
|
md5file = split_filename;
|
|
md5file += ".md5";
|
|
if (!TWFunc::Path_Exists(md5file)) {
|
|
LOGE("No md5 file found for '%s'.\n", split_filename);
|
|
LOGE("Please unselect Enable MD5 verification to restore.\n");
|
|
return false;
|
|
}
|
|
md5sum.setfn(split_filename);
|
|
while (index < 1000 && TWFunc::Path_Exists(split_filename)) {
|
|
if (md5sum.verify_md5digest() != 0) {
|
|
LOGE("MD5 failed to match on '%s'.\n", split_filename);
|
|
return false;
|
|
}
|
|
index++;
|
|
sprintf(split_filename, "%s%03i", Full_Filename.c_str(), index);
|
|
md5sum.setfn(split_filename);
|
|
}
|
|
return true;
|
|
} else {
|
|
// Single file archive
|
|
md5file = Full_Filename + ".md5";
|
|
if (!TWFunc::Path_Exists(md5file)) {
|
|
LOGE("No md5 file found for '%s'.\n", Full_Filename.c_str());
|
|
LOGE("Please unselect Enable MD5 verification to restore.\n");
|
|
return false;
|
|
}
|
|
md5sum.setfn(Full_Filename);
|
|
if (md5sum.verify_md5digest() != 0) {
|
|
LOGE("MD5 failed to match on '%s'.\n", Full_Filename.c_str());
|
|
return false;
|
|
} else
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Restore(string restore_folder) {
|
|
size_t first_period, second_period;
|
|
string Restore_File_System;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, "Restoring");
|
|
LOGI("Restore filename is: %s\n", Backup_FileName.c_str());
|
|
|
|
// Parse backup filename to extract the file system before wiping
|
|
first_period = Backup_FileName.find(".");
|
|
if (first_period == string::npos) {
|
|
LOGE("Unable to find file system (first period).\n");
|
|
return false;
|
|
}
|
|
Restore_File_System = Backup_FileName.substr(first_period + 1, Backup_FileName.size() - first_period - 1);
|
|
second_period = Restore_File_System.find(".");
|
|
if (second_period == string::npos) {
|
|
LOGE("Unable to find file system (second period).\n");
|
|
return false;
|
|
}
|
|
Restore_File_System.resize(second_period);
|
|
LOGI("Restore file system is: '%s'.\n", Restore_File_System.c_str());
|
|
|
|
if (Is_File_System(Restore_File_System))
|
|
return Restore_Tar(restore_folder, Restore_File_System);
|
|
else if (Is_Image(Restore_File_System)) {
|
|
if (Restore_File_System == "emmc")
|
|
return Restore_DD(restore_folder);
|
|
else if (Restore_File_System == "mtd" || Restore_File_System == "bml")
|
|
return Restore_Flash_Image(restore_folder);
|
|
}
|
|
|
|
LOGE("Unknown restore method for '%s'\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
|
|
string TWPartition::Backup_Method_By_Name() {
|
|
if (Backup_Method == NONE)
|
|
return "none";
|
|
else if (Backup_Method == FILES)
|
|
return "files";
|
|
else if (Backup_Method == DD)
|
|
return "dd";
|
|
else if (Backup_Method == FLASH_UTILS)
|
|
return "flash_utils";
|
|
else
|
|
return "undefined";
|
|
return "ERROR!";
|
|
}
|
|
|
|
bool TWPartition::Decrypt(string Password) {
|
|
LOGI("STUB TWPartition::Decrypt, password: '%s'\n", Password.c_str());
|
|
// Is this needed?
|
|
return 1;
|
|
}
|
|
|
|
bool TWPartition::Wipe_Encryption() {
|
|
bool Save_Data_Media = Has_Data_Media;
|
|
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
Has_Data_Media = false;
|
|
if (Wipe(Fstab_File_System)) {
|
|
Has_Data_Media = Save_Data_Media;
|
|
if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
|
|
Recreate_Media_Folder();
|
|
}
|
|
ui_print("You may need to reboot recovery to be able to use /data again.\n");
|
|
return true;
|
|
} else {
|
|
Has_Data_Media = Save_Data_Media;
|
|
LOGE("Unable to format to remove encryption.\n");
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TWPartition::Check_FS_Type() {
|
|
const char* type;
|
|
blkid_probe pr;
|
|
|
|
if (Fstab_File_System == "yaffs2" || Fstab_File_System == "mtd" || Fstab_File_System == "bml" || Ignore_Blkid)
|
|
return; // Running blkid on some mtd devices causes a massive crash or needs to be skipped
|
|
|
|
Find_Actual_Block_Device();
|
|
if (!Is_Present)
|
|
return;
|
|
|
|
pr = blkid_new_probe_from_filename(Actual_Block_Device.c_str());
|
|
if (blkid_do_fullprobe(pr)) {
|
|
blkid_free_probe(pr);
|
|
LOGI("Can't probe device %s\n", Actual_Block_Device.c_str());
|
|
return;
|
|
}
|
|
if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) < 0) {
|
|
blkid_free_probe(pr);
|
|
LOGI("can't find filesystem on device %s\n", Actual_Block_Device.c_str());
|
|
return;
|
|
}
|
|
Current_File_System = type;
|
|
return;
|
|
}
|
|
|
|
bool TWPartition::Wipe_EXT23(string File_System) {
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
if (TWFunc::Path_Exists("/sbin/mke2fs")) {
|
|
string command, result;
|
|
|
|
ui_print("Formatting %s using mke2fs...\n", Display_Name.c_str());
|
|
Find_Actual_Block_Device();
|
|
command = "mke2fs -t " + File_System + " -m 0 " + Actual_Block_Device;
|
|
LOGI("mke2fs command: %s\n", command.c_str());
|
|
if (TWFunc::Exec_Cmd(command, result) == 0) {
|
|
Current_File_System = File_System;
|
|
Recreate_AndSec_Folder();
|
|
ui_print("Done.\n");
|
|
return true;
|
|
} else {
|
|
LOGE("Unable to wipe '%s'.\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
} else
|
|
return Wipe_RMRF();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Wipe_EXT4() {
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
if (TWFunc::Path_Exists("/sbin/make_ext4fs")) {
|
|
string Command, result;
|
|
|
|
ui_print("Formatting %s using make_ext4fs...\n", Display_Name.c_str());
|
|
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;
|
|
}
|
|
Command += " " + Actual_Block_Device;
|
|
LOGI("make_ext4fs command: %s\n", Command.c_str());
|
|
if (TWFunc::Exec_Cmd(Command, result) == 0) {
|
|
Current_File_System = "ext4";
|
|
Recreate_AndSec_Folder();
|
|
ui_print("Done.\n");
|
|
return true;
|
|
} else {
|
|
LOGE("Unable to wipe '%s'.\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
} else
|
|
return Wipe_EXT23("ext4");
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Wipe_FAT() {
|
|
string command, result;
|
|
|
|
if (TWFunc::Path_Exists("/sbin/mkdosfs")) {
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
ui_print("Formatting %s using mkdosfs...\n", Display_Name.c_str());
|
|
Find_Actual_Block_Device();
|
|
command = "mkdosfs " + Actual_Block_Device;
|
|
if (TWFunc::Exec_Cmd(command, result) == 0) {
|
|
Current_File_System = "vfat";
|
|
Recreate_AndSec_Folder();
|
|
ui_print("Done.\n");
|
|
return true;
|
|
} else {
|
|
LOGE("Unable to wipe '%s'.\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
return Wipe_RMRF();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Wipe_EXFAT() {
|
|
string command, result;
|
|
|
|
if (TWFunc::Path_Exists("/sbin/mkexfatfs")) {
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
ui_print("Formatting %s using mkexfatfs...\n", Display_Name.c_str());
|
|
Find_Actual_Block_Device();
|
|
command = "mkexfatfs " + Actual_Block_Device;
|
|
if (TWFunc::Exec_Cmd(command, result) == 0) {
|
|
Recreate_AndSec_Folder();
|
|
ui_print("Done.\n");
|
|
return true;
|
|
} else {
|
|
LOGE("Unable to wipe '%s'.\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Wipe_MTD() {
|
|
if (!UnMount(true))
|
|
return false;
|
|
|
|
ui_print("MTD Formatting \"%s\"\n", MTD_Name.c_str());
|
|
|
|
mtd_scan_partitions();
|
|
const MtdPartition* mtd = mtd_find_partition_by_name(MTD_Name.c_str());
|
|
if (mtd == NULL) {
|
|
LOGE("No mtd partition named '%s'", MTD_Name.c_str());
|
|
return false;
|
|
}
|
|
|
|
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
|
if (ctx == NULL) {
|
|
LOGE("Can't write '%s', failed to format.", MTD_Name.c_str());
|
|
return false;
|
|
}
|
|
if (mtd_erase_blocks(ctx, -1) == -1) {
|
|
mtd_write_close(ctx);
|
|
LOGE("Failed to format '%s'", MTD_Name.c_str());
|
|
return false;
|
|
}
|
|
if (mtd_write_close(ctx) != 0) {
|
|
LOGE("Failed to close '%s'", MTD_Name.c_str());
|
|
return false;
|
|
}
|
|
Current_File_System = "yaffs2";
|
|
Recreate_AndSec_Folder();
|
|
ui_print("Done.\n");
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Wipe_RMRF() {
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
ui_print("Removing all files under '%s'\n", Mount_Point.c_str());
|
|
TWFunc::removeDir(Mount_Point, true);
|
|
Recreate_AndSec_Folder();
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Wipe_Data_Without_Wiping_Media() {
|
|
string dir;
|
|
|
|
// This handles wiping data on devices with "sdcard" in /data/media
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
ui_print("Wiping data without wiping /data/media ...\n");
|
|
|
|
DIR* d;
|
|
d = opendir("/data");
|
|
if (d != NULL) {
|
|
struct dirent* de;
|
|
while ((de = readdir(d)) != NULL) {
|
|
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
|
|
// The media folder is the "internal sdcard"
|
|
// The .layout_version file is responsible for determining whether 4.2 decides up upgrade
|
|
// the media folder for multi-user.
|
|
if (strcmp(de->d_name, "media") == 0 || strcmp(de->d_name, ".layout_version") == 0) continue;
|
|
|
|
dir = "/data/";
|
|
dir.append(de->d_name);
|
|
if (de->d_type == DT_DIR) {
|
|
TWFunc::removeDir(dir, false);
|
|
} else if (de->d_type == DT_REG || de->d_type == DT_LNK || de->d_type == DT_FIFO || de->d_type == DT_SOCK) {
|
|
if (!unlink(dir.c_str()))
|
|
LOGI("Unable to unlink '%s'\n", dir.c_str());
|
|
}
|
|
}
|
|
closedir(d);
|
|
ui_print("Done.\n");
|
|
return true;
|
|
}
|
|
ui_print("Dirent failed to open /data, error!\n");
|
|
return false;
|
|
}
|
|
|
|
bool TWPartition::Backup_Tar(string backup_folder) {
|
|
char back_name[255], split_index[5];
|
|
string Full_FileName, Split_FileName, Tar_Args, Command;
|
|
int use_compression, index, backup_count;
|
|
struct stat st;
|
|
unsigned long long total_bsize = 0, file_size;
|
|
twrpTar tar;
|
|
vector <string> files;
|
|
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Backup_Display_Name, "Backing Up");
|
|
ui_print("Backing up %s...\n", Backup_Display_Name.c_str());
|
|
|
|
DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
|
|
|
|
sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
|
|
Backup_FileName = back_name;
|
|
Full_FileName = backup_folder + "/" + Backup_FileName;
|
|
if (Backup_Size > MAX_ARCHIVE_SIZE) {
|
|
// This backup needs to be split into multiple archives
|
|
ui_print("Breaking backup file into multiple archives...\n");
|
|
sprintf(back_name, "%s", Backup_Path.c_str());
|
|
tar.setdir(back_name);
|
|
tar.setfn(Full_FileName);
|
|
backup_count = tar.splitArchiveFork();
|
|
if (backup_count == -1) {
|
|
LOGE("Error tarring split files!\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
Full_FileName = backup_folder + "/" + Backup_FileName;
|
|
if (use_compression) {
|
|
tar.setdir(Backup_Path);
|
|
tar.setfn(Full_FileName);
|
|
if (tar.createTarGZFork() != 0)
|
|
return -1;
|
|
string gzname = Full_FileName + ".gz";
|
|
rename(gzname.c_str(), Full_FileName.c_str());
|
|
}
|
|
else {
|
|
tar.setdir(Backup_Path);
|
|
tar.setfn(Full_FileName);
|
|
if (tar.createTarFork() != 0)
|
|
return -1;
|
|
}
|
|
if (TWFunc::Get_File_Size(Full_FileName) == 0) {
|
|
LOGE("Backup file size for '%s' is 0 bytes.\n", Full_FileName.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Backup_DD(string backup_folder) {
|
|
char back_name[255], backup_size[32];
|
|
string Full_FileName, Command, result, DD_BS;
|
|
int use_compression;
|
|
|
|
sprintf(backup_size, "%llu", Backup_Size);
|
|
DD_BS = backup_size;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, "Backing Up");
|
|
ui_print("Backing up %s...\n", Display_Name.c_str());
|
|
|
|
sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
|
|
Backup_FileName = back_name;
|
|
|
|
Full_FileName = backup_folder + "/" + Backup_FileName;
|
|
|
|
Command = "dd if=" + Actual_Block_Device + " of='" + Full_FileName + "'" + " bs=" + DD_BS + "c count=1";
|
|
LOGI("Backup command: '%s'\n", Command.c_str());
|
|
TWFunc::Exec_Cmd(Command, result);
|
|
if (TWFunc::Get_File_Size(Full_FileName) == 0) {
|
|
LOGE("Backup file size for '%s' is 0 bytes.\n", Full_FileName.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Backup_Dump_Image(string backup_folder) {
|
|
char back_name[255];
|
|
string Full_FileName, Command, result;
|
|
int use_compression;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, "Backing Up");
|
|
ui_print("Backing up %s...\n", Display_Name.c_str());
|
|
|
|
sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
|
|
Backup_FileName = back_name;
|
|
|
|
Full_FileName = backup_folder + "/" + Backup_FileName;
|
|
|
|
Command = "dump_image " + MTD_Name + " '" + Full_FileName + "'";
|
|
LOGI("Backup command: '%s'\n", Command.c_str());
|
|
TWFunc::Exec_Cmd(Command, result);
|
|
if (TWFunc::Get_File_Size(Full_FileName) == 0) {
|
|
// Actual size may not match backup size due to bad blocks on MTD devices so just check for 0 bytes
|
|
LOGE("Backup file size for '%s' is 0 bytes.\n", Full_FileName.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Restore_Tar(string restore_folder, string Restore_File_System) {
|
|
string Full_FileName, Command;
|
|
int index = 0;
|
|
char split_index[5];
|
|
|
|
if (Has_Android_Secure) {
|
|
if (!Wipe_AndSec())
|
|
return false;
|
|
} else {
|
|
ui_print("Wiping %s...\n", Display_Name.c_str());
|
|
if (!Wipe(Restore_File_System))
|
|
return false;
|
|
}
|
|
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Backup_Display_Name, "Restoring");
|
|
ui_print("Restoring %s...\n", Backup_Display_Name.c_str());
|
|
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
Full_FileName = restore_folder + "/" + Backup_FileName;
|
|
if (!TWFunc::Path_Exists(Full_FileName)) {
|
|
if (!TWFunc::Path_Exists(Full_FileName)) {
|
|
// Backup is multiple archives
|
|
LOGI("Backup is multiple archives.\n");
|
|
sprintf(split_index, "%03i", index);
|
|
Full_FileName = restore_folder + "/" + Backup_FileName + split_index;
|
|
while (TWFunc::Path_Exists(Full_FileName)) {
|
|
index++;
|
|
ui_print("Restoring archive %i...\n", index);
|
|
LOGI("Restoring '%s'...\n", Full_FileName.c_str());
|
|
twrpTar tar;
|
|
tar.setdir("/");
|
|
tar.setfn(Full_FileName);
|
|
if (tar.extractTarFork() != 0)
|
|
return false;
|
|
sprintf(split_index, "%03i", index);
|
|
Full_FileName = restore_folder + "/" + Backup_FileName + split_index;
|
|
}
|
|
if (index == 0) {
|
|
LOGE("Error locating restore file: '%s'\n", Full_FileName.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
twrpTar tar;
|
|
tar.setdir(Backup_Path);
|
|
tar.setfn(Full_FileName);
|
|
if (tar.extractTarFork() != 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Restore_DD(string restore_folder) {
|
|
string Full_FileName, Command, result;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, "Restoring");
|
|
Full_FileName = restore_folder + "/" + Backup_FileName;
|
|
|
|
if (!Find_Partition_Size()) {
|
|
LOGE("Unable to find partition size for '%s'\n", Mount_Point.c_str());
|
|
return false;
|
|
}
|
|
unsigned long long backup_size = TWFunc::Get_File_Size(Full_FileName);
|
|
if (backup_size > Size) {
|
|
LOGE("Size (%iMB) of backup '%s' is larger than target device '%s' (%iMB)\n",
|
|
(int)(backup_size / 1048576LLU), Full_FileName.c_str(),
|
|
Actual_Block_Device.c_str(), (int)(Size / 1048576LLU));
|
|
return false;
|
|
}
|
|
|
|
ui_print("Restoring %s...\n", Display_Name.c_str());
|
|
Command = "dd bs=4096 if='" + Full_FileName + "' of=" + Actual_Block_Device;
|
|
LOGI("Restore command: '%s'\n", Command.c_str());
|
|
TWFunc::Exec_Cmd(Command, result);
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Restore_Flash_Image(string restore_folder) {
|
|
string Full_FileName, Command, result;
|
|
|
|
ui_print("Restoring %s...\n", Display_Name.c_str());
|
|
Full_FileName = restore_folder + "/" + Backup_FileName;
|
|
// Sometimes flash image doesn't like to flash due to the first 2KB matching, so we erase first to ensure that it flashes
|
|
Command = "erase_image " + MTD_Name;
|
|
LOGI("Erase command: '%s'\n", Command.c_str());
|
|
TWFunc::Exec_Cmd(Command, result);
|
|
Command = "flash_image " + MTD_Name + " '" + Full_FileName + "'";
|
|
LOGI("Restore command: '%s'\n", Command.c_str());
|
|
TWFunc::Exec_Cmd(Command, result);
|
|
return true;
|
|
}
|
|
|
|
bool TWPartition::Update_Size(bool Display_Error) {
|
|
bool ret = false, Was_Already_Mounted = false;
|
|
|
|
if (!Can_Be_Mounted && !Is_Encrypted)
|
|
return false;
|
|
|
|
Was_Already_Mounted = Is_Mounted();
|
|
if (Removable || Is_Encrypted) {
|
|
if (!Mount(false))
|
|
return true;
|
|
} else if (!Mount(Display_Error))
|
|
return false;
|
|
|
|
ret = Get_Size_Via_statfs(Display_Error);
|
|
if (!ret || Size == 0) {
|
|
if (!Get_Size_Via_df(Display_Error)) {
|
|
if (!Was_Already_Mounted)
|
|
UnMount(false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (Has_Data_Media) {
|
|
if (Mount(Display_Error)) {
|
|
unsigned long long data_media_used, actual_data;
|
|
Used = TWFunc::Get_Folder_Size("/data", Display_Error);
|
|
data_media_used = TWFunc::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 {
|
|
if (!Was_Already_Mounted)
|
|
UnMount(false);
|
|
return false;
|
|
}
|
|
} else if (Has_Android_Secure) {
|
|
if (Mount(Display_Error))
|
|
Backup_Size = TWFunc::Get_Folder_Size(Backup_Path, Display_Error);
|
|
else {
|
|
if (!Was_Already_Mounted)
|
|
UnMount(false);
|
|
return false;
|
|
}
|
|
}
|
|
if (!Was_Already_Mounted)
|
|
UnMount(false);
|
|
return true;
|
|
}
|
|
|
|
void TWPartition::Find_Actual_Block_Device(void) {
|
|
if (Is_Decrypted) {
|
|
Actual_Block_Device = Decrypted_Block_Device;
|
|
if (TWFunc::Path_Exists(Primary_Block_Device))
|
|
Is_Present = true;
|
|
} else if (TWFunc::Path_Exists(Primary_Block_Device)) {
|
|
Is_Present = true;
|
|
Actual_Block_Device = Primary_Block_Device;
|
|
return;
|
|
}
|
|
if (Is_Decrypted) {
|
|
} else if (!Alternate_Block_Device.empty() && TWFunc::Path_Exists(Alternate_Block_Device)) {
|
|
Actual_Block_Device = Alternate_Block_Device;
|
|
Is_Present = true;
|
|
} else {
|
|
Is_Present = false;
|
|
}
|
|
}
|
|
|
|
void TWPartition::Recreate_Media_Folder(void) {
|
|
string Command;
|
|
|
|
if (!Mount(true)) {
|
|
LOGE("Unable to recreate /data/media folder.\n");
|
|
} else if (!TWFunc::Path_Exists("/data/media")) {
|
|
PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
|
|
LOGI("Recreating /data/media folder.\n");
|
|
mkdir("/data/media", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
|
PartitionManager.UnMount_By_Path(Symlink_Mount_Point, true);
|
|
}
|
|
}
|
|
|
|
void TWPartition::Recreate_AndSec_Folder(void) {
|
|
if (!Has_Android_Secure)
|
|
return;
|
|
LOGI("Creating %s: %s\n", Backup_Display_Name.c_str(), Symlink_Path.c_str());
|
|
if (!Mount(true)) {
|
|
LOGE("Unable to recreate %s folder.\n", Backup_Name.c_str());
|
|
} else if (!TWFunc::Path_Exists(Symlink_Path)) {
|
|
LOGI("Recreating %s folder.\n", Backup_Name.c_str());
|
|
PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
|
|
mkdir(Symlink_Path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
|
PartitionManager.UnMount_By_Path(Symlink_Mount_Point, true);
|
|
}
|
|
}
|