diff --git a/data.cpp b/data.cpp
index 0ce4ddc3..eb8b5534 100644
--- a/data.cpp
+++ b/data.cpp
@@ -852,6 +852,8 @@ void DataManager::SetDefaultValues()
mConstValues.insert(make_pair("tw_has_mtp", "0"));
mConstValues.insert(make_pair("tw_mtp_enabled", "0"));
#endif
+ mValues.insert(make_pair("tw_mount_system_ro", make_pair("1", 1)));
+ mValues.insert(make_pair("tw_never_show_system_ro_page", make_pair("0", 1)));
pthread_mutex_unlock(&m_valuesLock);
}
diff --git a/gui/action.cpp b/gui/action.cpp
index 34255124..7ecd0b46 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -196,6 +196,8 @@ GUIAction::GUIAction(xml_node<>* node)
ADD_ACTION(startmtp);
ADD_ACTION(stopmtp);
ADD_ACTION(cancelbackup);
+ ADD_ACTION(checkpartitionlifetimewrites);
+ ADD_ACTION(mountsystemtoggle);
// remember actions that run in the caller thread
for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it)
@@ -1737,3 +1739,55 @@ int GUIAction::getKeyByName(std::string key)
return atol(key.c_str());
}
+
+int GUIAction::checkpartitionlifetimewrites(std::string arg)
+{
+ int op_status = 0;
+ TWPartition* sys = PartitionManager.Find_Partition_By_Path(arg);
+
+ operation_start("Check Partition Lifetime Writes");
+ if (sys) {
+ if (sys->Check_Lifetime_Writes() != 0)
+ DataManager::SetValue("tw_lifetime_writes", 1);
+ else
+ DataManager::SetValue("tw_lifetime_writes", 0);
+ op_status = 0; // success
+ } else {
+ DataManager::SetValue("tw_lifetime_writes", 1);
+ op_status = 1; // fail
+ }
+
+ operation_end(op_status);
+ return 0;
+}
+
+int GUIAction::mountsystemtoggle(std::string arg)
+{
+ int op_status = 0;
+ bool remount_system = PartitionManager.Is_Mounted_By_Path("/system");
+
+ operation_start("Toggle System Mount");
+ if (!PartitionManager.UnMount_By_Path("/system", true)) {
+ op_status = 1; // fail
+ } else {
+ TWPartition* Part = PartitionManager.Find_Partition_By_Path("/system");
+ if (Part) {
+ if (DataManager::GetIntValue("tw_mount_system_ro")) {
+ DataManager::SetValue("tw_mount_system_ro", 0);
+ Part->Change_Mount_Read_Only(false);
+ } else {
+ DataManager::SetValue("tw_mount_system_ro", 1);
+ Part->Change_Mount_Read_Only(true);
+ }
+ if (remount_system) {
+ Part->Mount(true);
+ }
+ op_status = 0; // success
+ } else {
+ op_status = 1; // fail
+ }
+ }
+
+ operation_end(op_status);
+ return 0;
+}
diff --git a/gui/devices/1080x1920/res/ui.xml b/gui/devices/1080x1920/res/ui.xml
index 3135a03d..a611d058 100644
--- a/gui/devices/1080x1920/res/ui.xml
+++ b/gui/devices/1080x1920/res/ui.xml
@@ -86,6 +86,7 @@
+
diff --git a/gui/devices/320x320/res/ui.xml b/gui/devices/320x320/res/ui.xml
index 5495b771..1dec4050 100644
--- a/gui/devices/320x320/res/ui.xml
+++ b/gui/devices/320x320/res/ui.xml
@@ -174,6 +174,7 @@
+
@@ -189,7 +190,7 @@
-
+
diff --git a/gui/devices/480x800/res/ui.xml b/gui/devices/480x800/res/ui.xml
index 3c320a7e..98445410 100644
--- a/gui/devices/480x800/res/ui.xml
+++ b/gui/devices/480x800/res/ui.xml
@@ -82,6 +82,7 @@
+
diff --git a/gui/devices/landscape/res/landscape.xml b/gui/devices/landscape/res/landscape.xml
index 25b9b17e..12c66290 100644
--- a/gui/devices/landscape/res/landscape.xml
+++ b/gui/devices/landscape/res/landscape.xml
@@ -989,6 +989,27 @@
+
+
+
+
+
+
+
+
+
+
+ TWRP has detected an unmodified system partition.
+
+
+
+
+ TWRP can leave your system partition unmodified to make it easier for you to take official updates.
+
+
+
+
+ TWRP will be unable to prevent the stock ROM from replacing TWRP and will not offer to root your device.
+
+
+
+
+ Installing zips or performing adb operations may still modify the system partition.
+
+
+
+
+
+ Never show this screen during boot again
+
+
+
+
+
+ Keep Read Only
+
+ tw_mount_system_ro=1
+ tw_page_done=1
+ %tw_back%
+
+
+
+
+ Swipe to Allow Modifications
+
+ tw_mount_system_ro=0
+ tw_page_done=1
+ %tw_back%
+
+
+
+
+
diff --git a/gui/devices/portrait/res/portrait.xml b/gui/devices/portrait/res/portrait.xml
index bd235981..997da9d2 100644
--- a/gui/devices/portrait/res/portrait.xml
+++ b/gui/devices/portrait/res/portrait.xml
@@ -2105,6 +2105,27 @@
decrypt
+
+
+
+
+ Only mount system read-only
+
+
+
+
+
+
+
+
+ Only mount system read-only
+
+
+ tw_lifetime_writes=2
+ system_readonly_check
+
+
+
main
@@ -2161,6 +2182,31 @@
+
+
+ /system
+
+
+
+
+
+
+
+ mount
+
+
+
+
+
+
+
+
+ tw_back=mount
+ system_readonly
+
+
+
+
@@ -3607,5 +3653,72 @@
+
+
+
+
+
+
+ TWRP has detected an unmodified system partition.
+
+
+
+
+ TWRP can leave your system partition unmodified
+
+
+
+
+ to make it easier for you to take official updates.
+
+
+
+
+ TWRP will be unable to prevent the stock ROM from
+
+
+
+
+ replacing TWRP and will not offer to root your device.
+
+
+
+
+ Installing zips or performing adb operations may still
+
+
+
+
+ modify the system partition.
+
+
+
+
+
+ Never show this screen during boot again
+
+
+
+
+
+ Keep Read Only
+
+ tw_mount_system_ro=1
+ tw_page_done=1
+ %tw_back%
+
+
+
+
+ Swipe to Allow Modifications
+
+ tw_mount_system_ro=0
+ tw_page_done=1
+ %tw_back%
+
+
+
+
+
diff --git a/gui/devices/watch/res/watch.xml b/gui/devices/watch/res/watch.xml
index 872c47b6..f0f383dc 100644
--- a/gui/devices/watch/res/watch.xml
+++ b/gui/devices/watch/res/watch.xml
@@ -2113,6 +2113,27 @@
decrypt
+
+
+
+
+ Only mount system read-only
+
+
+
+
+
+
+
+
+ Only mount system read-only
+
+
+ tw_lifetime_writes=2
+ system_readonly_check
+
+
+
main
@@ -2168,6 +2189,31 @@
+
+
+ /system
+
+
+
+
+
+
+
+ mount
+
+
+
+
+
+
+
+
+ tw_back=mount
+ system_readonly
+
+
+
+
@@ -3596,5 +3642,70 @@
+
+
+
+
+
+
+ TWRP has detected an unmodified system partition.
+
+
+
+
+ TWRP can leave your system partition unmodified
+
+
+
+
+ to make it easier for you to take official updates.
+
+
+
+
+ TWRP will be unable to prevent the stock ROM from
+
+
+
+
+ replacing TWRP and will not offer to root your device.
+
+
+
+
+ Installing zips or performing adb operations may still
+
+
+
+
+ modify the system partition.
+
+
+
+
+
+ Never show this screen during boot again
+
+
+
+
+
+ Keep Read Only
+
+ tw_mount_system_ro=1
+ tw_page_done=1
+ %tw_back%
+
+
+
+
+ Swipe to Allow Modifications
+
+ tw_mount_system_ro=0
+ tw_page_done=1
+ %tw_back%
+
+
+
diff --git a/gui/objects.hpp b/gui/objects.hpp
index e70053e3..ee0f08b8 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -359,6 +359,8 @@ protected:
int stopmtp(std::string arg);
int flashimage(std::string arg);
int cancelbackup(std::string arg);
+ int checkpartitionlifetimewrites(std::string arg);
+ int mountsystemtoggle(std::string arg);
int simulate;
};
diff --git a/partition.cpp b/partition.cpp
index 248ee9bc..2f9f41a6 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -155,6 +155,7 @@ TWPartition::TWPartition() {
Crypto_Key_Location = "footer";
MTP_Storage_ID = 0;
Can_Flash_Img = false;
+ Mount_Read_Only = false;
}
TWPartition::~TWPartition(void) {
@@ -258,6 +259,7 @@ bool TWPartition::Process_Fstab_Line(string Line, bool Display_Error) {
Storage_Name = Display_Name;
Wipe_Available_in_GUI = true;
Can_Be_Backed_Up = true;
+ Mount_Read_Only = true;
} else if (Mount_Point == "/data") {
UnMount(false); // added in case /data is mounted as tmpfs for qcom hardware decrypt
Display_Name = "Data";
@@ -390,6 +392,11 @@ bool TWPartition::Process_Fstab_Line(string Line, bool Display_Error) {
Display_Name = "Recovery";
Backup_Display_Name = Display_Name;
Can_Flash_Img = true;
+ } else if (Mount_Point == "/system_image") {
+ Display_Name = "System Image";
+ Backup_Display_Name = Display_Name;
+ Can_Flash_Img = false;
+ Can_Be_Backed_Up = true;
}
}
@@ -936,6 +943,7 @@ bool TWPartition::Is_Mounted(void) {
bool TWPartition::Mount(bool Display_Error) {
int exfat_mounted = 0;
+ unsigned long flags = Mount_Flags;
if (Is_Mounted()) {
return true;
@@ -964,9 +972,15 @@ bool TWPartition::Mount(bool Display_Error) {
#endif
}
}
+
+ if (Mount_Read_Only)
+ flags |= MS_RDONLY;
+
if (Fstab_File_System == "yaffs2") {
// mount an MTD partition as a YAFFS2 filesystem.
- const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+ flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+ if (Mount_Read_Only)
+ flags |= MS_RDONLY;
if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags, NULL) < 0) {
if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags | MS_RDONLY, NULL) < 0) {
if (Display_Error)
@@ -1008,8 +1022,8 @@ bool TWPartition::Mount(bool Display_Error) {
mount_fs = "texfat";
if (!exfat_mounted &&
- mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), Mount_Flags, Mount_Options.c_str()) != 0 &&
- mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), Mount_Flags, NULL) != 0) {
+ mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), flags, Mount_Options.c_str()) != 0 &&
+ mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), flags, NULL) != 0) {
#ifdef TW_NO_EXFAT_FUSE
if (Current_File_System == "exfat") {
LOGINFO("Mounting exfat failed, trying vfat...\n");
@@ -1018,7 +1032,7 @@ bool TWPartition::Mount(bool Display_Error) {
LOGERR("Unable to mount '%s'\n", Mount_Point.c_str());
else
LOGINFO("Unable to mount '%s'\n", Mount_Point.c_str());
- LOGINFO("Actual block device: '%s', current file system: '%s', flags: 0x%8x, options: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str(), Mount_Flags, Mount_Options.c_str());
+ LOGINFO("Actual block device: '%s', current file system: '%s', flags: 0x%8x, options: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str(), flags, Mount_Options.c_str());
return false;
}
} else {
@@ -2138,3 +2152,37 @@ bool TWPartition::Flash_Image_FI(string Filename) {
TWFunc::Exec_Cmd(Command);
return true;
}
+
+void TWPartition::Change_Mount_Read_Only(bool new_value) {
+ Mount_Read_Only = new_value;
+}
+
+int TWPartition::Check_Lifetime_Writes() {
+ bool original_read_only = Mount_Read_Only;
+ int ret = 1;
+
+ Mount_Read_Only = true;
+ if (Mount(false)) {
+ Find_Actual_Block_Device();
+ string block = basename(Actual_Block_Device.c_str());
+ string file = "/sys/fs/" + Current_File_System + "/" + block + "/lifetime_write_kbytes";
+ string result;
+ if (TWFunc::Path_Exists(file)) {
+ if (TWFunc::read_file(file, result) != 0) {
+ LOGINFO("Check_Lifetime_Writes of '%s' failed to read_file\n", file.c_str());
+ } else {
+ LOGINFO("Check_Lifetime_Writes result: '%s'\n", result.c_str());
+ if (result == "0") {
+ ret = 0;
+ }
+ }
+ } else {
+ LOGINFO("Check_Lifetime_Writes file does not exist '%s'\n", file.c_str());
+ }
+ UnMount(true);
+ } else {
+ LOGINFO("Check_Lifetime_Writes failed to mount '%s'\n", Mount_Point.c_str());
+ }
+ Mount_Read_Only = original_read_only;
+ return ret;
+}
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index ffc17c3a..055f7369 100644
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -880,9 +880,13 @@ int TWPartitionManager::Run_Restore(string Restore_Name) {
restore_path = Restore_List.substr(start_pos, end_pos - start_pos);
restore_part = Find_Partition_By_Path(restore_path);
if (restore_part != NULL) {
- partition_count++;
+ if (restore_part->Mount_Read_Only) {
+ LOGERR("Cannot restore %s -- mounted read only.\n", restore_part->Backup_Display_Name.c_str());
+ return false;
+ }
if (check_md5 > 0 && !restore_part->Check_MD5(Restore_Name))
return false;
+ partition_count++;
total_restore_size += restore_part->Get_Restore_Size(Restore_Name);
if (restore_part->Has_SubPartition) {
std::vector::iterator subpart;
diff --git a/partitions.hpp b/partitions.hpp
index f74fac9a..1489a8ec 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -70,6 +70,8 @@ public:
bool Update_Size(bool Display_Error); // Updates size information
void Recreate_Media_Folder(); // Recreates the /data/media folder
bool Flash_Image(string Filename); // Flashes an image to the partition
+ void Change_Mount_Read_Only(bool new_value); // Changes Mount_Read_Only to new_value
+ int Check_Lifetime_Writes();
public:
string Current_File_System; // Current file system
@@ -167,6 +169,7 @@ private:
bool Ignore_Blkid; // Ignore blkid results due to superblocks lying to us on certain devices / partitions
bool Retain_Layout_Version; // Retains the .layout_version file during a wipe (needed on devices like Sony Xperia T where /data and /data/media are separate partitions)
bool Can_Flash_Img; // Indicates if this partition can have images flashed to it via the GUI
+ bool Mount_Read_Only; // Only mount this partition as read-only
friend class TWPartitionManager;
friend class DataManager;
diff --git a/twrp.cpp b/twrp.cpp
index 0c012d7e..b7fc9815 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -332,13 +332,27 @@ int main(int argc, char **argv) {
PartitionManager.Disable_MTP();
#endif
+ // Check if system has never been changed
+ if (DataManager::GetIntValue("tw_mount_system_ro") != 0 && DataManager::GetIntValue("tw_never_show_system_ro_page") == 0) {
+ TWPartition* sys = PartitionManager.Find_Partition_By_Path("/system");
+ if (sys && sys->Check_Lifetime_Writes() == 0) {
+ LOGINFO("System writes is 0, show system_readonly page\n");
+ DataManager::SetValue("tw_back", "main");
+ if (gui_startPage("system_readonly", 1, 1) != 0) {
+ LOGERR("Failed to start system_readonly GUI page.\n");
+ }
+ } else {
+ DataManager::SetValue("tw_mount_system_ro", 0);
+ }
+ }
+
// Launch the main GUI
gui_start();
// Disable flashing of stock recovery
TWFunc::Disable_Stock_Recovery_Replace();
// Check for su to see if the device is rooted or not
- if (PartitionManager.Mount_By_Path("/system", false)) {
+ if (PartitionManager.Mount_By_Path("/system", false) && DataManager::GetIntValue("tw_mount_system_ro") == 0) {
if (TWFunc::Path_Exists("/supersu/su") && !TWFunc::Path_Exists("/system/bin/su") && !TWFunc::Path_Exists("/system/xbin/su") && !TWFunc::Path_Exists("/system/bin/.ext/.su")) {
// Device doesn't have su installed
DataManager::SetValue("tw_busy", 1);