android_mt6572_jiabo/bootable/recovery/voldclient.cpp
2025-09-05 16:56:03 +08:00

466 lines
12 KiB
C++

/*
* Copyright (c) 2013 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <string>
#include <sstream>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include "common.h"
#include "roots.h"
#include "voldclient.h"
#include "VolumeBase.h"
#include "ResponseCode.h"
using namespace android::vold;
VoldClient* vdc = NULL;
static void* threadfunc(void* arg)
{
VoldClient* self = (VoldClient*)arg;
self->run();
return NULL;
}
VoldClient::VoldClient(VoldWatcher* watcher /* = nullptr */) :
mRunning(false),
mSock(-1),
mSockMutex(PTHREAD_MUTEX_INITIALIZER),
mSockCond(PTHREAD_COND_INITIALIZER),
mInFlight(0),
mResult(0),
mWatcher(watcher),
mVolumeLock(PTHREAD_RWLOCK_INITIALIZER),
mVolumeChanged(false),
mEmulatedStorage(true)
{
}
void VoldClient::start(void)
{
mRunning = true;
pthread_create(&mThread, NULL, threadfunc, this);
while (mSock == -1) {
sleep(1);
}
while (mInFlight != 0) {
sleep(1);
}
LOGI("VoldClient initialized, storage is %s\n",
vdc->isEmulatedStorage() ? "emulated" : "physical");
}
void VoldClient::stop(void)
{
if (mRunning) {
mRunning = false;
close(mSock);
mSock = -1;
void* retval;
pthread_join(mThread, &retval);
}
}
VolumeInfo VoldClient::getVolume(const std::string& id)
{
pthread_rwlock_wrlock(&mVolumeLock);
VolumeInfo* info = getVolumeLocked(id);
pthread_rwlock_unlock(&mVolumeLock);
return *info;
}
bool VoldClient::reset(void)
{
const char *cmd[2] = { "volume", "reset" };
return sendCommand(2, cmd);
}
bool VoldClient::mountAll(void)
{
bool ret = true;
pthread_rwlock_rdlock(&mVolumeLock);
for (auto& info : mVolumes) {
if (info.mState == (int)VolumeBase::State::kUnmounted) {
if (!volumeMount(info.mId)) {
ret = false;
}
}
}
pthread_rwlock_unlock(&mVolumeLock);
return ret;
}
bool VoldClient::unmountAll(void)
{
bool ret = true;
pthread_rwlock_rdlock(&mVolumeLock);
for (auto& info : mVolumes) {
if (info.mState == (int)VolumeBase::State::kMounted) {
if (!volumeUnmount(info.mId)) {
ret = false;
}
}
}
pthread_rwlock_unlock(&mVolumeLock);
return ret;
}
bool VoldClient::volumeMount(const std::string& id)
{
// Special case for emulated storage
if (id == "emulated") {
pthread_rwlock_wrlock(&mVolumeLock);
VolumeInfo* info = getVolumeLocked(id);
if (!info) {
pthread_rwlock_unlock(&mVolumeLock);
return false;
}
info->mPath = "/storage/emulated";
info->mInternalPath = "/data/media";
pthread_rwlock_unlock(&mVolumeLock);
return ensure_path_mounted("/data") == 0;
}
const char *cmd[3] = { "volume", "mount", id.c_str() };
return sendCommand(3, cmd);
}
// NB: can only force or detach, not both
bool VoldClient::volumeUnmount(const std::string& id, bool detach /* = false */)
{
// Special case for emulated storage
if (id == "emulated") {
if (ensure_path_unmounted("/data", detach) != 0) {
return false;
}
return true;
}
const char *cmd[4] = { "volume", "unmount", id.c_str(), NULL };
int cmdlen = 3;
if (detach) {
cmd[3] = "detach";
cmdlen = 4;
}
return sendCommand(cmdlen, cmd);
}
bool VoldClient::volumeFormat(const std::string& id)
{
const char* cmd[3] = { "volume", "format", id.c_str() };
return sendCommand(3, cmd);
}
void VoldClient::resetVolumeState(void)
{
pthread_rwlock_wrlock(&mVolumeLock);
mVolumes.clear();
mVolumeChanged = false;
mEmulatedStorage = true;
pthread_rwlock_unlock(&mVolumeLock);
if (mWatcher) {
mWatcher->onVolumeChanged();
}
const char *cmd[2] = { "volume", "reset" };
sendCommand(2, cmd, false);
}
VolumeInfo* VoldClient::getVolumeLocked(const std::string& id)
{
for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
if (iter->mId == id) {
return &(*iter);
}
}
return nullptr;
}
bool VoldClient::sendCommand(unsigned int len, const char** command, bool wait /* = true */)
{
char line[4096];
char* p;
unsigned int i;
size_t sz;
bool ret = true;
p = line;
p += sprintf(p, "0 "); /* 0 is a (now required) sequence number */
for (i = 0; i < len; i++) {
const char* cmd = command[i];
if (!cmd[0] || !strchr(cmd, ' '))
p += sprintf(p, "%s", cmd);
else
p += sprintf(p, "\"%s\"", cmd);
if (i < len - 1)
*p++ = ' ';
if (p >= line + sizeof(line)) {
LOGE("vold command line too long\n");
exit(1);
}
}
// only one writer at a time
pthread_mutex_lock(&mSockMutex);
if (write(mSock, line, (p - line) + 1) < 0) {
LOGE("Unable to send command to vold!\n");
pthread_mutex_unlock(&mSockMutex);
return false;
}
++mInFlight;
if (wait) {
while (mInFlight) {
// wait for completion
pthread_cond_wait(&mSockCond, &mSockMutex);
}
ret = (mResult >= 200 && mResult < 300);
}
pthread_mutex_unlock(&mSockMutex);
return ret;
}
void VoldClient::handleCommandOkay(void)
{
bool changed = false;
pthread_rwlock_wrlock(&mVolumeLock);
if (mVolumeChanged) {
mVolumeChanged = false;
changed = true;
}
pthread_rwlock_unlock(&mVolumeLock);
if (changed) {
mWatcher->onVolumeChanged();
}
}
void VoldClient::handleVolumeCreated(const std::string& id, const std::string& type,
const std::string& disk, const std::string& guid)
{
pthread_rwlock_wrlock(&mVolumeLock);
// Ignore emulated storage if primary storage is physical
if (id == "emulated") {
char value[PROPERTY_VALUE_MAX];
property_get("ro.vold.primary_physical", value, "0");
if (value[0] == '1' || value[0] == 'y' || !strcmp(value, "true")) {
mEmulatedStorage = false;
return;
}
mEmulatedStorage = true;
}
VolumeInfo info;
info.mId = id;
mVolumes.push_back(info);
pthread_rwlock_unlock(&mVolumeLock);
}
void VoldClient::handleVolumeStateChanged(const std::string& id, const std::string& state)
{
pthread_rwlock_wrlock(&mVolumeLock);
auto info = getVolumeLocked(id);
if (info) {
info->mState = atoi(state.c_str());
}
pthread_rwlock_unlock(&mVolumeLock);
}
void VoldClient::handleVolumeFsLabelChanged(const std::string& id, const std::string& label)
{
pthread_rwlock_wrlock(&mVolumeLock);
auto info = getVolumeLocked(id);
if (info) {
info->mLabel = label;
}
pthread_rwlock_unlock(&mVolumeLock);
}
void VoldClient::handleVolumePathChanged(const std::string& id, const std::string& path)
{
pthread_rwlock_wrlock(&mVolumeLock);
auto info = getVolumeLocked(id);
if (info) {
info->mPath = path;
}
pthread_rwlock_unlock(&mVolumeLock);
}
void VoldClient::handleVolumeInternalPathChanged(const std::string& id, const std::string& path)
{
pthread_rwlock_wrlock(&mVolumeLock);
auto info = getVolumeLocked(id);
if (info) {
info->mInternalPath = path;
}
pthread_rwlock_unlock(&mVolumeLock);
}
void VoldClient::handleVolumeDestroyed(const std::string& id)
{
pthread_rwlock_wrlock(&mVolumeLock);
for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
if (iter->mId == id) {
mVolumes.erase(iter);
break;
}
}
pthread_rwlock_unlock(&mVolumeLock);
}
static std::vector<std::string> split(const std::string& line)
{
std::vector<std::string> tokens;
const char* tok = line.c_str();
while (*tok) {
unsigned int toklen;
const char* next;
if (*tok == '"') {
++tok;
const char* q = strchr(tok, '"');
if (!q) {
LOGE("vold line <%s> malformed\n", line.c_str());
exit(1);
}
toklen = q - tok;
next = q + 1;
if (*next) {
if (*next != ' ') {
LOGE("vold line <%s> malformed\n", line.c_str());
exit(0);
}
++next;
}
}
else {
next = strchr(tok, ' ');
if (next) {
toklen = next - tok;
++next;
}
else {
toklen = strlen(tok);
next = tok + toklen;
}
}
tokens.push_back(std::string(tok, toklen));
tok = next;
}
return tokens;
}
void VoldClient::dispatch(const std::string& line)
{
std::vector<std::string> tokens = split(line);
switch (mResult) {
case ResponseCode::CommandOkay:
handleCommandOkay();
break;
case ResponseCode::VolumeCreated:
handleVolumeCreated(tokens[1], tokens[2], tokens[3], tokens[4]);
break;
case ResponseCode::VolumeStateChanged:
handleVolumeStateChanged(tokens[1], tokens[2]);
break;
case ResponseCode::VolumeFsLabelChanged:
handleVolumeFsLabelChanged(tokens[1], tokens[2]);
break;
case ResponseCode::VolumePathChanged:
handleVolumePathChanged(tokens[1], tokens[2]);
break;
case ResponseCode::VolumeInternalPathChanged:
handleVolumeInternalPathChanged(tokens[1], tokens[2]);
break;
case ResponseCode::VolumeDestroyed:
handleVolumeDestroyed(tokens[1]);
break;
}
}
void VoldClient::run(void)
{
LOGI("VoldClient thread starting\n");
while (mRunning) {
if (mSock == -1) {
LOGI("Connecting to Vold...\n");
mSock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
if (mSock == -1) {
sleep(1);
continue;
}
resetVolumeState();
}
int rc;
struct timeval tv;
fd_set rfds;
memset(&tv, 0, sizeof(tv));
tv.tv_usec = 100 * 1000;
FD_ZERO(&rfds);
FD_SET(mSock, &rfds);
rc = select(mSock + 1, &rfds, NULL, NULL, &tv);
if (rc <= 0) {
if (rc < 0 && errno != EINTR) {
LOGE("vdc: error in select (%s)\n", strerror(errno));
close(mSock);
mSock = -1;
}
continue;
}
char buf[4096];
memset(buf, 0, sizeof(buf));
rc = read(mSock, buf, sizeof(buf) - 1);
if (rc <= 0) {
LOGE("vdc: read failed: %s\n", (rc == 0 ? "EOF" : strerror(errno)));
close(mSock);
mSock = -1;
continue;
}
// dispatch each line of the response
int nread = rc;
int off = 0;
while (off < nread) {
char* eol = (char*)memchr(buf + off, 0, nread - off);
if (!eol) {
break;
}
mResult = atoi(buf + off);
dispatch(std::string(buf + off));
if (mResult >= 200 && mResult < 600) {
pthread_mutex_lock(&mSockMutex);
--mInFlight;
pthread_cond_signal(&mSockCond);
pthread_mutex_unlock(&mSockMutex);
}
off = (eol - buf) + 1;
}
}
}