* HTC U11 Oreo is using keymaster3 FDE encryption which requires
the new services:
1- /system/bin/hwservicemanager
2- /vendor/bin/hw/android.hardware.keymaster@3.0-service
3- /vendor/bin/qseecomd (instead of /system/bin/qseecomd)
So in addition to /vendor/lib and /vendor/lib64 also
symlink /system/vendor/bin to /vendor/bin.
* vold_decrypt services now have separate prefixes:
1- 'sys_' referring to /system/bin
2- 'ven_' referring to /vendor/bin
* The additional (hwservicemanager, keymaster-3-0) and modified
(qseecomd) .rc files have been updated in the vold_decrypt
directory.
Comments were added directly in the .rc files, please check
them.
* /etc/recovery.fstab needs to be temporarily moved since
vold will use it if it finds the '/sbin/recovery' file
(refer to fs_mgr for the fstab load code https://goo.gl/8KaZyf).
Since fs_mgr cannot parse TWRP style fstab, we 'hide' it
and attempt to create a symlink to /fstab.{ro.hardware}.
Also remove shell dependencies, code cleanup, new error codes:
* Critical sections of vold_decrypt should not rely on the external
shell (and the available binaries) provided by TWFunc::Exec_Cmd.
Doing so may lead to failures resulting from different shell
provided binaries not working properly, especially since busybox
can be inconsistent across different trees.
In particular the following functions have been changed:
* run_vdc() no longer uses daisy chained commands, instead
it now forks and executes vdc directly including a 30 second
built in timeout.
* Symlink_Firmware_Files() no longer relies on the shell 'find'
command to retrieve the list of firmware/vendor files and instead
uses a built in function, Find_Firmware_Files(), which traverses
the system partition to retrieve the list of files.
* The code has also been cleaned up a little for better consistency,
and vold_decrypt will now return various error codes for the
different failures, as defined in vold_decrypt.h, which allows the
gui_msg to be moved back to partitionmanager.cpp.
Notes regarding pre Android 8.0 builds:
* Service names in .rc files cannot exceed 16 characters (including
the prepended 'sys_' or 'ven_') in Android 7.1 and below, so a
service name such as 'sys_hwservicemanager' is out of the question
for 7.1 and below.
* hwservicemanager will check ACLs on 'hwservicemanager' and 'ITokenManager'
if they are even allowed to run, otherwise the interfaces will fail.
The policies have only been introduced in 8.0, and although it is possible
to manually add them to the 7.1 policies it's not recommended.
* Therefore the best course of action is to build in 8.0.
* SIDE NOTE: On the HTC U11 we are actually using omni-7.1 with some changes
in the device tree to support both Nougat and Oreo decryption, please
refer to:
1- https://gerrit.twrp.me/c/2756/ for the necessary sepolicy and
BoardConfig changes.
2- The Android.mk file for vold_decrypt was modified to truncate
greater than 16 character service names (as mentioned therein)
Other changes:
* TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT is now deprecated due to built-
in fork and timeout.
* Output_dmesg_to_recovery_log() is also deprecated so upon a failed
decryption the recovery.log will no longer append it, instead you can
just use 'adb shell dmesg' to check it. Nonetheless if a true debug
build is needed use the original TW_CRYPTO_SYSTEM_VOLD_DEBUG flag as
outlined in the original commit message (see below).
Usage info:
This is an update to the initial vold_decrypt, for more info refer to
71c6c50d0d
Change-Id: Id7129d125ae7f5dcba0779489825add718022ba3
1127 lines
32 KiB
C++
1127 lines
32 KiB
C++
/*
|
|
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;
|
|
|
|
|
|
/* Debugging Functions */
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
|
|
#ifndef VD_STRACE_BIN
|
|
#define VD_STRACE_BIN "/sbin/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");
|
|
vsymlink("/system/vendor/lib", "/vendor/lib");
|
|
vsymlink("/system/vendor/bin", "/vendor/bin");
|
|
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 "/sbin/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 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.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.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) {
|
|
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].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("sbin" + services[j].Service_Name))
|
|
services[j].TWRP_Service_Name = "sbin" + 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].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");
|
|
int sdkver = 20;
|
|
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());
|
|
}
|
|
}
|
|
|
|
|
|
/* 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];
|
|
vdcResult->Output.clear();
|
|
|
|
if (pipe(pipe_fd)) {
|
|
LOGERROR("exec_vdc_cryptfs: pipe() error!\n");
|
|
return -1;
|
|
}
|
|
|
|
const char *cmd[] = { "/system/bin/vdc", "cryptfs" };
|
|
const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL };
|
|
|
|
#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);
|
|
close(pipe_fd[0]);
|
|
dup2(pipe_fd[1], STDOUT_FILENO);
|
|
dup2(pipe_fd[1], STDERR_FILENO);
|
|
close(pipe_fd[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;
|
|
vdcResult->Output.clear();
|
|
close(pipe_fd[1]);
|
|
|
|
// Non-blocking read loop with timeout
|
|
int flags = fcntl(pipe_fd[0], F_GETFL, 0);
|
|
fcntl(pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
|
|
|
|
char buffer[128];
|
|
ssize_t count;
|
|
pid_t retpid = waitpid(pid, &status, WNOHANG);
|
|
while (true) {
|
|
count = read(pipe_fd[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) {
|
|
vdcResult->Output.append(buffer, count);
|
|
}
|
|
|
|
retpid = waitpid(pid, &status, WNOHANG);
|
|
if (retpid == 0 && --timeout)
|
|
usleep(10000);
|
|
else
|
|
break;
|
|
};
|
|
close(pipe_fd[0]);
|
|
if (vdcResult->Output.length() > 0 && vdcResult->Output[vdcResult->Output.length() - 1] == '\n')
|
|
vdcResult->Output.erase(vdcResult->Output.length() - 1);
|
|
vdcResult->ResponseCode = vdcResult->Sequence = vdcResult->Message = -1;
|
|
sscanf(vdcResult->Output.c_str(), "%d %d %d", &vdcResult->ResponseCode, &vdcResult->Sequence, &vdcResult->Message);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Run_vdc(const string& Password) {
|
|
int res;
|
|
struct timeval t1, t2;
|
|
vdc_ReturnValues vdcResult;
|
|
|
|
LOGINFO("About to run vdc...\n");
|
|
|
|
// 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) {
|
|
char str_res[4 + sizeof(int) + 1];
|
|
snprintf(str_res, sizeof(str_res), "ret=%d", res);
|
|
vdcResult.Output += str_res;
|
|
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 (ret=%d)\n", vdcResult.Output.c_str(), res);
|
|
/*
|
|
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
|
|
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
*/
|
|
|
|
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 (ret=%d)\n", vdcResult.Output.c_str(), res);
|
|
/*
|
|
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
|
|
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
*/
|
|
}
|
|
|
|
// vdc's return value is dependant upon source origin, it will either
|
|
// return 0 or ResponseCode, so disregard and focus on decryption instead
|
|
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 system and check for vold and vdc
|
|
if (!PartitionManager.Mount_By_Path("/system", true)) {
|
|
return VD_ERR_UNABLE_TO_MOUNT_SYSTEM;
|
|
} else if (!TWFunc::Path_Exists("/system/bin/vold")) {
|
|
LOGINFO("ERROR: /system/bin/vold not found, aborting.\n");
|
|
return VD_ERR_MISSING_VOLD;
|
|
} else if (!TWFunc::Path_Exists("/system/bin/vdc")) {
|
|
LOGINFO("ERROR: /system/bin/vdc not found, aborting.\n");
|
|
return VD_ERR_MISSING_VDC;
|
|
}
|
|
|
|
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();
|
|
|
|
// 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].exists)
|
|
Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name);
|
|
}
|
|
#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].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
|
|
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;
|
|
}
|
|
|
|
// 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].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("/system", true)) {
|
|
// PartitionManager failed to unmount /system, this should not happen,
|
|
// but in case it does, do a lazy unmount
|
|
LOGINFO("WARNING: system could not be unmounted normally!\n");
|
|
umount2("/system", MNT_DETACH);
|
|
}
|
|
|
|
LOGINFO("Finished.\n");
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
// Restart previously running services
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (Services[i].resume)
|
|
Start_Service(Services[i].TWRP_Service_Name);
|
|
}
|
|
#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);
|
|
}
|