Files
android_bootable_recovery/crypto/vold_decrypt/vold_decrypt.cpp
bigbiff ad58e1bfae repack: move sbin to /system ramdisk and update repacking
for android-10

This will support updating the ramdisk to a different
compression format and co-exist with magisk.

We are also cleaning up and removing non android-10
api makefile actions.

We are also moving twrp repacking to its own class. We
check the new ramdisk format and if it's different
we have magisk compress using the new ramdisk format.

Change-Id: I770030aae7797e75817178b2f0fccd9f39dc23af
2020-09-08 10:08:33 -04:00

1442 lines
44 KiB
C++
Executable File

/*
Copyright 2017 TeamWin
This file is part of TWRP/TeamWin Recovery Project.
TWRP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TWRP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TWRP. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/time.h>
#include <dirent.h>
#include <fnmatch.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#ifdef _USING_SHORT_SERVICE_NAMES
#include <map>
#endif
#include "../../partitions.hpp"
#include "../../twrp-functions.hpp"
using namespace std;
extern "C" {
#include <cutils/properties.h>
}
#include "vold_decrypt.h"
namespace {
/* Timeouts as defined by ServiceManager */
/* The maximum amount of time to wait for a service to start or stop,
* in micro-seconds (really an approximation) */
#define SLEEP_MAX_USEC 2000000 /* 2 seconds */
/* The minimal sleeping interval between checking for the service's state
* when looping for SLEEP_MAX_USEC */
#define SLEEP_MIN_USEC 200000 /* 200 msec */
/* vold response codes defined in ResponseCode.h */
// 200 series - Requested action has been successfully completed
#define COMMAND_OKAY 200
#define PASSWORD_TYPE_RESULT 213
#define LOGINFO(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]I:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
#define LOGKMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]K:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
#define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
FILE *fp_kmsg = NULL;
int sdkver = 20;
/* Debugging Functions */
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
#ifndef VD_STRACE_BIN
#define VD_STRACE_BIN "/system/bin/strace"
#endif
bool has_strace = false;
pid_t pid_strace = 0;
void Strace_init_Start(void) {
has_strace = TWFunc::Path_Exists(VD_STRACE_BIN);
if (!has_strace) {
LOGINFO("strace binary (%s) not found, disabling strace in vold_decrypt!\n", VD_STRACE_BIN);
return;
}
pid_t pid;
switch(pid = fork())
{
case -1:
LOGKMSG("forking strace_init failed: %d (%s)!\n", errno, strerror(errno));
return;
case 0: // child
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL);
LOGKMSG("strace_init fork failed: %d (%s)!\n", errno, strerror(errno));
exit(-1);
default:
LOGKMSG("Starting strace_init (pid=%d)\n", pid);
pid_strace = pid;
return;
}
}
void Strace_init_Stop(void) {
if (pid_strace > 0) {
LOGKMSG("Stopping strace_init (pid=%d)\n", pid_strace);
int timeout;
int status;
pid_t retpid = waitpid(pid_strace, &status, WNOHANG);
kill(pid_strace, SIGTERM);
for (timeout = 5; retpid == 0 && timeout; --timeout) {
sleep(1);
retpid = waitpid(pid_strace, &status, WNOHANG);
}
if (retpid)
LOGKMSG("strace_init terminated successfully\n");
else {
// SIGTERM didn't work, kill it instead
kill(pid_strace, SIGKILL);
for (timeout = 5; retpid == 0 && timeout; --timeout) {
sleep(1);
retpid = waitpid(pid_strace, &status, WNOHANG);
}
if (retpid)
LOGKMSG("strace_init killed successfully\n");
else
LOGKMSG("strace_init took too long to kill, may be a zombie process\n");
}
}
}
#endif // TW_CRYPTO_SYSTEM_VOLD_DEBUG
/* Convert a binary key of specified length into an ascii hex string equivalent,
* without the leading 0x and with null termination
*
* Original code from cryptfs.c
*/
string convert_key_to_hex_ascii(const string& master_key) {
size_t i;
unsigned char nibble;
string master_key_ascii = "";
for (i = 0; i < master_key.size(); ++i) {
nibble = (master_key[i] >> 4) & 0xf;
nibble += nibble > 9 ? 0x57 : 0x30;
master_key_ascii += nibble;
nibble = master_key[i] & 0xf;
nibble += nibble > 9 ? 0x57 : 0x30;
master_key_ascii += nibble;
}
return master_key_ascii;
}
/* Helper Functions */
#define PATH_EXISTS(path) (access(path, F_OK) >= 0)
int vrename(const string& oldname, const string& newname, bool verbose = false) {
const char *old_name = oldname.c_str();
const char *new_name = newname.c_str();
if (!PATH_EXISTS(old_name))
return 0;
if (rename(old_name, new_name) < 0) {
LOGERROR("Moving %s to %s failed: %d (%s)\n", old_name, new_name, errno, strerror(errno));
return -1;
} else if (verbose)
LOGINFO("Renamed %s to %s\n", old_name, new_name);
else
LOGKMSG("Renamed %s to %s\n", old_name, new_name);
return 0;
}
int vsymlink(const string& oldname, const string& newname, bool verbose = false) {
const char *old_name = oldname.c_str();
const char *new_name = newname.c_str();
if (!PATH_EXISTS(old_name))
return 0;
if (symlink(old_name, new_name) < 0) {
LOGERROR("Symlink %s -> %s failed: %d (%s)\n", new_name, old_name, errno, strerror(errno));
return -1;
} else if (verbose)
LOGINFO("Symlinked %s -> %s\n", new_name, old_name);
else
LOGKMSG("Symlinked %s -> %s\n", new_name, old_name);
return 0;
}
/* Properties and Services Functions */
string Wait_For_Property(const string& property_name, int utimeout = SLEEP_MAX_USEC, const string& expected_value = "not_empty") {
char prop_value[PROPERTY_VALUE_MAX];
if (expected_value == "not_empty") {
while (utimeout > 0) {
property_get(property_name.c_str(), prop_value, "error");
if (strcmp(prop_value, "error") != 0)
break;
LOGKMSG("waiting for %s to get set\n", property_name.c_str());
utimeout -= SLEEP_MIN_USEC;
usleep(SLEEP_MIN_USEC);;
}
}
else {
while (utimeout > 0) {
property_get(property_name.c_str(), prop_value, "error");
if (strcmp(prop_value, expected_value.c_str()) == 0)
break;
LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str());
utimeout -= SLEEP_MIN_USEC;
usleep(SLEEP_MIN_USEC);
}
}
property_get(property_name.c_str(), prop_value, "error");
return prop_value;
}
string Get_Service_State(const string& initrc_svc) {
char prop_value[PROPERTY_VALUE_MAX];
string init_svc = "init.svc." + initrc_svc;
property_get(init_svc.c_str(), prop_value, "error");
return prop_value;
}
bool Service_Exists(const string& initrc_svc) {
return (Get_Service_State(initrc_svc) != "error");
}
bool Is_Service_Running(const string& initrc_svc) {
return (Get_Service_State(initrc_svc) == "running");
}
bool Is_Service_Stopped(const string& initrc_svc) {
return (Get_Service_State(initrc_svc) == "stopped");
}
bool Start_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
string res = "error";
string init_svc = "init.svc." + initrc_svc;
property_set("ctl.start", initrc_svc.c_str());
res = Wait_For_Property(init_svc, utimeout, "running");
LOGINFO("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str());
return (res == "running");
}
bool Stop_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
string res = "error";
if (Service_Exists(initrc_svc)) {
string init_svc = "init.svc." + initrc_svc;
property_set("ctl.stop", initrc_svc.c_str());
res = Wait_For_Property(init_svc, utimeout, "stopped");
LOGINFO("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str());
}
return (res == "stopped");
}
/* Vendor, Firmware and fstab symlink Functions */
bool is_Vendor_Mounted(void) {
static int is_mounted = -1;
if (is_mounted < 0)
is_mounted = PartitionManager.Is_Mounted_By_Path("/vendor") ? 1 : 0;
return is_mounted;
}
bool is_Firmware_Mounted(void) {
static int is_mounted = -1;
if (is_mounted < 0)
is_mounted = PartitionManager.Is_Mounted_By_Path("/firmware") ? 1 : 0;
return is_mounted;
}
bool will_VendorBin_Be_Symlinked(void) {
return (!is_Vendor_Mounted() && TWFunc::Path_Exists("/system/vendor"));
}
bool Symlink_Vendor_Folder(void) {
bool is_vendor_symlinked = false;
if (is_Vendor_Mounted()) {
LOGINFO("vendor partition mounted, skipping /vendor substitution\n");
}
else if (TWFunc::Path_Exists("/system/vendor")) {
LOGINFO("Symlinking vendor folder...\n");
if (!TWFunc::Path_Exists("/vendor") || vrename("/vendor", "/vendor-orig") == 0) {
TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster");
vsymlink("/system/vendor/lib64", "/vendor/lib64", true);
vsymlink("/system/vendor/lib", "/vendor/lib", true);
vsymlink("/system/vendor/bin", "/vendor/bin", true);
is_vendor_symlinked = true;
property_set("vold_decrypt.symlinked_vendor", "1");
}
}
return is_vendor_symlinked;
}
void Restore_Vendor_Folder(void) {
property_set("vold_decrypt.symlinked_vendor", "0");
TWFunc::removeDir("/vendor", false);
vrename("/vendor-orig", "/vendor");
}
bool Symlink_Firmware_Folder(void) {
bool is_firmware_symlinked = false;
if (is_Firmware_Mounted()) {
LOGINFO("firmware partition mounted, skipping /firmware substitution\n");
}
else {
LOGINFO("Symlinking firmware folder...\n");
if (!TWFunc::Path_Exists("/firmware") || vrename("/firmware", "/firmware-orig") == 0) {
TWFunc::Recursive_Mkdir("/firmware/image");
is_firmware_symlinked = true;
property_set("vold_decrypt.symlinked_firmware", "1");
}
}
return is_firmware_symlinked;
}
void Restore_Firmware_Folder(void) {
property_set("vold_decrypt.symlinked_firmware", "0");
TWFunc::removeDir("/firmware", false);
vrename("/firmware-orig", "/firmware");
}
int Find_Firmware_Files(const string& Path, vector<string> *FileList) {
int ret;
DIR* d;
struct dirent* de;
string FileName;
d = opendir(Path.c_str());
if (d == NULL) {
closedir(d);
return -1;
}
while ((de = readdir(d)) != NULL) {
if (de->d_type == DT_DIR) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
FileName = Path + "/" + de->d_name;
ret = Find_Firmware_Files(FileName, FileList);
if (ret < 0)
return -1;
} else if (de->d_type == DT_REG) {
if (fnmatch("keymaste*.*", de->d_name, 0) == 0 || fnmatch("cmnlib.*", de->d_name, 0) == 0) {
FileName = Path + "/" + de->d_name;
FileList->push_back(FileName);
}
}
}
closedir(d);
return 0;
}
void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) {
if (!is_vendor_symlinked && !is_firmware_symlinked)
return;
LOGINFO("Symlinking firmware files...\n");
vector<string> FirmwareFiles;
Find_Firmware_Files("/system", &FirmwareFiles);
for (size_t i = 0; i < FirmwareFiles.size(); ++i) {
string base_name = TWFunc::Get_Filename(FirmwareFiles[i]);
if (is_firmware_symlinked)
vsymlink(FirmwareFiles[i], "/firmware/image/" + base_name);
if (is_vendor_symlinked) {
vsymlink(FirmwareFiles[i], "/vendor/firmware/" + base_name);
vsymlink(FirmwareFiles[i], "/vendor/firmware/keymaster/" + base_name);
}
}
LOGINFO("%d file(s) symlinked.\n", (int)FirmwareFiles.size());
}
// Android 8.0 fs_mgr checks for "/system/bin/recovery", in which case it will
// use /etc/recovery.fstab -> symlink it temporarily. Reference:
// https://android.googlesource.com/platform/system/core/+/android-8.0.0_r17/fs_mgr/fs_mgr_fstab.cpp#693
bool Symlink_Recovery_Fstab(void) {
bool is_fstab_symlinked = false;
if (vrename("/etc/recovery.fstab", "/etc/recovery-fstab-orig") == 0) {
is_fstab_symlinked = true;
// now attempt to symlink to /fstab.{ro.hardware}, but even if that
// fails, keep TWRP's fstab hidden since it cannot be parsed by fs_mgr
char prop_value[PROPERTY_VALUE_MAX];
property_get("ro.hardware", prop_value, "error");
if (strcmp(prop_value, "error")) {
string fstab_device = "/fstab."; fstab_device += prop_value;
vsymlink(fstab_device, "/etc/recovery.fstab");
}
}
return is_fstab_symlinked;
}
void Restore_Recovery_Fstab(void) {
unlink("/etc/recovery.fstab");
vrename("/etc/recovery-fstab-orig", "/etc/recovery.fstab");
}
/* Additional Services Functions */
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
typedef struct {
string Service_Name;
string Service_Path;
string Service_Binary;
string VOLD_Service_Name;
string TWRP_Service_Name;
bool is_running;
bool resume;
bool bin_exists;
bool svc_exists;
} AdditionalService;
typedef struct {
string Service_Name;
string Service_Path;
string Service_Binary;
} RC_Service;
// expand_props() courtesy of platform_system_core_init_util.cpp
bool expand_props(const std::string& src, std::string* dst) {
const char* src_ptr = src.c_str();
if (!dst) {
return false;
}
/* - variables can either be $x.y or ${x.y}, in case they are only part
* of the string.
* - will accept $$ as a literal $.
* - no nested property expansion, i.e. ${foo.${bar}} is not supported,
* bad things will happen
* - ${x.y:-default} will return default value if property empty.
*/
while (*src_ptr) {
const char* c;
c = strchr(src_ptr, '$');
if (!c) {
dst->append(src_ptr);
return true;
}
dst->append(src_ptr, c);
c++;
if (*c == '$') {
dst->push_back(*(c++));
src_ptr = c;
continue;
} else if (*c == '\0') {
return true;
}
std::string prop_name;
std::string def_val;
if (*c == '{') {
c++;
const char* end = strchr(c, '}');
if (!end) {
// failed to find closing brace, abort.
return false;
}
prop_name = std::string(c, end);
c = end + 1;
size_t def = prop_name.find(":-");
if (def < prop_name.size()) {
def_val = prop_name.substr(def + 2);
prop_name = prop_name.substr(0, def);
}
} else {
prop_name = c;
c += prop_name.size();
}
if (prop_name.empty()) {
return false;
}
char prop_value[PROPERTY_VALUE_MAX];
property_get(prop_name.c_str(), prop_value, "");
std::string prop_val = prop_value;
if (prop_val.empty()) {
if (def_val.empty()) {
return false;
}
prop_val = def_val;
}
dst->append(prop_val);
src_ptr = c;
}
return true;
}
string GetArgument(const string& line, size_t argument_number, bool expand_properties) {
size_t beg;
size_t end;
string argument;
beg = line.find_first_not_of(" \t\r");
if (beg == string::npos)
return "";
for (size_t i = 0; i < argument_number; ++i) {
end = line.find_first_of(" \t\r", beg);
if (end == string::npos)
return "";
beg = line.find_first_not_of(" \t\r", end);
if (beg == string::npos)
return "";
}
end = line.find_first_of(" \t\r", beg);
if (end == string::npos)
argument = line.substr(beg);
else
argument = line.substr(beg, end - beg); // exclude trailing whitespace
if (expand_properties) {
string expanded_property_argument;
if (expand_props(argument, &expanded_property_argument))
return expanded_property_argument;
else
return "";
} else {
return argument;
}
}
// Very simplified .rc parser to get services
void Parse_RC_File(const string& rc_file, vector<RC_Service>& RC_Services) {
ifstream file;
file.open(rc_file.c_str(), ios::in);
if (!file.is_open())
return;
size_t beg; // left trim
size_t end; // right trim
bool continuation = false; // backslash continuation
string line; // line
string real_line; // trimmed line with backslash continuation removal
vector<string> imports; // file names of imports (we don't want to recursively do while the file is open)
while (getline(file, line)) {
beg = line.find_first_not_of(" \t\r");
end = line.find_last_not_of(" \t\r");
if (end == string::npos)
end = line.length();
if (beg == string::npos) {
if (continuation)
continuation = false;
else
continue;
} else if (line[end] == '\\') {
continuation = true;
real_line += line.substr(beg, end - beg); // excluding backslash
continue;
} else if (continuation) {
continuation = false;
real_line += line.substr(beg, end - beg + 1);
} else {
real_line = line.substr(beg, end - beg + 1);
}
if (GetArgument(real_line, 0, false) == "import") {
// handle: import <file>
string file_name = GetArgument(real_line, 1, true);
if (file_name.empty()) {
// INVALID IMPORT
} else
imports.push_back(file_name);
} else if (GetArgument(real_line, 0, false) == "service") {
// handle: service <name> <path>
RC_Service svc;
svc.Service_Name = GetArgument(real_line, 1, false);
svc.Service_Path = GetArgument(real_line, 2, true);
if (svc.Service_Name.empty() || svc.Service_Path.empty()) {
// INVALID SERVICE ENTRY
} else {
beg = svc.Service_Path.find_last_of("/");
if (beg == string::npos)
svc.Service_Binary = svc.Service_Path;
else
svc.Service_Binary = svc.Service_Path.substr(beg + 1);
/*
#ifdef _USING_SHORT_SERVICE_NAMES
if (svc.Service_Name.length() > 16) {
LOGERROR("WARNING: Ignoring service %s (-> %s)\n"
" defined in %s is greater than 16 characters and will\n"
" not be able to be run by init on android-7.1 or below!\n",
svc.Service_Name.c_str(), svc.Service_Path.c_str(), rc_file.c_str()
);
}
else
#endif
*/
RC_Services.push_back(svc);
}
}
real_line.clear();
}
file.close();
for (size_t i = 0; i < imports.size(); ++i) {
Parse_RC_File(imports[i], RC_Services);
}
}
vector<AdditionalService> Get_List_Of_Additional_Services(void) {
vector<AdditionalService> services;
// Additional Services needed by vold_decrypt (eg qseecomd, hwservicemanager, etc)
vector<string> service_names = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_SERVICES, " ");
for (size_t i = 0; i < service_names.size(); ++i) {
AdditionalService svc;
svc.Service_Name = service_names[i];
svc.bin_exists = false;
svc.svc_exists = false;
services.push_back(svc);
#ifdef _USING_SHORT_SERVICE_NAMES
// Fallback code for >16 character service names which
// allows for multiple definitions in custom .rc files
if (service_names[i].length() > 12) {
svc.Service_Name = service_names[i].substr(0, 12); // 16-4(prefix)=12
svc.bin_exists = false;
svc.svc_exists = false;
services.push_back(svc);
}
#endif
}
// Read list of all services defined in all .rc files
vector<RC_Service> RC_Services;
Parse_RC_File("/init.rc", RC_Services);
// Cross reference Additional Services against the .rc Services and establish
// availability of the binaries, otherwise disable it to avoid unnecessary
// delays and log spam.
// Also check for duplicate entries between TWRP and vold_decrypt so we can
// stop and restart any conflicting services.
for (size_t i = 0; i < RC_Services.size(); ++i) {
string prefix = RC_Services[i].Service_Name.substr(0, 4);
#ifdef _USING_SHORT_SERVICE_NAMES
map<string,size_t> rc_indeces;
#endif
if (prefix != "sys_" && prefix != "ven_") {
#ifdef _USING_SHORT_SERVICE_NAMES
if (RC_Services[i].Service_Name.length() > 12) {
// save this entry for potential binary name match
rc_indeces.insert(pair<string,size_t>(RC_Services[i].Service_Binary, i));
}
#endif
continue;
}
for (size_t j = 0; j < services.size(); ++j) {
string path = RC_Services[i].Service_Path;
if (prefix == "ven_" && will_VendorBin_Be_Symlinked()) {
path = "/system" + path; // vendor is going to get symlinked to /system/vendor
}
if (RC_Services[i].Service_Name == prefix + services[j].Service_Name) {
services[j].svc_exists = true;
if (!services[j].VOLD_Service_Name.empty() && TWFunc::Path_Exists(path)) {
// Duplicate match, log but use previous definition
LOGERROR("Service %s: VOLD_Service_Name already defined as %s\n", RC_Services[i].Service_Name.c_str(), services[j].VOLD_Service_Name.c_str());
}
else if (TWFunc::Path_Exists(path)) {
services[j].bin_exists = true;
services[j].VOLD_Service_Name = RC_Services[i].Service_Name; // prefix + service_name
services[j].Service_Path = RC_Services[i].Service_Path;
services[j].Service_Binary = RC_Services[i].Service_Binary;
if (Service_Exists(services[j].Service_Name))
services[j].TWRP_Service_Name = services[j].Service_Name;
else if (Service_Exists("system/bin" + services[j].Service_Name))
services[j].TWRP_Service_Name = "system/bin" + services[j].Service_Name;
else
services[j].TWRP_Service_Name.clear();
#ifdef _USING_SHORT_SERVICE_NAMES
if (services[j].TWRP_Service_Name.empty()) {
// Try matching Service_Binary (due to 16 character service_name limit in 7.1 and below)
map<string,size_t>::iterator it = rc_indeces.find(services[j].Service_Binary);
if (it != rc_indeces.end()) {
services[j].TWRP_Service_Name = RC_Services[it->second].Service_Name;
}
}
#endif
}
break;
}
}
}
LOGINFO("List of additional services for vold_decrypt:\n");
for (size_t i = 0; i < services.size(); ++i) {
if (!services[i].svc_exists) {
LOGINFO(" %s: Disabled due to lack of .rc service entry\n", services[i].Service_Name.c_str());
} else if (services[i].bin_exists) {
if (services[i].TWRP_Service_Name.empty()) {
LOGINFO(" %s: Enabled as %s -> %s\n",
services[i].Service_Name.c_str(),
services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str()
);
} else {
LOGINFO(" %s: Enabled as %s -> %s (temporarily replacing TWRP's %s service)\n",
services[i].Service_Name.c_str(),
services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str(),
services[i].TWRP_Service_Name.c_str()
);
}
}
else {
LOGINFO(" %s: Disabled due to lack of matching binary\n", services[i].Service_Name.c_str());
}
}
return services;
}
#endif
/* Misc Functions */
void Set_Needed_Properties(void) {
// vold won't start without ro.storage_structure on Kitkat
string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
if (!sdkverstr.empty()) {
sdkver = atoi(sdkverstr.c_str());
}
if (sdkver <= 19) {
string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure");
if (!ro_storage_structure.empty())
property_set("ro.storage_structure", ro_storage_structure.c_str());
}
property_set("hwservicemanager.ready", "false");
property_set("sys.listeners.registered", "false");
property_set("vendor.sys.listeners.registered", "false");
}
void Update_Patch_Level(void) {
// On Oreo and above, keymaster requires Android version & patch level to match installed system
string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
if (!sdkverstr.empty()) {
sdkver = atoi(sdkverstr.c_str());
}
if (sdkver <= 25) {
property_set("vold_decrypt.legacy_system", "true");
} else {
LOGINFO("Current system is Oreo or above. Setting OS version and security patch level from installed system...\n");
property_set("vold_decrypt.legacy_system", "false");
}
char prop_value[PROPERTY_VALUE_MAX];
char legacy_system_value[PROPERTY_VALUE_MAX] = "false";
property_get("vold_decrypt.legacy_system", prop_value, "");
// Only set OS ver and patch level if device uses Oreo+ system
if (strcmp(prop_value, legacy_system_value) == 0) {
property_get("ro.build.version.release", prop_value, "");
std::string osver_orig = prop_value;
property_set("vold_decrypt.osver_orig", osver_orig.c_str());
LOGINFO("Current OS version: %s\n", osver_orig.c_str());
int error = 0;
std::string osver = TWFunc::System_Property_Get("ro.build.version.release");
if (!(osver == osver_orig)) {
if (!(error = TWFunc::Property_Override("ro.build.version.release", osver))) {
LOGINFO("Property override successful! New OS version: %s\n", osver.c_str());
} else {
LOGERROR("Property override failed, code %d\n", error);
return;
}
// TODO: Confirm whether we actually need to update the props in prop.default
std::string sed_osver = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver + "/g' /prop.default";
TWFunc::Exec_Cmd(sed_osver);
property_set("vold_decrypt.osver_set", "true");
} else {
LOGINFO("Current OS version & System OS version already match. Proceeding to next step.\n");
property_set("vold_decrypt.osver_set", "false");
}
property_get("ro.build.version.security_patch", prop_value, "");
std::string patchlevel_orig = prop_value;
property_set("vold_decrypt.patchlevel_orig", patchlevel_orig.c_str());
LOGINFO("Current security patch level: %s\n", patchlevel_orig.c_str());
std::string patchlevel = TWFunc::System_Property_Get("ro.build.version.security_patch");
if (!(patchlevel == patchlevel_orig)) {
if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel))) {
LOGINFO("Property override successful! New security patch level: %s\n", patchlevel.c_str());
} else {
LOGERROR("Property override failed, code %d\n", error);
return;
}
// TODO: Confirm whether we actually need to update the props in prop.default
std::string sed_patchlevel = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel + "/g' /prop.default";
TWFunc::Exec_Cmd(sed_patchlevel);
property_set("vold_decrypt.patchlevel_set", "true");
} else {
LOGINFO("Current security patch level & System security patch level already match. Proceeding to next step.\n");
property_set("vold_decrypt.patchlevel_set", "false");
}
return;
} else {
LOGINFO("Current system is Nougat or older. Skipping OS version and security patch level setting...\n");
return;
}
}
void Revert_Patch_Level(void) {
char osver_set[PROPERTY_VALUE_MAX];
char patchlevel_set[PROPERTY_VALUE_MAX];
char osver_patchlevel_set[PROPERTY_VALUE_MAX] = "false";
property_get("vold_decrypt.osver_set", osver_set, "");
property_get("vold_decrypt.patchlevel_set", patchlevel_set, "");
int osver_result = strcmp(osver_set, osver_patchlevel_set);
int patchlevel_result = strcmp(patchlevel_set, osver_patchlevel_set);
if (!(osver_result == 0 && patchlevel_result == 0)) {
char prop_value[PROPERTY_VALUE_MAX];
LOGINFO("Reverting OS version and security patch level to original TWRP values...\n");
property_get("vold_decrypt.osver_orig", prop_value, "");
std::string osver_orig = prop_value;
property_get("ro.build.version.release", prop_value, "");
std::string osver = prop_value;
int error = 0;
if (!(osver == osver_orig)) {
if (!(error = TWFunc::Property_Override("ro.build.version.release", osver_orig))) {
LOGINFO("Property override successful! Original OS version: %s\n", osver_orig.c_str());
} else {
LOGERROR("Property override failed, code %d\n", error);
return;
}
// TODO: Confirm whether we actually need to update the props in prop.default
std::string sed_osver_orig = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver_orig + "/g' /prop.default";
TWFunc::Exec_Cmd(sed_osver_orig);
}
property_get("vold_decrypt.patchlevel_orig", prop_value, "");
std::string patchlevel_orig = prop_value;
property_get("ro.build.version.security_patch", prop_value, "");
std::string patchlevel = prop_value;
if (!(patchlevel == patchlevel_orig)) {
if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel_orig))) {
LOGINFO("Property override successful! Original security patch level: %s\n", patchlevel_orig.c_str());
} else {
LOGERROR("Property override failed, code %d\n", error);
return;
}
// TODO: Confirm whether we actually need to update the props in prop.default
std::string sed_patchlevel_orig = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel_orig + "/g' /prop.default";
TWFunc::Exec_Cmd(sed_patchlevel_orig);
}
} else {
return;
}
}
static unsigned int get_blkdev_size(int fd) {
unsigned long nr_sec;
if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
nr_sec = 0;
}
return (unsigned int) nr_sec;
}
#define CRYPT_FOOTER_OFFSET 0x4000
static char footer[16 * 1024];
const char* userdata_path;
static off64_t offset;
int footer_br(const string& command) {
int fd;
if (command == "backup") {
unsigned int nr_sec;
TWPartition* userdata = PartitionManager.Find_Partition_By_Path("/data");
userdata_path = userdata->Actual_Block_Device.c_str();
fd = open(userdata_path, O_RDONLY);
if (fd < 0) {
LOGERROR("E:footer_backup: Cannot open '%s': %s\n", userdata_path, strerror(errno));
return -1;
}
if ((nr_sec = get_blkdev_size(fd))) {
offset = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
} else {
LOGERROR("E:footer_br: Failed to get offset\n");
close(fd);
return -1;
}
if (lseek64(fd, offset, SEEK_SET) == -1) {
LOGERROR("E:footer_backup: Failed to lseek64\n");
close(fd);
return -1;
}
if (read(fd, footer, sizeof(footer)) != sizeof(footer)) {
LOGERROR("E:footer_br: Failed to read: %s\n", strerror(errno));
close(fd);
return -1;
}
close(fd);
} else if (command == "restore") {
fd = open(userdata_path, O_WRONLY);
if (fd < 0) {
LOGERROR("E:footer_restore: Cannot open '%s': %s\n", userdata_path, strerror(errno));
return -1;
}
if (lseek64(fd, offset, SEEK_SET) == -1) {
LOGERROR("E:footer_restore: Failed to lseek64\n");
close(fd);
return -1;
}
if (write(fd, footer, sizeof(footer)) != sizeof(footer)) {
LOGERROR("E:footer_br: Failed to write: %s\n", strerror(errno));
close(fd);
return -1;
}
close(fd);
} else {
LOGERROR("E:footer_br: wrong command argument: %s\n", command.c_str());
return -1;
}
return 0;
}
/* vdc Functions */
typedef struct {
string Output; // Entire line excluding \n
int ResponseCode; // ResponseCode.h (int)
int Sequence; // Sequence (int)
int Message; // Message (string) but we're only interested in int
} vdc_ReturnValues;
int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnValues* vdcResult) {
pid_t pid;
int status;
int pipe_fd[2][2];
vdcResult->Output.clear();
vdcResult->ResponseCode = vdcResult->Sequence = vdcResult->Message = -1;
for (int i = 0; i < 2; ++i) {
if (pipe(pipe_fd[i])) {
LOGERROR("exec_vdc_cryptfs: pipe() error!\n");
return -1;
}
}
// getpwtype and checkpw commands are removed from Pie vdc, using modified vdc_pie
const char *cmd[] = { "/system/bin/vdc_pie", "cryptfs" };
if (sdkver < 28)
cmd[0] = "/system/bin/vdc";
const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL };
LOGINFO("sdkver: %d, using %s\n", sdkver, cmd[0]);
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
string log_name = "/tmp/strace_vdc_" + command;
#endif
switch(pid = fork())
{
case -1:
LOGERROR("exec_vdc_cryptfs: fork failed: %d (%s)!\n", errno, strerror(errno));
return -1;
case 0: // child
fflush(stdout); fflush(stderr);
for (int i = 0; i < 2; ++i) {
close(pipe_fd[i][0]);
dup2(pipe_fd[i][1], ((i == 0) ? STDOUT_FILENO : STDERR_FILENO));
close(pipe_fd[i][1]);
}
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
if (has_strace) {
if (argument.empty())
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
"-E", env[0], cmd[0], cmd[1], command.c_str(), NULL);
else
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
"-E", env[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL);
} else
#endif
if (argument.empty())
execle(cmd[0], cmd[0], cmd[1], command.c_str(), NULL, env);
else
execle(cmd[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL, env);
_exit(127);
break;
default:
{
int timeout = 30*100;
for (int i = 0; i < 2; ++i) {
close(pipe_fd[i][1]);
// Non-blocking read loop with timeout
int flags = fcntl(pipe_fd[i][0], F_GETFL, 0);
fcntl(pipe_fd[i][0], F_SETFL, flags | O_NONBLOCK);
}
char buffer[128];
ssize_t count;
string strout[2];
pid_t retpid = waitpid(pid, &status, WNOHANG);
while (true) {
for (int i = 0; i < 2; ++i) {
count = read(pipe_fd[i][0], buffer, sizeof(buffer));
if (count == -1) {
if (errno == EINTR)
continue;
else if (errno != EAGAIN)
LOGERROR("exec_vdc_cryptfs: read() error %d (%s)\n!", errno, strerror(errno));
} else if (count > 0) {
strout[i].append(buffer, count);
}
}
retpid = waitpid(pid, &status, WNOHANG);
if (retpid == 0 && --timeout)
usleep(10000);
else
break;
};
for (int i = 0; i < 2; ++i) {
close(pipe_fd[i][0]);
}
if (!strout[0].empty()) {
sscanf(strout[0].c_str(), "%d %d %d", &vdcResult->ResponseCode, &vdcResult->Sequence, &vdcResult->Message);
vdcResult->Output = "I:" + strout[0];
}
if (!strout[1].empty()) {
vdcResult->Output += "E:" + strout[1];
}
std::replace(vdcResult->Output.begin(), vdcResult->Output.end(), '\n', '|');
if (!vdcResult->Output.empty() && vdcResult->Output[vdcResult->Output.length() - 1] != '|')
vdcResult->Output += "|";
vdcResult->Output += "RC=" + TWFunc::to_string(WEXITSTATUS(status));
// Error handling
if (retpid == 0 && timeout == 0) {
LOGERROR("exec_vdc_cryptfs: took too long, killing process\n");
kill(pid, SIGKILL);
for (timeout = 5; retpid == 0 && timeout; --timeout) {
sleep(1);
retpid = waitpid(pid, &status, WNOHANG);
}
if (retpid)
LOGINFO("exec_vdc_cryptfs: process killed successfully\n");
else
LOGERROR("exec_vdc_cryptfs: process took too long to kill, may be a zombie process\n");
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
} else if (retpid > 0) {
if (WIFSIGNALED(status)) {
LOGERROR("exec_vdc_cryptfs: process ended with signal: %d\n", WTERMSIG(status)); // Seg fault or some other non-graceful termination
return -1;
}
} else if (retpid < 0) { // no PID returned
if (errno == ECHILD)
LOGINFO("exec_vdc_cryptfs: no child process exist\n");
else {
LOGERROR("exec_vdc_cryptfs: Unexpected error %d (%s)\n", errno, strerror(errno));
return -1;
}
}
if (sdkver >= 28) {
return WEXITSTATUS(status);
}
return 0;
}
}
}
int Run_vdc(const string& Password) {
int res;
struct timeval t1, t2;
vdc_ReturnValues vdcResult;
LOGINFO("About to run vdc...\n");
// Pie vdc communicates with vold directly, no socket so lets not waste time
if (sdkver < 28) {
// Wait for vold connection
gettimeofday(&t1, NULL);
t2 = t1;
while ((t2.tv_sec - t1.tv_sec) < 5) {
// cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default"
res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult);
if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) {
res = 0;
break;
}
LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str());
usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
gettimeofday(&t2, NULL);
}
if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5)
LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str());
else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype
else if (res)
return VD_ERR_FORK_EXECL_ERROR;
else if (vdcResult.ResponseCode != -1)
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
else
return VD_ERR_VDC_FAILED_TO_CONNECT;
}
// Input password from GUI, or default password
res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult);
if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
else if (res)
return VD_ERR_FORK_EXECL_ERROR;
LOGINFO("vdc cryptfs result (passwd): %s\n", vdcResult.Output.c_str());
/*
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
*/
// our vdc returns vold binder op status,
// we care about status.ok() only which is 0
if (sdkver >= 28) {
vdcResult.Message = res;
}
if (vdcResult.Message != 0) {
// try falling back to Lollipop hex passwords
string hexPassword = convert_key_to_hex_ascii(Password);
res = Exec_vdc_cryptfs("checkpw", hexPassword, &vdcResult);
if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
else if (res)
return VD_ERR_FORK_EXECL_ERROR;
LOGINFO("vdc cryptfs result (hex_pw): %s\n", vdcResult.Output.c_str());
/*
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
*/
}
// sdk < 28 vdc's return value is dependant upon source origin, it will either
// return 0 or ResponseCode, so disregard and focus on decryption instead
// our vdc always returns 0 on success
if (vdcResult.Message == 0) {
// Decryption successful wait for crypto blk dev
Wait_For_Property("ro.crypto.fs_crypto_blkdev");
res = VD_SUCCESS;
} else if (vdcResult.ResponseCode != COMMAND_OKAY) {
res = VD_ERR_VOLD_UNEXPECTED_RESPONSE;
} else {
res = VD_ERR_DECRYPTION_FAILED;
}
return res;
}
int Vold_Decrypt_Core(const string& Password) {
int res;
bool is_vendor_symlinked = false;
bool is_firmware_symlinked = false;
bool is_fstab_symlinked = false;
bool is_vold_running = false;
if (Password.empty()) {
LOGINFO("vold_decrypt: password is empty!\n");
return VD_ERR_PASSWORD_EMPTY;
}
// Mount ANDROID_ROOT and check for vold and vdc
if (!PartitionManager.Mount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
return VD_ERR_UNABLE_TO_MOUNT_SYSTEM;
} else if ((!TWFunc::Path_Exists("/system/bin/vold")) && (!TWFunc::Path_Exists(PartitionManager.Get_Android_Root_Path() + "/system/bin/vold"))) {
LOGINFO("ERROR: vold not found, aborting.\n");
return VD_ERR_MISSING_VOLD;
} else if ((!TWFunc::Path_Exists("/system/bin/vdc")) && (!TWFunc::Path_Exists(PartitionManager.Get_Android_Root_Path() + "/system/bin/vdc"))) {
LOGINFO("ERROR: vdc not found, aborting.\n");
return VD_ERR_MISSING_VDC;
}
#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
vector<string> partitions = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_MOUNT, " ");
for (size_t i = 0; i < partitions.size(); ++i) {
string mnt_point = "/" + partitions[i];
if(PartitionManager.Find_Partition_By_Path(mnt_point)) {
if (!PartitionManager.Mount_By_Path(mnt_point, true)) {
LOGERROR("Unable to mount %s\n", mnt_point.c_str());
return VD_ERR_UNABLE_TO_MOUNT_EXTRA;
}
LOGINFO("%s partition mounted\n", partitions[i].c_str());
}
}
#endif
fp_kmsg = fopen("/dev/kmsg", "a");
LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n");
// just cache the result to avoid unneeded duplicates in recovery.log
LOGINFO("Checking existence of vendor and firmware partitions...\n");
is_Vendor_Mounted();
is_Firmware_Mounted();
LOGINFO("Attempting to use system's vold for decryption...\n");
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
Strace_init_Start();
#endif
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
vector<AdditionalService> Services = Get_List_Of_Additional_Services();
// Check if TWRP is running any of the services
for (size_t i = 0; i < Services.size(); ++i) {
if (!Services[i].TWRP_Service_Name.empty() && !Is_Service_Stopped(Services[i].TWRP_Service_Name)) {
Services[i].resume = true;
Stop_Service(Services[i].TWRP_Service_Name);
} else
Services[i].resume = false;
}
#endif
LOGINFO("Setting up folders and permissions...\n");
is_fstab_symlinked = Symlink_Recovery_Fstab();
is_vendor_symlinked = Symlink_Vendor_Folder();
is_firmware_symlinked = Symlink_Firmware_Folder();
Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked);
Set_Needed_Properties();
#ifdef TW_INCLUDE_LIBRESETPROP
Update_Patch_Level();
#endif
// Start services needed for vold decrypt
LOGINFO("Starting services...\n");
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
for (size_t i = 0; i < Services.size(); ++i) {
if (Services[i].bin_exists) {
if (Services[i].Service_Binary.find("keymaster") != string::npos) {
Wait_For_Property("hwservicemanager.ready", 500000, "true");
LOGINFO(" hwservicemanager is ready.\n");
}
Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name);
if (Services[i].Service_Binary == "qseecomd") {
if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
LOGINFO(" qseecomd listeners registered.\n");
}
}
}
#endif
is_vold_running = Start_Service("sys_vold");
if (is_vold_running) {
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
for (size_t i = 0; i < Services.size(); ++i) {
if (Services[i].bin_exists && !Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume) {
// if system_service has died restart the twrp_service
LOGINFO("%s is not running, resuming %s!\n", Services[i].VOLD_Service_Name.c_str(), Services[i].TWRP_Service_Name.c_str());
Start_Service(Services[i].TWRP_Service_Name);
}
}
#endif
/*
* Oreo and Pie vold on some devices alters footer causing
* system to ask for decryption password at next boot although
* password haven't changed so we save footer before and restore it
* after vold operations
*/
if (sdkver > 25) {
if (footer_br("backup") == 0) {
LOGINFO("footer_br: crypto footer backed up\n");
res = Run_vdc(Password);
if (footer_br("restore") == 0)
LOGINFO("footer_br: crypto footer restored\n");
else
LOGERROR("footer_br: Failed to restore crypto footer\n");
} else {
LOGERROR("footer_br: Failed to backup crypto footer, \
skipping decrypt to prevent data loss. Reboot recovery to try again...\n");
res = -1;
}
} else {
res = Run_vdc(Password);
}
if (res != 0) {
LOGINFO("Decryption failed\n");
}
} else {
LOGINFO("Failed to start vold\n");
res = VD_ERR_VOLD_FAILED_TO_START;
}
#ifdef TW_INCLUDE_LIBRESETPROP
Revert_Patch_Level();
#endif
// Stop services needed for vold decrypt so /system can be unmounted
LOGINFO("Stopping services...\n");
Stop_Service("sys_vold");
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
for (size_t i = 0; i < Services.size(); ++i) {
if (!Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume)
Stop_Service(Services[i].TWRP_Service_Name);
else if (Services[i].bin_exists)
Stop_Service(Services[i].VOLD_Service_Name);
}
#endif
if (is_firmware_symlinked)
Restore_Firmware_Folder();
if (is_vendor_symlinked)
Restore_Vendor_Folder();
if (is_fstab_symlinked)
Restore_Recovery_Fstab();
if (!PartitionManager.UnMount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
// PartitionManager failed to unmount ANDROID_ROOT, this should not happen,
// but in case it does, do a lazy unmount
LOGINFO("WARNING: '%s' could not be unmounted normally!\n", PartitionManager.Get_Android_Root_Path().c_str());
umount2(PartitionManager.Get_Android_Root_Path().c_str(), MNT_DETACH);
}
#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
for (size_t i = 0; i < partitions.size(); ++i) {
string mnt_point = "/" + partitions[i];
if(PartitionManager.Is_Mounted_By_Path(mnt_point)) {
if (!PartitionManager.UnMount_By_Path(mnt_point, true)) {
LOGINFO("WARNING: %s partition could not be unmounted normally!\n", partitions[i].c_str());
umount2(mnt_point.c_str(), MNT_DETACH);
}
}
}
#endif
LOGINFO("Finished.\n");
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
Set_Needed_Properties();
// Restart previously running services
for (size_t i = 0; i < Services.size(); ++i) {
if (Services[i].resume) {
if (Services[i].Service_Binary.find("keymaster") != string::npos) {
Wait_For_Property("hwservicemanager.ready", 500000, "true");
LOGINFO(" hwservicemanager is ready.\n");
}
Start_Service(Services[i].TWRP_Service_Name);
if (Services[i].Service_Binary == "qseecomd") {
if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
LOGINFO(" qseecomd listeners registered.\n");
}
}
}
#endif
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
Strace_init_Stop();
#endif
// Finish up and exit
if (fp_kmsg) {
fflush(fp_kmsg);
fclose(fp_kmsg);
}
return res;
}
} // namespace
/*
* Common vold Response Codes / Errors:
* 406 (OpFailedStorageNotFound) -> Problem reading or parsing fstab
*
*/
/* Main function separated from core in case we want to return error info */
int vold_decrypt(const string& Password) {
return Vold_Decrypt_Core(Password);
}