From 6e8c27a52b67e3474936113f897f309a0091910a Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Thu, 22 Dec 2016 17:55:57 -0600 Subject: Support v2 fstab format Auto detect and support both the v1 and v2 fstab formats Support putting TWRP style flags in a separate /etc/twrp.flags file twrp.flags format is the same as twrp.fstab (v1 with TWRP flags) Support using a wildcard in a block device and find all partitions: /usb-otg vfat /dev/block/sda* Support using sysfs entries (voldmanaged) and read uevents and scan for wildcard partitions from uevent data. (twvold?) May not be complete for some of the newer flags found in fstabs in newer build trees and there is a slim chance of a crash if the user removes a removable device while TWRP is performing actions. May need to add some kind of mutex to prevent the 2 threads from causing this crash. We need to start somewhere though and this change is pretty innocuous when not using a v2 fstab. Change-Id: I617d97c7db332cbe671a9d2b8ad98b3d9c4f03cc --- partition.cpp | 351 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 296 insertions(+), 55 deletions(-) (limited to 'partition.cpp') diff --git a/partition.cpp b/partition.cpp index 4157e9791..8b73f646b 100644 --- a/partition.cpp +++ b/partition.cpp @@ -77,40 +77,47 @@ extern "C" { using namespace std; +static int auto_index = 0; // v2 fstab allows you to specify a mount point of "auto" with no /. These items are given a mount point of /auto* where * == auto_index + extern struct selabel_handle *selinux_handle; extern bool datamedia; struct flag_list { const char *name; - unsigned flag; + unsigned long flag; }; const struct flag_list mount_flags[] = { - { "noatime", MS_NOATIME }, - { "noexec", MS_NOEXEC }, - { "nosuid", MS_NOSUID }, - { "nodev", MS_NODEV }, - { "nodiratime", MS_NODIRATIME }, - { "ro", MS_RDONLY }, - { "rw", 0 }, - { "remount", MS_REMOUNT }, - { "bind", MS_BIND }, - { "rec", MS_REC }, + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, #ifdef MS_UNBINDABLE - { "unbindable", MS_UNBINDABLE }, + { "unbindable", MS_UNBINDABLE }, #endif #ifdef MS_PRIVATE - { "private", MS_PRIVATE }, + { "private", MS_PRIVATE }, #endif #ifdef MS_SLAVE - { "slave", MS_SLAVE }, + { "slave", MS_SLAVE }, #endif #ifdef MS_SHARED - { "shared", MS_SHARED }, + { "shared", MS_SHARED }, #endif - { "sync", MS_SYNCHRONOUS }, - { "defaults", 0 }, - { 0, 0 }, + { "sync", MS_SYNCHRONOUS }, + { 0, 0 }, +}; + +const char *ignored_mount_items[] = { + "defaults=", + "errors=", + NULL }; enum TW_FSTAB_FLAGS { @@ -141,6 +148,14 @@ enum TW_FSTAB_FLAGS { TWFLAG_WIPEDURINGFACTORYRESET, TWFLAG_WIPEINGUI, TWFLAG_SLOTSELECT, + TWFLAG_WAIT, + TWFLAG_VERIFY, + TWFLAG_CHECK, + TWFLAG_ALTDEVICE, + TWFLAG_NOTRIM, + TWFLAG_VOLDMANAGED, + TWFLAG_FORMATTABLE, + TWFLAG_RESIZE, }; /* Flags without a trailing '=' are considered dual format flags and can be @@ -175,6 +190,14 @@ const struct flag_list tw_flags[] = { { "wipeduringfactoryreset", TWFLAG_WIPEDURINGFACTORYRESET }, { "wipeingui", TWFLAG_WIPEINGUI }, { "slotselect", TWFLAG_SLOTSELECT }, + { "wait", TWFLAG_WAIT }, + { "verify", TWFLAG_VERIFY }, + { "check", TWFLAG_CHECK }, + { "altdevice", TWFLAG_ALTDEVICE }, + { "notrim", TWFLAG_NOTRIM }, + { "voldmanaged=", TWFLAG_VOLDMANAGED }, + { "formattable", TWFLAG_FORMATTABLE }, + { "resize", TWFLAG_RESIZE }, { 0, 0 }, }; @@ -192,6 +215,8 @@ TWPartition::TWPartition() { Symlink_Mount_Point = ""; Mount_Point = ""; Backup_Path = ""; + Wildcard_Block_Device = false; + Sysfs_Entry = ""; Actual_Block_Device = ""; Primary_Block_Device = ""; Alternate_Block_Device = ""; @@ -242,12 +267,15 @@ TWPartition::~TWPartition(void) { // Do nothing } -bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) { +bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error, std::map *twrp_flags) { char full_line[MAX_FSTAB_LINE_LENGTH]; char twflags[MAX_FSTAB_LINE_LENGTH] = ""; char* ptr; int line_len = strlen(fstab_line), index = 0, item_index = 0; bool skip = false; + int fstab_version = 1, mount_point_index = 0, fs_index = 1, block_device_index = 2; + TWPartition *additional_entry = NULL; + std::map::iterator it; strlcpy(full_line, fstab_line, sizeof(full_line)); for (index = 0; index < line_len; index++) { @@ -256,26 +284,43 @@ bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) if (!skip && full_line[index] <= 32) full_line[index] = '\0'; } - Mount_Point = full_line; - LOGINFO("Processing '%s'\n", Mount_Point.c_str()); - Backup_Path = Mount_Point; - Storage_Path = Mount_Point; - Display_Name = full_line + 1; - Backup_Display_Name = Display_Name; - Storage_Name = Display_Name; - index = Mount_Point.size(); + if (line_len < 10) + return false; // There can't possibly be a valid fstab line that is less than 10 chars + if (strncmp(fstab_line, "/dev/", strlen("/dev/")) == 0 || strncmp(fstab_line, "/devices/", strlen("/devices/")) == 0) { + fstab_version = 2; + block_device_index = 0; + mount_point_index = 1; + fs_index = 2; + } + + index = 0; 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) { + if (item_index == mount_point_index) { + Mount_Point = ptr; + if (fstab_version == 2) { + additional_entry = PartitionManager.Find_Partition_By_Path(Mount_Point); + if (additional_entry) { + LOGINFO("Found an additional entry for '%s'\n", Mount_Point.c_str()); + } + } + LOGINFO("Processing '%s'\n", Mount_Point.c_str()); + Backup_Path = Mount_Point; + Storage_Path = Mount_Point; + Display_Name = ptr + 1; + Backup_Display_Name = Display_Name; + Storage_Name = Display_Name; + item_index++; + } else if (item_index == fs_index) { // File System Fstab_File_System = ptr; Current_File_System = ptr; item_index++; - } else if (item_index == 1) { + } else if (item_index == block_device_index) { // Primary Block Device if (Fstab_File_System == "mtd" || Fstab_File_System == "yaffs2") { MTD_Name = ptr; @@ -299,8 +344,19 @@ bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) Find_Real_Block_Device(Primary_Block_Device, Display_Error); } item_index++; - } else if (item_index > 1) { - if (*ptr == '/') { + } else if (item_index > 2) { + if (fstab_version == 2) { + if (item_index == 3) { + Process_FS_Flags(ptr); + if (additional_entry) { + additional_entry->Save_FS_Flags(Fstab_File_System, Mount_Flags, Mount_Options); + return false; // We save the extra fs flags in the other partition entry and by returning false, this entry will be deleted + } + } else { + strlcpy(twflags, ptr, sizeof(twflags)); + } + item_index++; + } else if (*ptr == '/') { // v2 fstab does not allow alternate block devices // Alternate Block Device Alternate_Block_Device = ptr; Find_Real_Block_Device(Alternate_Block_Device, Display_Error); @@ -323,7 +379,50 @@ bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) index++; } - if (!Is_File_System(Fstab_File_System) && !Is_Image(Fstab_File_System)) { + // override block devices from the v2 fstab with the ones we read from the twrp.flags file in case they are different + if (fstab_version == 2 && twrp_flags && twrp_flags->size() > 0) { + it = twrp_flags->find(Mount_Point); + if (it != twrp_flags->end()) { + if (!it->second.Primary_Block_Device.empty()) { + Primary_Block_Device = it->second.Primary_Block_Device; + Find_Real_Block_Device(Primary_Block_Device, Display_Error); + } + if (!it->second.Alternate_Block_Device.empty()) { + Alternate_Block_Device = it->second.Alternate_Block_Device; + Find_Real_Block_Device(Alternate_Block_Device, Display_Error); + } + } + } + + if (strncmp(fstab_line, "/devices/", strlen("/devices/")) == 0) { + Sysfs_Entry = Primary_Block_Device; + Primary_Block_Device = ""; + Is_Storage = true; + Removable = true; + Wipe_Available_in_GUI = true; + Wildcard_Block_Device = true; + } + if (Primary_Block_Device.find("*") != string::npos) + Wildcard_Block_Device = true; + + if (Mount_Point == "auto") { + Mount_Point = "/auto"; + char autoi[5]; + sprintf(autoi, "%i", auto_index); + Mount_Point += autoi; + Backup_Path = Mount_Point; + Storage_Path = Mount_Point; + auto_index++; + Setup_File_System(Display_Error); + Display_Name = "Storage"; + Backup_Display_Name = Display_Name; + Storage_Name = Display_Name; + Can_Be_Backed_Up = false; + Wipe_Available_in_GUI = true; + Is_Storage = true; + Removable = true; + Wipe_Available_in_GUI = true; + } else if (!Is_File_System(Fstab_File_System) && !Is_Image(Fstab_File_System)) { if (Display_Error) LOGERR("Unknown File System: '%s'\n", Fstab_File_System.c_str()); else @@ -448,7 +547,8 @@ bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) Storage_Name = ""; Backup_Display_Name = ""; - Process_TW_Flags(twflags, Display_Error); + Process_TW_Flags(twflags, (fstab_version == 1), fstab_version); + Save_FS_Flags(Fstab_File_System, Mount_Flags, Mount_Options); bool has_display_name = !Display_Name.empty(); bool has_storage_name = !Storage_Name.empty(); @@ -466,6 +566,21 @@ bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) if (!has_display_name && has_backup_name) Display_Name = Backup_Display_Name; } + + if (fstab_version == 2 && twrp_flags && twrp_flags->size() > 0) { + it = twrp_flags->find(Mount_Point); + if (it != twrp_flags->end()) { + char twrpflags[MAX_FSTAB_LINE_LENGTH] = ""; + int skip = 0; + string Flags = it->second.Flags; + strcpy(twrpflags, Flags.c_str()); + if (strlen(twrpflags) > strlen("flags=") && strncmp(twrpflags, "flags=", strlen("flags=")) == 0) + skip += strlen("flags="); + char* flagptr = twrpflags; + flagptr += skip; + Process_TW_Flags(flagptr, Display_Error, 1); // Forcing the fstab to ver 1 because this data is coming from the /etc/twrp.flags which should be using the TWRP v1 flags format + } + } return true; } @@ -599,35 +714,59 @@ void TWPartition::Process_FS_Flags(const char *str) { Mount_Options = ""; // Avoid issues with potentially nested strtok by using strtok_r - ptr = strtok_r(options, ",", &savep); - while (ptr) { - const struct flag_list* mount_flag = mount_flags; + for (ptr = strtok_r(options, ",", &savep); ptr; ptr = strtok_r(NULL, ",", &savep)) { + char *equals = strstr(ptr, "="); + size_t name_len; - for (; mount_flag->name; mount_flag++) { - // mount_flags are never postfixed by '=', - // so only match identical strings (including length) - if (strcmp(ptr, mount_flag->name) == 0) { - Mount_Flags |= mount_flag->flag; + if (!equals) + name_len = strlen(ptr); + else + name_len = equals - ptr; + + // There are some flags that we want to ignore in TWRP + bool found_match = false; + for (const char** ignored_mount_item = ignored_mount_items; *ignored_mount_item; ignored_mount_item++) { + if (strncmp(ptr, *ignored_mount_item, name_len) == 0) { + found_match = true; break; } } + if (found_match) + continue; - if (mount_flag->flag == MS_RDONLY) - Mount_Read_Only = true; - - if (mount_flag->name != 0) { - if (!Mount_Options.empty()) - Mount_Options += ","; - Mount_Options += mount_flag->name; - } else { - LOGINFO("Unhandled mount flag: '%s'\n", ptr); + // mount_flags are never postfixed by '=' + if (!equals) { + const struct flag_list* mount_flag = mount_flags; + for (; mount_flag->name; mount_flag++) { + if (strcmp(ptr, mount_flag->name) == 0) { + if (mount_flag->flag == MS_RDONLY) + Mount_Read_Only = true; + else + Mount_Flags |= (unsigned)mount_flag->flag; + found_match = true; + break; + } + } + if (found_match) + continue; } - ptr = strtok_r(NULL, ",", &savep); + // If we aren't ignoring this flag and it's not a mount flag, then it must be a mount option + if (!Mount_Options.empty()) + Mount_Options += ","; + Mount_Options += ptr; } free(options); } +void TWPartition::Save_FS_Flags(const string& local_File_System, int local_Mount_Flags, const string& local_Mount_Options) { + partition_fs_flags_struct flags; + flags.File_System = local_File_System; + flags.Mount_Flags = local_Mount_Flags; + flags.Mount_Options = local_Mount_Options; + fs_flags.push_back(flags); +} + void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool val) { switch (flag) { case TWFLAG_ANDSEC: @@ -649,6 +788,12 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool Can_Encrypt_Backup = val; break; case TWFLAG_DEFAULTS: + case TWFLAG_WAIT: + case TWFLAG_VERIFY: + case TWFLAG_CHECK: + case TWFLAG_NOTRIM: + case TWFLAG_VOLDMANAGED: + case TWFLAG_RESIZE: // Do nothing break; case TWFLAG_DISPLAY: @@ -713,6 +858,7 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool } break; case TWFLAG_WIPEINGUI: + case TWFLAG_FORMATTABLE: Wipe_Available_in_GUI = val; if (Wipe_Available_in_GUI) Can_Be_Wiped = true; @@ -720,6 +866,9 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool case TWFLAG_SLOTSELECT: SlotSelect = true; break; + case TWFLAG_ALTDEVICE: + Alternate_Block_Device = str; + break; default: // Should not get here LOGINFO("Flag identified for processing, but later unmatched: %i\n", flag); @@ -727,16 +876,20 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool } } -void TWPartition::Process_TW_Flags(char *flags, bool Display_Error) { +void TWPartition::Process_TW_Flags(char *flags, bool Display_Error, int fstab_ver) { char separator[2] = {'\n', 0}; char *ptr, *savep; + char source_separator = ';'; + + if (fstab_ver == 2) + source_separator = ','; // Semicolons within double-quotes are not forbidden, so replace // only the semicolons intended as separators with '\n' for strtok for (unsigned i = 0, skip = 0; i < strlen(flags); i++) { if (flags[i] == '\"') skip = !skip; - if (!skip && flags[i] == ';') + if (!skip && flags[i] == source_separator) flags[i] = separator[0]; } @@ -925,7 +1078,7 @@ void TWPartition::Setup_Data_Media() { } void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) { - char device[512], realDevice[512]; + char device[PATH_MAX], realDevice[PATH_MAX]; strcpy(device, Block.c_str()); memset(realDevice, 0, sizeof(realDevice)); @@ -1826,6 +1979,23 @@ void TWPartition::Check_FS_Type() { Current_File_System = type; blkid_free_probe(pr); + if (fs_flags.size() > 1) { + std::vector::iterator iter; + std::vector::iterator found = fs_flags.begin(); + + for (iter = fs_flags.begin(); iter != fs_flags.end(); iter++) { + if (iter->File_System == Current_File_System) { + found = iter; + break; + } + } + // If we don't find a match, we default the flags to the first set of flags that we received from the fstab + if (Mount_Flags != found->Mount_Flags || Mount_Options != found->Mount_Options) { + Mount_Flags = found->Mount_Flags; + Mount_Options = found->Mount_Options; + LOGINFO("Mount_Flags: %i, Mount_Options: %s\n", Mount_Flags, Mount_Options.c_str()); + } + } } bool TWPartition::Wipe_EXT23(string File_System) { @@ -2526,8 +2696,79 @@ bool TWPartition::Update_Size(bool Display_Error) { return true; } +bool TWPartition::Find_Wildcard_Block_Devices(const string& Device) { + int mount_point_index = 0; // we will need to create separate mount points for each partition found and we use this index to name each one + string Path = TWFunc::Get_Path(Device); + string Dev = TWFunc::Get_Filename(Device); + size_t wildcard_index = Dev.find("*"); + if (wildcard_index != string::npos) + Dev = Dev.substr(0, wildcard_index); + wildcard_index = Dev.size(); + DIR* d = opendir(Path.c_str()); + if (d == NULL) { + LOGINFO("Error opening '%s': %s\n", Path.c_str(), strerror(errno)); + return false; + } + struct dirent* de; + while ((de = readdir(d)) != NULL) { + if (de->d_type != DT_BLK || strlen(de->d_name) <= wildcard_index || strncmp(de->d_name, Dev.c_str(), wildcard_index) != 0) + continue; + + string item = Path + "/"; + item.append(de->d_name); + if (PartitionManager.Find_Partition_By_Block_Device(item)) + continue; + TWPartition *part = new TWPartition; + char buffer[MAX_FSTAB_LINE_LENGTH]; + sprintf(buffer, "%s %s-%i auto defaults defaults", item.c_str(), Mount_Point.c_str(), ++mount_point_index); + part->Process_Fstab_Line(buffer, false, NULL); + char display[MAX_FSTAB_LINE_LENGTH]; + sprintf(display, "%s %i", Storage_Name.c_str(), mount_point_index); + part->Storage_Name = display; + part->Display_Name = display; + part->Primary_Block_Device = item; + part->Wildcard_Block_Device = false; + part->Is_SubPartition = true; + part->SubPartition_Of = Mount_Point; + part->Is_Storage = Is_Storage; + part->Can_Be_Mounted = true; + part->Removable = true; + part->Can_Be_Wiped = Can_Be_Wiped; + part->Wipe_Available_in_GUI = Wipe_Available_in_GUI; + part->Find_Actual_Block_Device(); + part->Update_Size(false); + Has_SubPartition = true; + PartitionManager.Output_Partition(part); + PartitionManager.Add_Partition(part); + } + closedir(d); + return (mount_point_index > 0); +} + void TWPartition::Find_Actual_Block_Device(void) { - if (Is_Decrypted && !Decrypted_Block_Device.empty()) { + if (!Sysfs_Entry.empty() && Primary_Block_Device.empty() && Decrypted_Block_Device.empty()) { + /* Sysfs_Entry.empty() indicates if this is a sysfs entry that begins with /device/ + * If we have a syfs entry then we are looking for this device from a uevent add. + * The uevent add will set the primary block device based on the data we receive from + * after checking for adopted storage. If the device ends up being adopted, then the + * decrypted block device will be set instead of the primary block device. */ + Is_Present = false; + return; + } + if (Wildcard_Block_Device && !Is_Adopted_Storage) { + Is_Present = false; + Actual_Block_Device = ""; + Can_Be_Mounted = false; + if (!Find_Wildcard_Block_Devices(Primary_Block_Device)) { + string Dev = Primary_Block_Device.substr(0, Primary_Block_Device.find("*")); + if (TWFunc::Path_Exists(Dev)) { + Is_Present = true; + Can_Be_Mounted = true; + Actual_Block_Device = Dev; + } + } + return; + } else if (Is_Decrypted && !Decrypted_Block_Device.empty()) { Actual_Block_Device = Decrypted_Block_Device; if (TWFunc::Path_Exists(Decrypted_Block_Device)) { Is_Present = true; -- cgit v1.2.3