Adopted Storage support

-Detects, decrypts, and mounts an adopted SD card if a
  secondary block device is defined (usually mmcblk1)
 -Handles unified storage
 -Displays the adopted storage in MTP along with internal
 -Factory Reset - wiped just like a data media device, we
  retain the keys folder and the storage.xml during a
  factory reset
 -Backup / Restore
 -Disable mass storage when adopted storage is present
 -Read storage nickname from storage.xml and apply it to
  display names in the GUI
 -Read storage.xml and determine what storage location is in
  use for /sdcard and remap accordingly

libgpt_twrp is source code mostly kanged from an efimanager
project. It is GPL v2 or higher, so we will opt for GPL v3.

Change-Id: Ieda0030bec5155ba8d2b9167dc0016cebbf39d55
This commit is contained in:
Ethan Yonker
2016-01-25 23:59:17 +01:00
committed by Dees Troy
parent 56a7a99d8e
commit 66a1949df9
18 changed files with 1427 additions and 53 deletions
+201 -36
View File
@@ -26,6 +26,7 @@
#include <dirent.h>
#include <iostream>
#include <sstream>
#include <sys/param.h>
#ifdef TW_INCLUDE_CRYPTO
#include "cutils/properties.h"
@@ -53,6 +54,7 @@ extern "C" {
#ifdef TW_INCLUDE_CRYPTO
#include "crypto/lollipop/cryptfs.h"
#include "gpt/gpt.h"
#else
#define CRYPT_FOOTER_OFFSET 0x4000
#endif
@@ -159,6 +161,8 @@ TWPartition::TWPartition() {
MTP_Storage_ID = 0;
Can_Flash_Img = false;
Mount_Read_Only = false;
Is_Adopted_Storage = false;
Adopted_GUID = "";
}
TWPartition::~TWPartition(void) {
@@ -703,28 +707,40 @@ void TWPartition::Setup_AndSec(void) {
void TWPartition::Setup_Data_Media() {
LOGINFO("Setting up '%s' as data/media emulated storage.\n", Mount_Point.c_str());
Storage_Name = "Internal Storage";
if (Storage_Name.empty() || Storage_Name == "Data")
Storage_Name = "Internal Storage";
Has_Data_Media = true;
Is_Storage = true;
Is_Settings_Storage = true;
Storage_Path = "/data/media";
Storage_Path = Mount_Point + "/media";
Symlink_Path = Storage_Path;
if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
Make_Dir("/emmc", false);
Symlink_Mount_Point = "/emmc";
if (Mount_Point == "/data") {
Is_Settings_Storage = true;
if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
Make_Dir("/emmc", false);
Symlink_Mount_Point = "/emmc";
} else {
Make_Dir("/sdcard", false);
Symlink_Mount_Point = "/sdcard";
}
if (Mount(false) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
Storage_Path = Mount_Point + "/media/0";
Symlink_Path = Storage_Path;
DataManager::SetValue(TW_INTERNAL_PATH, Mount_Point + "/media/0");
UnMount(true);
}
DataManager::SetValue("tw_has_internal", 1);
DataManager::SetValue("tw_has_data_media", 1);
du.add_absolute_dir(Mount_Point + "/misc/vold");
du.add_absolute_dir(Mount_Point + "/.layout_version");
du.add_absolute_dir(Mount_Point + "/system/storage.xml");
} else {
Make_Dir("/sdcard", false);
Symlink_Mount_Point = "/sdcard";
if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
Storage_Path = Mount_Point + "/media/0";
Symlink_Path = Storage_Path;
UnMount(true);
}
}
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);
}
DataManager::SetValue("tw_has_internal", 1);
DataManager::SetValue("tw_has_data_media", 1);
du.add_absolute_dir("/data/media");
du.add_absolute_dir(Mount_Point + "/media");
}
void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) {
@@ -1204,7 +1220,7 @@ bool TWPartition::Wipe(string New_File_System) {
}
}
if (Mount_Point == "/data" && Has_Data_Media && recreate_media) {
if (Has_Data_Media && recreate_media) {
Recreate_Media_Folder();
}
}
@@ -1907,30 +1923,42 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media() {
// In an OEM Build we want to do a full format
return Wipe_Encryption();
#else
string dir;
bool ret = false;
// This handles wiping data on devices with "sdcard" in /data/media
if (!Mount(true))
return false;
gui_msg("wiping_data=Wiping data without wiping /data/media ...");
ret = Wipe_Data_Without_Wiping_Media_Func(Mount_Point + "/");
if (ret)
gui_msg("done=Done.");
return ret;
#endif // ifdef TW_OEM_BUILD
}
bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unused) {
string dir;
DIR* d;
d = opendir("/data");
d = opendir(parent.c_str());
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.
//TODO: convert this to use twrpDU.cpp
if (strcmp(de->d_name, "media") == 0 || strcmp(de->d_name, ".layout_version") == 0) continue;
dir = "/data/";
dir = parent;
dir.append(de->d_name);
if (du.check_skip_dirs(dir)) {
LOGINFO("skipped '%s'\n", dir.c_str());
continue;
}
if (de->d_type == DT_DIR) {
TWFunc::removeDir(dir, false);
dir.append("/");
if (!Wipe_Data_Without_Wiping_Media_Func(dir)) {
closedir(d);
return false;
}
rmdir(dir.c_str());
} 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()))
LOGINFO("Unable to unlink '%s'\n", dir.c_str());
@@ -1938,12 +1966,10 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media() {
}
closedir(d);
gui_msg("done=Done.");
return true;
}
gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Mount_Point)(strerror(errno)));
return false;
#endif // ifdef TW_OEM_BUILD
}
bool TWPartition::Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid) {
@@ -2200,7 +2226,7 @@ bool TWPartition::Update_Size(bool Display_Error) {
if (Has_Data_Media) {
if (Mount(Display_Error)) {
unsigned long long data_media_used, actual_data;
Used = du.Get_Folder_Size("/data");
Used = du.Get_Folder_Size(Mount_Point);
Backup_Size = Used;
int bak = (int)(Used / 1048576LLU);
int fre = (int)(Free / 1048576LLU);
@@ -2245,13 +2271,14 @@ void TWPartition::Find_Actual_Block_Device(void) {
void TWPartition::Recreate_Media_Folder(void) {
string Command;
string Media_Path = Mount_Point + "/media";
if (!Mount(true)) {
gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")("/data/media"));
} else if (!TWFunc::Path_Exists("/data/media")) {
gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")(Media_Path));
} else if (!TWFunc::Path_Exists(Media_Path)) {
PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
LOGINFO("Recreating /data/media folder.\n");
mkdir("/data/media", 0770);
LOGINFO("Recreating %s folder.\n", Media_Path.c_str());
mkdir(Media_Path.c_str(), 0770);
string Internal_path = DataManager::GetStrValue("tw_internal_path");
if (!Internal_path.empty()) {
LOGINFO("Recreating %s folder.\n", Internal_path.c_str());
@@ -2264,7 +2291,7 @@ void TWPartition::Recreate_Media_Folder(void) {
// Afterwards, we will try to set the
// default metadata that we were hopefully able to get during
// early boot.
tw_set_default_metadata("/data/media");
tw_set_default_metadata(Media_Path.c_str());
if (!Internal_path.empty())
tw_set_default_metadata(Internal_path.c_str());
#endif
@@ -2401,3 +2428,141 @@ int TWPartition::Check_Lifetime_Writes() {
Mount_Read_Only = original_read_only;
return ret;
}
int TWPartition::Decrypt_Adopted() {
#ifdef TW_INCLUDE_CRYPTO
int ret = 1;
Is_Adopted_Storage = false;
string Adopted_Key_File = "";
if (!Removable)
return ret;
int fd = open(Alternate_Block_Device.c_str(), O_RDONLY);
if (fd < 0) {
LOGINFO("failed to open '%s'\n", Alternate_Block_Device.c_str());
return ret;
}
char type_guid[80];
char part_guid[80];
if (gpt_disk_get_partition_info(fd, 2, type_guid, part_guid) == 0) {
LOGINFO("type: '%s'\n", type_guid);
LOGINFO("part: '%s'\n", part_guid);
Adopted_GUID = part_guid;
LOGINFO("Adopted_GUID '%s'\n", Adopted_GUID.c_str());
if (strcmp(type_guid, TWGptAndroidExpand) == 0) {
LOGINFO("android_expand found\n");
Adopted_Key_File = "/data/misc/vold/expand_";
Adopted_Key_File += part_guid;
Adopted_Key_File += ".key";
if (TWFunc::Path_Exists(Adopted_Key_File)) {
Is_Adopted_Storage = true;
/* Until we find a use case for this, I think it is safe
* to disable USB Mass Storage whenever adopted storage
* is present.
*/
LOGINFO("Detected adopted storage, disabling USB mass storage mode\n");
DataManager::SetValue("tw_has_usb_storage", 0);
}
}
}
if (Is_Adopted_Storage) {
string Adopted_Block_Device = Alternate_Block_Device + "p2";
if (!TWFunc::Path_Exists(Adopted_Block_Device)) {
Adopted_Block_Device = Alternate_Block_Device + "2";
if (!TWFunc::Path_Exists(Adopted_Block_Device)) {
LOGINFO("Adopted block device does not exist\n");
goto exit;
}
}
LOGINFO("key file is '%s', block device '%s'\n", Adopted_Key_File.c_str(), Adopted_Block_Device.c_str());
char crypto_blkdev[MAXPATHLEN];
std::string thekey;
int fdkey = open(Adopted_Key_File.c_str(), O_RDONLY);
if (fdkey < 0) {
LOGINFO("failed to open key file\n");
goto exit;
}
char buf[512];
ssize_t n;
while ((n = read(fdkey, &buf[0], sizeof(buf))) > 0) {
thekey.append(buf, n);
}
close(fdkey);
unsigned char* key = (unsigned char*) thekey.data();
cryptfs_revert_ext_volume(part_guid);
ret = cryptfs_setup_ext_volume(part_guid, Adopted_Block_Device.c_str(), key, thekey.size(), crypto_blkdev);
if (ret == 0) {
LOGINFO("adopted storage new block device: '%s'\n", crypto_blkdev);
Decrypted_Block_Device = crypto_blkdev;
Is_Decrypted = true;
Is_Encrypted = true;
Find_Actual_Block_Device();
if (!Mount(false)) {
LOGERR("Failed to mount decrypted adopted storage device\n");
Is_Decrypted = false;
Is_Encrypted = false;
cryptfs_revert_ext_volume(part_guid);
ret = 1;
} else {
Setup_Data_Media();
Recreate_Media_Folder();
Wipe_Available_in_GUI = true;
Wipe_During_Factory_Reset = true;
Can_Be_Backed_Up = true;
Can_Encrypt_Backup = true;
Use_Userdata_Encryption = true;
Is_Storage = true;
Storage_Name = "Adopted Storage";
Is_SubPartition = true;
SubPartition_Of = "/data";
PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
DataManager::SetValue("tw_has_adopted_storage", 1);
}
} else {
LOGERR("Failed to setup adopted storage decryption\n");
}
}
exit:
return ret;
#else
LOGINFO("Decrypt_Adopted: no crypto support\n");
return 1;
#endif
}
void TWPartition::Revert_Adopted() {
#ifdef TW_INCLUDE_CRYPTO
if (!Adopted_GUID.empty()) {
PartitionManager.Remove_MTP_Storage(Mount_Point);
UnMount(false);
cryptfs_revert_ext_volume(Adopted_GUID.c_str());
Is_Adopted_Storage = false;
Is_Encrypted = false;
Is_Decrypted = false;
Decrypted_Block_Device = "";
Find_Actual_Block_Device();
Wipe_During_Factory_Reset = false;
Can_Be_Backed_Up = false;
Can_Encrypt_Backup = false;
Use_Userdata_Encryption = false;
Is_SubPartition = false;
SubPartition_Of = "";
Has_Data_Media = false;
Storage_Path = Mount_Point;
if (!Symlink_Mount_Point.empty()) {
TWPartition* Dat = PartitionManager.Find_Partition_By_Path("/data");
if (Dat) {
Dat->UnMount(false);
Dat->Symlink_Mount_Point = Symlink_Mount_Point;
}
Symlink_Mount_Point = "";
}
}
#else
LOGINFO("Revert_Adopted: no crypto support\n");
#endif
}