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
diff --git a/partition.cpp b/partition.cpp
index 71d6f22..54bf5e1 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -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 @@
#ifdef TW_INCLUDE_CRYPTO
#include "crypto/lollipop/cryptfs.h"
+ #include "gpt/gpt.h"
#else
#define CRYPT_FOOTER_OFFSET 0x4000
#endif
@@ -159,6 +161,8 @@
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_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 @@
}
}
- if (Mount_Point == "/data" && Has_Data_Media && recreate_media) {
+ if (Has_Data_Media && recreate_media) {
Recreate_Media_Folder();
}
}
@@ -1907,30 +1923,42 @@
// 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 @@
}
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 @@
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::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 @@
// 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 @@
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
+}