Move Exec_Cmd to libcrecovery __popen Provide opt out build flag for exFAT Default fstype to exfat on external storage if exfat support is present and fstype is vfat or auto Fix invalid unmount errors Improve handling of unencrypted sdcards on Samsung devices
1559 lines
44 KiB
C++
1559 lines
44 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 "variables.h"
|
|
#include "common.h"
|
|
#include "partitions.hpp"
|
|
#include "data.hpp"
|
|
#include "twrp-functions.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;
|
|
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_Name = "";
|
|
Backup_FileName = "";
|
|
MTD_Name = "";
|
|
Backup_Method = NONE;
|
|
Has_Data_Media = false;
|
|
Has_Android_Secure = false;
|
|
Is_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);
|
|
|
|
for (index = 0; index < line_len; index++) {
|
|
if (full_line[index] <= 32)
|
|
full_line[index] = '\0';
|
|
}
|
|
Mount_Point = full_line;
|
|
LOGI("Processing '%s'\n", Mount_Point.c_str());
|
|
Backup_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";
|
|
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";
|
|
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";
|
|
Wipe_Available_in_GUI = true;
|
|
Wipe_During_Factory_Reset = 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";
|
|
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;
|
|
Removable = true;
|
|
} else if (Mount_Point == "/boot") {
|
|
Display_Name = "Boot";
|
|
DataManager::SetValue("tw_boot_is_mountable", 1);
|
|
}
|
|
#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;
|
|
#else
|
|
if (Mount_Point == "/sdcard") {
|
|
Is_Storage = true;
|
|
Storage_Path = "/sdcard";
|
|
Removable = true;
|
|
#ifndef RECOVERY_SDCARD_ON_DATA
|
|
Setup_AndSec();
|
|
Mount_Storage_Retry();
|
|
#endif
|
|
#endif
|
|
// blkid cannot detect exfat so we force exfat at the start if exfat support is present
|
|
if (TWFunc::Path_Exists("/sbin/exfat-fuse") && (Fstab_File_System == "vfat" || Fstab_File_System == "auto")) {
|
|
Fstab_File_System = "exfat";
|
|
Current_File_System = Fstab_File_System;
|
|
}
|
|
}
|
|
#ifdef TW_INTERNAL_STORAGE_PATH
|
|
if (Mount_Point == EXPAND(TW_INTERNAL_STORAGE_PATH)) {
|
|
Is_Storage = true;
|
|
Storage_Path = EXPAND(TW_INTERNAL_STORAGE_PATH);
|
|
#ifndef RECOVERY_SDCARD_ON_DATA
|
|
Setup_AndSec();
|
|
Mount_Storage_Retry();
|
|
#endif
|
|
}
|
|
#else
|
|
if (Mount_Point == "/emmc") {
|
|
Is_Storage = true;
|
|
Storage_Path = "/emmc";
|
|
#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);
|
|
}
|
|
|
|
// 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 += 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 (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 == "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_Name = "and-sec";
|
|
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;
|
|
|
|
// 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) {
|
|
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 (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 (Current_File_System == "exfat" && TWFunc::Path_Exists("/sbin/exfat-fuse")) {
|
|
string cmd = "/sbin/exfat-fuse " + Actual_Block_Device + " " + Mount_Point;
|
|
LOGI("cmd: %s\n", cmd.c_str());
|
|
string result;
|
|
if (TWFunc::Exec_Cmd(cmd, result) != 0)
|
|
return false;
|
|
} else if (mount(Actual_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());
|
|
LOGI("Actual block device: '%s', current file system: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str());
|
|
return false;
|
|
} else {
|
|
#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;
|
|
}
|
|
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 .android_secure\n");
|
|
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;
|
|
char split_filename[512];
|
|
int index = 0;
|
|
|
|
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);
|
|
while (index < 1000 && TWFunc::Path_Exists(split_filename)) {
|
|
if (TWFunc::Check_MD5(split_filename) == 0) {
|
|
LOGE("MD5 failed to match on '%s'.\n", split_filename);
|
|
return false;
|
|
}
|
|
index++;
|
|
sprintf(split_filename, "%s%03i", Full_Filename.c_str(), index);
|
|
}
|
|
return true;
|
|
} else {
|
|
// Single file archive
|
|
if (TWFunc::Check_MD5(Full_Filename) == 0) {
|
|
LOGE("MD5 failed to match on '%s'.\n", split_filename);
|
|
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() {
|
|
string blkCommand, result;
|
|
string blkOutput;
|
|
char* blk;
|
|
char* arg;
|
|
char* ptr;
|
|
int type_found = 0;
|
|
|
|
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;
|
|
|
|
blkCommand = "blkid " + Actual_Block_Device;
|
|
TWFunc::Exec_Cmd(blkCommand, result);
|
|
std::stringstream line(result);
|
|
while (getline(line, blkOutput))
|
|
{
|
|
blk = (char*) blkOutput.c_str();
|
|
ptr = (char*) blkOutput.c_str();
|
|
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) {
|
|
type_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*ptr == 0) {
|
|
arg = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type_found) {
|
|
arg += 6; // Skip the TYPE=" portion
|
|
arg[strlen(arg)-1] = '\0'; // Drop the tail quote
|
|
if (Current_File_System != arg) {
|
|
LOGI("'%s' was '%s' now set to '%s'\n", Mount_Point.c_str(), Current_File_System.c_str(), arg);
|
|
Current_File_System = arg;
|
|
}
|
|
} else
|
|
continue;
|
|
}
|
|
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;
|
|
|
|
if (Backup_Path == "/and-sec") {
|
|
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, "Android Secure", "Backing Up");
|
|
ui_print("Backing up %s...\n", "Android Secure");
|
|
} else {
|
|
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, "Backing Up");
|
|
ui_print("Backing up %s...\n", 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.splitArchiveThread();
|
|
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.createTarGZThread() != 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.createTarThread() != 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];
|
|
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 = "dd if=" + Actual_Block_Device + " of='" + Full_FileName + "'";
|
|
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) {
|
|
ui_print("Wiping android secure...\n");
|
|
if (!Wipe_AndSec())
|
|
return false;
|
|
} else {
|
|
ui_print("Wiping %s...\n", Display_Name.c_str());
|
|
if (!Wipe(Restore_File_System))
|
|
return false;
|
|
}
|
|
|
|
if (!Mount(true))
|
|
return false;
|
|
|
|
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, "Restoring");
|
|
ui_print("Restoring %s...\n", Display_Name.c_str());
|
|
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.extractTarThread() != 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.extractTarThread() != 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 .android_secure: %s\n", Symlink_Path.c_str());
|
|
if (!Mount(true)) {
|
|
LOGE("Unable to recreate android secure folder.\n");
|
|
} else if (!TWFunc::Path_Exists(Symlink_Path)) {
|
|
LOGI("Recreating android secure folder.\n");
|
|
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);
|
|
}
|
|
}
|