Commit 6e8c27a5 authored by Ethan Yonker's avatar Ethan Yonker Committed by Dees Troy
Browse files

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
parent adcb4d8c
/*update
/*
Copyright 2013 bigbiff/Dees_Troy TeamWin
This file is part of TWRP/TeamWin Recovery Project.
......
......@@ -33,7 +33,6 @@
#include <sys/mount.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
extern "C"
{
......@@ -79,6 +78,8 @@ int gGuiRunning = 0;
int g_pty_fd = -1; // set by terminal on init
void terminal_pty_read();
int select_fd = 0;
static int gRecorder = -1;
extern "C" void gr_write_frame_to_file(int fd);
......@@ -395,9 +396,18 @@ void InputHandler::handleDrag()
}
}
void set_select_fd() {
select_fd = ors_read_fd + 1;
if (g_pty_fd >= select_fd)
select_fd = g_pty_fd + 1;
if (PartitionManager.uevent_pfd.fd >= select_fd)
select_fd = PartitionManager.uevent_pfd.fd + 1;
}
static void setup_ors_command()
{
ors_read_fd = -1;
set_select_fd();
unlink(ORS_INPUT_FILE);
if (mkfifo(ORS_INPUT_FILE, 06660) != 0) {
......@@ -417,6 +427,7 @@ static void setup_ors_command()
unlink(ORS_INPUT_FILE);
unlink(ORS_OUTPUT_FILE);
}
set_select_fd();
}
// callback called after a CLI command was executed
......@@ -448,6 +459,7 @@ static void ors_command_read()
if (!orsout) {
close(ors_read_fd);
ors_read_fd = -1;
set_select_fd();
LOGINFO("Unable to fopen %s\n", ORS_OUTPUT_FILE);
unlink(ORS_INPUT_FILE);
unlink(ORS_OUTPUT_FILE);
......@@ -554,29 +566,30 @@ static int runPages(const char *page_name, const int stop_on_page_done)
for (;;)
{
loopTimer(input_timeout_ms);
FD_ZERO(&fdset);
timeout.tv_sec = 0;
timeout.tv_usec = 1;
if (g_pty_fd > 0) {
// TODO: this is not nice, we should have one central select for input, pty, and ors
FD_ZERO(&fdset);
FD_SET(g_pty_fd, &fdset);
timeout.tv_sec = 0;
timeout.tv_usec = 1;
has_data = select(g_pty_fd+1, &fdset, NULL, NULL, &timeout);
if (has_data > 0) {
terminal_pty_read();
}
}
if (PartitionManager.uevent_pfd.fd > 0) {
FD_SET(PartitionManager.uevent_pfd.fd, &fdset);
}
#ifndef TW_OEM_BUILD
if (ors_read_fd > 0 && !orsout) { // orsout is non-NULL if a command is still running
FD_ZERO(&fdset);
FD_SET(ors_read_fd, &fdset);
timeout.tv_sec = 0;
timeout.tv_usec = 1;
has_data = select(ors_read_fd+1, &fdset, NULL, NULL, &timeout);
if (has_data > 0) {
ors_command_read();
}
}
#endif
// TODO: combine this select with the poll done by input handling
has_data = select(select_fd, &fdset, NULL, NULL, &timeout);
if (has_data > 0) {
if (g_pty_fd > 0 && FD_ISSET(g_pty_fd, &fdset))
terminal_pty_read();
if (PartitionManager.uevent_pfd.fd > 0 && FD_ISSET(PartitionManager.uevent_pfd.fd, &fdset))
PartitionManager.read_uevent();
if (ors_read_fd > 0 && !orsout && FD_ISSET(ors_read_fd, &fdset))
ors_command_read();
}
if (!gForceRender.get_value())
{
......@@ -636,6 +649,7 @@ static int runPages(const char *page_name, const int stop_on_page_done)
if (ors_read_fd > 0)
close(ors_read_fd);
ors_read_fd = -1;
set_select_fd();
gGuiRunning = 0;
return 0;
}
......
......@@ -21,6 +21,8 @@
#include "twmsg.h"
void set_select_fd();
void gui_msg(const char* text);
void gui_warn(const char* text);
void gui_err(const char* text);
......
......@@ -34,6 +34,7 @@ extern "C" {
#include "../twcommon.h"
}
#include "../minuitwrp/minui.h"
#include "gui.hpp"
#include "rapidxml.hpp"
#include "objects.hpp"
......@@ -83,6 +84,7 @@ public:
// and write it to the terminal
// this currently works through gui.cpp calling terminal_pty_read below
g_pty_fd = fdMaster;
set_select_fd();
return true;
}
else {
......@@ -174,6 +176,7 @@ public:
}
close(fdMaster);
g_pty_fd = fdMaster = -1;
set_select_fd();
int status;
waitpid(pid, &status, WNOHANG); // avoid zombies but don't hang if the child is still alive and we got here due to some error
pid = 0;
......
......@@ -30,6 +30,7 @@
<string name="sdext">SD-EXT</string>
<string name="adopted_data">Adopted Data</string>
<string name="adopted_storage">Adopted Storage</string>
<string name="autostorage">Storage</string>
<!-- GUI XML strings -->
<string name="twrp_header">Team Win Recovery Project</string>
......
......@@ -25,8 +25,8 @@
struct mtpmsg {
int message_type; // 1 is add, 2 is remove, see above
unsigned int storage_id;
const char* display;
const char* path;
char display[1024];
char path[1024];
uint64_t maxFileSize;
};
......
......@@ -170,7 +170,7 @@ int twmtp_MtpServer::mtppipe_thread(void)
if (mtp_message.storage_id) {
long reserveSpace = 1;
bool removable = false;
MtpStorage* storage = new MtpStorage(mtp_message.storage_id, mtp_message.path, mtp_message.display, reserveSpace, removable, mtp_message.maxFileSize, refserver);
MtpStorage* storage = new MtpStorage(mtp_message.storage_id, &mtp_message.path[0], &mtp_message.display[0], reserveSpace, removable, mtp_message.maxFileSize, refserver);
server->addStorage(storage);
MTPD("mtppipe done adding storage\n");
} else {
......
......@@ -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<string, Flags_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<string, Flags_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<partition_fs_flags_struct>::iterator iter;
std::vector<partition_fs_flags_struct>::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());