* Copyright (C) 2021 Huawei Device Co., Ltd.
* 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 "transfer.h"
#include "serial_struct.h"
#include <sys/stat.h>
#ifdef HARMONY_PROJECT
#include <lz4.h>
#endif
#if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
#include <selinux/selinux.h>
#endif
#include <memory>
#ifdef HDC_SUPPORT_REPORT_COMMAND_EVENT
#include "command_event_report.h"
#endif
#include "memory_pool.h"
namespace Hdc {
constexpr uint64_t HDC_TIME_CONVERT_BASE = 1000000000;
HdcTransferBase::HdcTransferBase(HTaskInfo hTaskInfo)
: HdcTaskBase(hTaskInfo)
{
ResetCtx(&ctxNow, true);
commandBegin = 0;
commandData = 0;
isStableBuf = false;
}
HdcTransferBase::~HdcTransferBase()
{
if (ctxNow.isFdOpen) {
WRITE_LOG(LOG_DEBUG, "~HdcTransferBase channelId:%u lastErrno:%u result:%d ioFinish:%d",
taskInfo->channelId, ctxNow.lastErrno, ctxNow.openFd, ctxNow.ioFinish);
if (ctxNow.lastErrno != 0 || (ctxNow.openFd > 0 && !ctxNow.ioFinish)) {
CloseCtxFd(&ctxNow);
}
} else {
WRITE_LOG(LOG_DEBUG, "~HdcTransferBase channelId:%u lastErrno:%u ioFinish:%d",
taskInfo->channelId, ctxNow.lastErrno, ctxNow.ioFinish);
}
};
bool HdcTransferBase::ResetCtx(CtxFile *context, bool full)
{
if (full) {
*context = {};
context->thisClass = this;
context->loop = loopTask;
context->cb = OnFileIO;
}
context->closeNotify = false;
context->indexIO = 0;
context->lastErrno = 0;
context->ioFinish = false;
context->closeReqSubmitted = false;
context->openFd = -1;
return true;
}
int HdcTransferBase::SimpleFileIO(CtxFile *context, uint64_t index, uint8_t *sendBuf, int bytes)
{
StartTraceScope("HdcTransferBase::SimpleFileIO");
std::string sessionIdMaskStr = Hdc::MaskSessionIdToString(taskInfo->sessionId);
#ifndef CONFIG_USE_JEMALLOC_DFX_INIF
uint8_t *buf = cirbuf.Malloc();
#else
uint8_t *buf = new uint8_t[bytes + payloadPrefixReserve]();
#endif
if (buf == nullptr) {
WRITE_LOG(LOG_FATAL, "SimpleFileIO buf nullptr cid:%u sid:%s", taskInfo->channelId, sessionIdMaskStr.c_str());
return -1;
}
CtxFileIO *ioContext = static_cast<CtxFileIO*>(MemoryPool::Instance().Allocate(sizeof(CtxFileIO)));
if (ioContext == nullptr) {
#ifndef CONFIG_USE_JEMALLOC_DFX_INIF
cirbuf.Free(buf);
#else
delete[] buf;
#endif
WRITE_LOG(LOG_FATAL, "SimpleFileIO ioContext nullptr cid:%u sid:%s",
taskInfo->channelId, sessionIdMaskStr.c_str());
return -1;
}
bool ret = false;
while (true) {
size_t bufMaxSize = context->isStableBufSize ?
static_cast<size_t>(Base::GetUsbffsBulkSizeStable() - payloadPrefixReserve) :
static_cast<size_t>(Base::GetUsbffsBulkSize() - payloadPrefixReserve);
if (bytes < 0 || static_cast<size_t>(bytes) > bufMaxSize) {
WRITE_LOG(LOG_DEBUG, "SimpleFileIO param check failed cid:%u sid:%s", taskInfo->channelId,
sessionIdMaskStr.c_str());
break;
}
if (context->ioFinish) {
WRITE_LOG(LOG_DEBUG, "SimpleFileIO to closed IOStream cid:%u sid:%s", taskInfo->channelId,
sessionIdMaskStr.c_str());
break;
}
uv_fs_t *req = &ioContext->fs;
ioContext->bytes = static_cast<uint64_t>(bytes);
ioContext->bufIO = buf + payloadPrefixReserve;
ioContext->context = context;
req->data = ioContext;
if (context->master) {
uv_buf_t iov = uv_buf_init(reinterpret_cast<char *>(ioContext->bufIO), bytes);
int rc = uv_fs_read(context->loop, req, context->openFd, &iov, 1, index, context->cb);
if (rc < 0) {
WRITE_LOG(LOG_DEBUG, "uv_fs_read failed rc:%d", rc);
break;
} else {
ioContext = nullptr;
++refCount;
}
} else {
if (bytes > 0 && memcpy_s(ioContext->bufIO, ioContext->bytes, sendBuf, bytes) != EOK) {
WRITE_LOG(LOG_WARN, "SimpleFileIO memcpy error cid:%u sid:%s", taskInfo->channelId,
sessionIdMaskStr.c_str());
break;
}
uv_buf_t iov = uv_buf_init(reinterpret_cast<char *>(ioContext->bufIO), bytes);
int rc = uv_fs_write(context->loop, req, context->openFd, &iov, 1, index, context->cb);
if (rc < 0) {
WRITE_LOG(LOG_DEBUG, "uv_fs_write failed rc:%d", rc);
break;
} else {
ioContext = nullptr;
++refCount;
}
}
ret = true;
break;
}
if (!ret) {
if (ioContext != nullptr) {
MemoryPool::Instance().Deallocate(ioContext);
ioContext = nullptr;
WRITE_LOG(LOG_WARN, "SimpleFileIO ret=false, delete context, cid:%u sid:%s", taskInfo->channelId,
sessionIdMaskStr.c_str());
}
#ifndef CONFIG_USE_JEMALLOC_DFX_INIF
cirbuf.Free(buf);
#else
delete[] buf;
#endif
return -1;
}
return bytes;
}
void HdcTransferBase::OnFileClose(CtxFile *context)
{
StartTraceScope("HdcTransferBase::OnFileClose");
context->closeReqSubmitted = false;
HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
WRITE_LOG(LOG_DEBUG, "OnFileClose channelId:%u closeNotify:%d",
thisClass->taskInfo->channelId, context->closeNotify);
if (context->closeNotify) {
thisClass->WhenTransferFinish(context);
}
--thisClass->refCount;
return;
}
void HdcTransferBase::SetFileTime(CtxFile *context)
{
if (!context->transferConfig.holdTimestamp) {
return;
}
if (!context->transferConfig.mtime) {
return;
}
uv_fs_t fs;
double aTimeSec = static_cast<long double>(context->transferConfig.atime) / HDC_TIME_CONVERT_BASE;
double mTimeSec = static_cast<long double>(context->transferConfig.mtime) / HDC_TIME_CONVERT_BASE;
uv_fs_futime(nullptr, &fs, context->openFd, aTimeSec, mTimeSec, nullptr);
uv_fs_req_cleanup(&fs);
}
bool HdcTransferBase::InitTransferPayload(TransferPayload &payloadHead, uint64_t index, uint8_t compressType,
uint32_t dataSize)
{
payloadHead.compressType = compressType;
payloadHead.uncompressSize = dataSize;
payloadHead.index = index;
return true;
}
bool HdcTransferBase::SendIOPayload(CtxFile *context, uint64_t index, uint8_t *data, int dataSize)
{
TransferPayload payloadHead;
string head;
int compressSize = 0;
int sendBufSize = payloadPrefixReserve + dataSize;
uint8_t *sendBuf = data - payloadPrefixReserve;
bool ret = false;
StartTraceScope("HdcTransferBase::SendIOPayload");
InitTransferPayload(payloadHead, index, context->transferConfig.compressType, dataSize);
if (dataSize > 0) {
switch (payloadHead.compressType) {
#ifdef HARMONY_PROJECT
case COMPRESS_LZ4: {
sendBuf = new uint8_t[sendBufSize]();
if (!sendBuf) {
WRITE_LOG(LOG_FATAL, "alloc LZ4 buffer failed");
return false;
}
compressSize = LZ4_compress_default((const char *)data, (char *)sendBuf + payloadPrefixReserve,
dataSize, dataSize);
if (compressSize <= 0) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "LZ4 compress failed, path: %s compress none",
context->localPath.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "LZ4 compress failed, path: %s compress none",
Hdc::MaskString(context->remotePath).c_str());
}
delete[] sendBuf;
payloadHead.compressType = COMPRESS_NONE;
compressSize = dataSize;
sendBuf = data - payloadPrefixReserve;
}
break;
}
#endif
default: {
compressSize = dataSize;
break;
}
}
}
payloadHead.compressSize = static_cast<uint32_t>(compressSize);
head = SerialStruct::SerializeToString(payloadHead);
if (head.size() + 1 > payloadPrefixReserve ||
EOK != memcpy_s(sendBuf, sendBufSize, head.c_str(), head.size() + 1)) {
WRITE_LOG(LOG_WARN, "fail, head size:%d, reserve:%d", head.size(), payloadPrefixReserve);
goto out;
}
ret = SendToAnother(commandData, sendBuf, payloadPrefixReserve + compressSize) > 0;
out:
if (dataSize > 0 && payloadHead.compressType == COMPRESS_LZ4) {
delete[] sendBuf;
}
return ret;
}
void HdcTransferBase::ProcressFileIOFinish(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
{
++thisClass->refCount;
if (req->fs_type == UV_FS_WRITE && context->isFdOpen) {
uv_fs_t req = {};
uv_fs_fsync(nullptr, &req, context->openFd, nullptr);
uv_fs_req_cleanup(&req);
}
WRITE_LOG(LOG_DEBUG, "channelId:%u result:%d, closeReqSubmitted:%d, context->isFdOpen %d",
thisClass->taskInfo->channelId, context->openFd, context->closeReqSubmitted, context->isFdOpen);
CloseCtxFd(context);
if (context->lastErrno == 0 && !context->closeReqSubmitted) {
context->closeReqSubmitted = true;
WRITE_LOG(LOG_DEBUG, "OnFileIO fs_close, channelId:%u", thisClass->taskInfo->channelId);
OnFileClose(context);
} else {
thisClass->WhenTransferFinish(context);
--thisClass->refCount;
}
}
bool HdcTransferBase::ProcressFileIOWrite(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
{
DEBUG_LOG("write file data %" PRIu64 "/%" PRIu64 "", context->indexIO, context->fileSize);
if (context->indexIO >= context->fileSize || req->result == 0) {
context->closeNotify = true;
thisClass->SetFileTime(context);
return true;
}
return false;
}
bool HdcTransferBase::ProcressFileIORead(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
{
CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
uint8_t *bufIO = contextIO->bufIO;
DEBUG_LOG("read file data %" PRIu64 "/%" PRIu64 "", context->indexIO, context->fileSize);
if (!thisClass->SendIOPayload(context, context->indexIO - req->result, bufIO, req->result)) {
WRITE_LOG(LOG_WARN, "OnFileIO SendIOPayload fail.");
return true;
}
if (req->result == 0) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "path:%s fd:%d eof", context->localPath.c_str(), context->openFd);
} else {
WRITE_LOG(LOG_DEBUG, "path:%s fd:%d eof", Hdc::MaskString(context->remotePath).c_str(),
context->openFd);
}
return true;
}
if (context->indexIO < context->fileSize) {
thisClass->SimpleFileIO(context, context->indexIO, nullptr, context->isStableBufSize ?
(Base::GetMaxBufSizeStable() * thisClass->maxTransferBufFactor) :
(Base::GetMaxBufSize() * thisClass->maxTransferBufFactor));
return false;
}
return true;
}
bool HdcTransferBase::ProcressFileIO(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass,
uint64_t bytes)
{
if (context->ioFinish) {
WRITE_LOG(LOG_DEBUG, "OnFileIO finish is true.");
return true;
}
if (!ProcressFileIOIsSuccess(req, context, bytes)) {
context->closeNotify = true;
if (!context->master) {
uint8_t payload = 0;
thisClass->CommandDispatch(CMD_FILE_FINISH, &payload, sizeof(payload));
}
return true;
}
context->indexIO += static_cast<uint64_t>(req->result);
if (req->fs_type == UV_FS_READ) {
return ProcressFileIORead(req, context, thisClass);
}
if (req->fs_type == UV_FS_WRITE) {
return ProcressFileIOWrite(req, context, thisClass);
}
return true;
}
bool HdcTransferBase::ProcressFileIOIsSuccess(uv_fs_t *req, CtxFile *context, uint64_t bytes)
{
if (req->result < 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r((int)req->result, buf, bufSize);
WRITE_LOG(LOG_DEBUG, "OnFileIO error: %s", buf);
context->lastErrno = static_cast<uint32_t>(std::abs(req->result));
return false;
}
if (context->master) {
return true;
}
if (bytes == static_cast<uint64_t>(req->result)) {
return true;
}
WRITE_LOG(LOG_WARN, "OnFileIO read bytes:%llu not equal to req result:%d", bytes, req->result);
uv_fs_t fs = {};
int ret = uv_fs_statfs(nullptr, &fs, context->localPath.c_str(), nullptr);
if (ret < 0 || (!fs.ptr)) {
constexpr int size = 1024;
char buf[size] = { 0 };
uv_strerror_r(ret, buf, size);
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_WARN, "CheckSpace error querying filesystem: %s, path: %s",
buf, context->localPath.c_str());
} else {
WRITE_LOG(LOG_WARN, "CheckSpace error querying filesystem: %s, path: %s",
buf, Hdc::MaskString(context->remotePath).c_str());
}
uv_fs_req_cleanup(&fs);
context->lastErrno = static_cast<uint32_t>(abs(ret));
return false;
}
uv_statfs_t* statfs = static_cast<uv_statfs_t*>(fs.ptr);
uint64_t freeBytes = statfs->f_bsize * statfs->f_bfree;
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "CheckSpace, path: %s, freeBytes: %llu",
context->localPath.c_str(), freeBytes);
} else {
WRITE_LOG(LOG_DEBUG, "CheckSpace, path: %s, freeBytes: %llu",
Hdc::MaskString(context->remotePath).c_str(), freeBytes);
}
uv_fs_req_cleanup(&fs);
context->lastErrno = static_cast<uint32_t>((freeBytes == 0) ? ENOSPC : EIO);
return false;
}
bool HdcTransferBase::IODelayed(uv_fs_t *req)
{
#ifndef HDC_HOST
return false;
#endif
if (req->fs_type != UV_FS_READ) {
return false;
}
CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
CtxFile *context = reinterpret_cast<CtxFile *>(contextIO->context);
HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
if (thisClass->taskInfo->channelTask) {
const int maxPackages = 64;
const int onePackageTransferTime = 2;
const int delayPackages = maxPackages / 2;
const int delayMs = delayPackages * onePackageTransferTime;
HdcChannelBase *channelBase = reinterpret_cast<HdcChannelBase *>(thisClass->taskInfo->channelClass);
if (channelBase->queuedPackages.load() >= maxPackages) {
WRITE_LOG(LOG_DEBUG, "queued packages:%d is full", channelBase->queuedPackages.load());
Base::DelayDo(req->loop, delayMs, 0, "ChannelFull", req,
[](const uint8_t flag, string &msg, const void *data) {
uv_fs_t *req = (uv_fs_t *)data;
OnFileIO(req);
});
return true;
}
channelBase->queuedPackages.fetch_add(1, std::memory_order_relaxed);
}
return false;
}
void HdcTransferBase::OnFileIO(uv_fs_t *req)
{
StartTraceScope("HdcTransferBase::OnFileIO");
if (IODelayed(req)) {
return;
}
CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
CtxFile *context = reinterpret_cast<CtxFile *>(contextIO->context);
HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
CALLSTAT_GUARD(*(thisClass->loopTaskStatus), req->loop, "HdcTransferBase::OnFileIO");
uint8_t *bufIO = contextIO->bufIO;
uv_fs_req_cleanup(req);
context->ioFinish = ProcressFileIO(req, context, thisClass, contextIO->bytes);
if (context->ioFinish) {
ProcressFileIOFinish(req, context, thisClass);
}
#ifndef CONFIG_USE_JEMALLOC_DFX_INIF
thisClass->cirbuf.Free(bufIO - payloadPrefixReserve);
#else
delete [] (bufIO - payloadPrefixReserve);
#endif
--thisClass->refCount;
MemoryPool::Instance().Deallocate(contextIO);
}
void HdcTransferBase::OnFileOpenFailed(CtxFile *context)
{
HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
if (context->isDir && context->master) {
uint8_t payload = 1;
thisClass->CommandDispatch(CMD_FILE_FINISH, &payload, 1);
} else if (context->isDir && !context->master) {
uint8_t payload = 1;
thisClass->SendToAnother(CMD_FILE_FINISH, &payload, 1);
} else {
thisClass->TaskFinish();
}
return;
}
bool HdcTransferBase::IsValidBundlePath(const string &bundleName)
{
string fullPath = SANDBOX_ROOT_DIR + bundleName + Base::GetPathSep();
return Base::CheckBundleName(bundleName) && access(fullPath.c_str(), F_OK) == 0;
}
void HdcTransferBase::RemoveSandboxRootPath(std::string &srcStr, const std::string &bundleName)
{
if (taskInfo->serverOrDaemon || !IsValidBundlePath(bundleName)) {
return;
}
string fullPath = SANDBOX_ROOT_DIR + bundleName + Base::GetPathSep();
if ((srcStr.size() >= fullPath.size()) && (srcStr.compare(0, fullPath.size(), fullPath) == 0)) {
srcStr = srcStr.substr(fullPath.size());
} else {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "fullPath:%s, srcStr:%s", fullPath.c_str(), srcStr.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "fullPath:%s, srcStr:%s", Hdc::MaskString(fullPath).c_str(),
Hdc::MaskString(srcStr).c_str());
}
}
}
void HdcTransferBase::OnFileOpen(uv_fs_t *req)
{
StartTraceScope("HdcTransferBase::OnFileOpen");
std::unique_ptr<uv_fs_t> uptrReq(req);
CtxFile *context = (CtxFile *)req->data;
HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
CALLSTAT_GUARD(*(thisClass->loopTaskStatus), req->loop, "HdcTransferBase::OnFileOpen");
uv_fs_req_cleanup(req);
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "Filemod openfile:%s channelId:%u result:%d",
context->localPath.c_str(), thisClass->taskInfo->channelId, req->result);
} else {
WRITE_LOG(LOG_DEBUG, "Filemod openfile:%s channelId:%u result:%d",
Hdc::MaskString(context->remotePath).c_str(), thisClass->taskInfo->channelId, req->result);
}
--thisClass->refCount;
if (req->result <= 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r((int)req->result, buf, bufSize);
string localPath = context->localPath;
thisClass->RemoveSandboxRootPath(localPath, context->bundleName);
thisClass->LogMsg(MSG_FAIL, "Error opening file: %s, path:%s", buf,
localPath.c_str());
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "open path:%s, localPath:%s, error:%s, dir:%d, master:%d",
context->localPath.c_str(),
localPath.c_str(), buf, context->isDir, context->master);
} else {
WRITE_LOG(LOG_FATAL, "open path:%s, localPath:%s, error:%s, dir:%d, master:%d",
Hdc::MaskString(context->remotePath).c_str(),
Hdc::MaskString(localPath).c_str(), buf, context->isDir, context->master);
}
OnFileOpenFailed(context);
return;
}
thisClass->ResetCtx(context);
context->isFdOpen = true;
context->openFd = req->result;
if (context->master) {
uv_fs_t fs = {};
uv_fs_fstat(nullptr, &fs, context->openFd, nullptr);
WRITE_LOG(LOG_DEBUG, "uv_fs_fstat result:%d fileSize:%llu",
context->openFd, fs.statbuf.st_size);
TransferConfig &st = context->transferConfig;
st.fileSize = fs.statbuf.st_size;
st.optionalName = context->localName;
if (st.holdTimestamp) {
st.atime = static_cast<uint64_t>(fs.statbuf.st_atim.tv_sec * HDC_TIME_CONVERT_BASE
+ fs.statbuf.st_atim.tv_nsec);
st.mtime = static_cast<uint64_t>(fs.statbuf.st_mtim.tv_sec * HDC_TIME_CONVERT_BASE
+ fs.statbuf.st_mtim.tv_nsec);
}
st.path = context->remotePath;
context->fileSize = st.fileSize;
context->fileMode.perm = fs.statbuf.st_mode;
context->fileMode.uId = fs.statbuf.st_uid;
context->fileMode.gId = fs.statbuf.st_gid;
#if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
char *con = nullptr;
int getfileconRet = getfilecon(context->localPath.c_str(), &con);
if (getfileconRet < 0) {
WRITE_LOG(LOG_WARN, "getfilecon failed, ret:%d, errno:%d", getfileconRet, errno);
} else if (con != nullptr) {
context->fileMode.context = con;
freecon(con);
}
#endif
uv_fs_req_cleanup(&fs);
thisClass->CheckMaster(context);
} else {
if (context->fileModeSync) {
FileMode &mode = context->fileMode;
uv_fs_t fs = {};
uv_fs_chmod(nullptr, &fs, context->localPath.c_str(), mode.perm, nullptr);
uv_fs_chown(nullptr, &fs, context->localPath.c_str(), mode.uId, mode.gId, nullptr);
uv_fs_req_cleanup(&fs);
#if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
if (!mode.context.empty()) {
WRITE_LOG(LOG_DEBUG, "setfilecon from master = %s", mode.context.c_str());
setfilecon(context->localPath.c_str(), mode.context.c_str());
}
#endif
}
union FeatureFlagsUnion f{};
if (!thisClass->AddFeatures(f)) {
WRITE_LOG(LOG_FATAL, "AddFeatureFlag failed");
thisClass->SendToAnother(thisClass->commandBegin, nullptr, 0);
} else {
thisClass->SendToAnother(thisClass->commandBegin, f.raw, sizeof(f));
}
}
}
bool HdcTransferBase::MatchPackageExtendName(string fileName, string extName)
{
bool match = false;
size_t subfixIndex = fileName.rfind(extName);
if (subfixIndex == string::npos) {
return false;
}
if ((fileName.size() - subfixIndex) != extName.size()) {
return false;
}
match = true;
return match;
}
int HdcTransferBase::GetSubFiles(const char *path, string filter, vector<string> *out)
{
int retNum = 0;
uv_fs_t req = {};
uv_dirent_t dent;
vector<string> filterStrings;
if (!strlen(path)) {
return retNum;
}
if (filter.size()) {
Base::SplitString(filter, ";", filterStrings);
}
if (uv_fs_scandir(nullptr, &req, path, 0, nullptr) < 0) {
uv_fs_req_cleanup(&req);
return retNum;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
if (strcmp(dent.name, ".") == 0 || strcmp(dent.name, "..") == 0) {
continue;
}
if (!(static_cast<uint32_t>(dent.type) & UV_DIRENT_FILE)) {
continue;
}
string fileName = dent.name;
for (auto &&s : filterStrings) {
size_t subfixIndex = fileName.rfind(s);
if ((fileName.size() - subfixIndex) != s.size())
continue;
string fullPath = string(path) + Base::GetPathSep();
fullPath += fileName;
out->push_back(fullPath);
++retNum;
}
}
uv_fs_req_cleanup(&req);
return retNum;
}
int HdcTransferBase::GetSubFilesRecursively(string path, string currentDirname, vector<string> *out)
{
int retNum = 0;
uv_fs_t req = {};
uv_dirent_t dent;
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "GetSubFiles path = %s currentDirname = %s", path.c_str(), currentDirname.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "GetSubFiles path = %s currentDirname = %s",
Hdc::MaskString(path).c_str(), Hdc::MaskString(currentDirname).c_str());
}
if (!path.size()) {
return retNum;
}
if (uv_fs_scandir(nullptr, &req, path.c_str(), 0, nullptr) < 0) {
uv_fs_req_cleanup(&req);
return retNum;
}
uv_fs_t fs = {};
int ret = uv_fs_stat(nullptr, &fs, path.c_str(), nullptr);
if (ret == 0) {
FileMode mode;
mode.fullName = currentDirname;
mode.perm = fs.statbuf.st_mode;
mode.uId = fs.statbuf.st_uid;
mode.gId = fs.statbuf.st_gid;
#if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
char *con = nullptr;
getfilecon(path.c_str(), &con);
if (con != nullptr) {
mode.context = con;
freecon(con);
}
#endif
ctxNow.dirMode.push_back(mode);
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
if (strcmp(dent.name, ".") == 0 || strcmp(dent.name, "..") == 0) {
continue;
}
if (!(static_cast<uint32_t>(dent.type) & UV_DIRENT_FILE)) {
WRITE_LOG(LOG_DEBUG, "subdir dent.name fileName = %s", dent.name);
GetSubFilesRecursively(path + Base::GetPathSep() + dent.name,
currentDirname + Base::GetPathSep() + dent.name, out);
continue;
}
string fileName = dent.name;
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "GetSubFiles fileName = %s", fileName.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "GetSubFiles fileName = %s", Hdc::MaskString(fileName).c_str());
}
out->push_back(currentDirname + Base::GetPathSep() + fileName);
}
uv_fs_req_cleanup(&req);
return retNum;
}
bool HdcTransferBase::CheckLocalPath(string &localPath, string &optName, string &errStr)
{
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "CheckDirectory localPath = %s optName = %s",
localPath.c_str(), optName.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "CheckDirectory localPath = %s optName = %s",
Hdc::MaskString(localPath).c_str(), Hdc::MaskString(optName).c_str());
}
if ((optName.find('/') == string::npos) && (optName.find('\\') == string::npos)) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "Not directory mode optName = %s, return", optName.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "Not directory mode optName = %s, return",
Hdc::MaskString(optName).c_str());
}
return true;
}
ctxNow.isDir = true;
uv_fs_t req;
int r = uv_fs_lstat(nullptr, &req, localPath.c_str(), nullptr);
mode_t mode = req.statbuf.st_mode;
uv_fs_req_cleanup(&req);
if (r) {
vector<string> dirsOflocalPath;
string split(1, Base::GetPathSep());
Base::SplitString(localPath, split, dirsOflocalPath);
if (dirsOflocalPath.size() > 0 && !ctxNow.isDir && localPath.back() != Base::GetPathSep()) {
dirsOflocalPath.pop_back();
}
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "localPath = %s dir layers = %zu", localPath.c_str(), dirsOflocalPath.size());
} else {
WRITE_LOG(LOG_DEBUG, "localPath = %s dir layers = %zu",
Hdc::MaskString(localPath).c_str(), dirsOflocalPath.size());
}
string makedirPath;
if (!Base::IsAbsolutePath(localPath)) {
makedirPath = ".";
}
for (auto dir : dirsOflocalPath) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "CheckLocalPath create dir = %s", dir.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "CheckLocalPath create dir = %s", Hdc::MaskString(dir).c_str());
}
if (dir == ".") {
continue;
} else {
#ifdef _WIN32
if (dir.find(":") == 1) {
makedirPath = dir;
continue;
}
#endif
makedirPath = makedirPath + Base::GetPathSep() + dir;
if (!Base::TryCreateDirectory(makedirPath, errStr)) {
return false;
}
}
}
ctxNow.targetDirNotExist = true;
} else if (ctxNow.isDir && !(mode & S_IFDIR)) {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_WARN, "Not a directory, path:%s", localPath.c_str());
} else {
WRITE_LOG(LOG_WARN, "Not a directory, path:%s", Hdc::MaskString(localPath).c_str());
}
errStr = "Not a directory, path:" + localPath;
return false;
}
return true;
}
bool HdcTransferBase::CheckFilename(string &localPath, string &optName, string &errStr)
{
string localPathBackup = localPath;
if (ctxNow.targetDirNotExist) {
if (optName.find('/') != string::npos) {
optName = optName.substr(optName.find('/') + 1);
} else if (optName.find('\\') != string::npos) {
optName = optName.substr(optName.find('\\') + 1);
}
}
vector<string> dirsOfOptName;
if (optName.find('/') != string::npos) {
Base::SplitString(optName, "/", dirsOfOptName);
} else if (optName.find('\\') != string::npos) {
Base::SplitString(optName, "\\", dirsOfOptName);
} else {
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "No need create dir for file = %s", optName.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "No need create dir for file = %s", Hdc::MaskString(optName).c_str());
}
return true;
}
optName = dirsOfOptName.back();
dirsOfOptName.pop_back();
for (auto s : dirsOfOptName) {
localPath = localPath + Base::GetPathSep() + s;
if (!Base::TryCreateDirectory(localPath, errStr)) {
return false;
}
if (ctxNow.fileModeSync) {
string resolvedPath = Base::CanonicalizeSpecPath(localPath);
auto pos = resolvedPath.find(localPathBackup);
if (pos == 0) {
string shortPath = resolvedPath.substr(localPathBackup.size());
if (shortPath.at(0) == Base::GetPathSep()) {
shortPath = shortPath.substr(1);
}
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "pos = %zu, shortPath = %s", pos, shortPath.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "pos = %zu, shortPath = %s", pos, Hdc::MaskString(shortPath).c_str());
}
auto it = ctxNow.dirModeMap.find(shortPath);
if (it != ctxNow.dirModeMap.end()) {
auto mode = it->second;
uv_fs_t fs = {};
uv_fs_chmod(nullptr, &fs, localPath.c_str(), mode.perm, nullptr);
uv_fs_chown(nullptr, &fs, localPath.c_str(), mode.uId, mode.gId, nullptr);
uv_fs_req_cleanup(&fs);
#if (!(defined(HOST_MINGW) || defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
if (!mode.context.empty()) {
WRITE_LOG(LOG_DEBUG, "setfilecon from master = %s", mode.context.c_str());
setfilecon(localPath.c_str(), mode.context.c_str());
}
#endif
}
}
}
}
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "CheckFilename finish localPath:%s optName:%s",
localPath.c_str(), optName.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "CheckFilename finish localPath:%s optName:%s",
Hdc::MaskString(localPath).c_str(), optName.c_str());
}
return true;
}
bool HdcTransferBase::SmartSlavePath(string &cwd, string &localPath, const char *optName)
{
string errStr;
if (taskInfo->serverOrDaemon) {
ExtractRelativePath(cwd, localPath);
}
mode_t mode = mode_t(~S_IFMT);
if (Base::CheckDirectoryOrPath(localPath.c_str(), true, false, errStr, mode)) {
WRITE_LOG(LOG_DEBUG, "%s", errStr.c_str());
return true;
}
uv_fs_t req;
int r = uv_fs_lstat(nullptr, &req, localPath.c_str(), nullptr);
uv_fs_req_cleanup(&req);
if (r == 0 && (req.statbuf.st_mode & S_IFDIR)) {
localPath = localPath + Base::GetPathSep() + optName;
}
if (r != 0 && (localPath.back() == Base::GetPathSep())) {
localPath = localPath + optName;
}
return false;
}
bool HdcTransferBase::RecvIOPayload(CtxFile *context, uint8_t *data, int dataSize)
{
if (dataSize < static_cast<int>(payloadPrefixReserve)) {
WRITE_LOG(LOG_WARN, "unable to parse TransferPayload: invalid dataSize %d", dataSize);
return false;
}
uint8_t *clearBuf = nullptr;
string serialString(reinterpret_cast<char *>(data), payloadPrefixReserve);
TransferPayload pld;
(void)memset_s(&pld, sizeof(TransferPayload), 0, sizeof(TransferPayload));
bool ret = false;
SerialStruct::ParseFromString(pld, serialString);
int clearSize = 0;
StartTraceScope("HdcTransferBase::RecvIOPayload");
if ((pld.compressSize > (static_cast<uint32_t>(dataSize) - payloadPrefixReserve)) ||
pld.uncompressSize > MAX_SIZE_IOBUF) {
WRITE_LOG(LOG_FATAL, "compress size is greater than the dataSize. pld.compressSize = %d", pld.compressSize);
return false;
}
bool isClearBufAllocated = false;
if (pld.compressSize > 0) {
switch (pld.compressType) {
#ifdef HARMONY_PROJECT
case COMPRESS_LZ4: {
clearBuf = new uint8_t[pld.uncompressSize]();
if (!clearBuf) {
WRITE_LOG(LOG_FATAL, "alloc LZ4 buffer failed");
return false;
}
isClearBufAllocated = true;
clearSize = LZ4_decompress_safe((const char *)data + payloadPrefixReserve, (char *)clearBuf,
pld.compressSize, pld.uncompressSize);
break;
}
#endif
default: {
clearBuf = data + payloadPrefixReserve;
clearSize = static_cast<int>(pld.compressSize);
break;
}
}
}
while (true) {
if (clearSize < 0) {
WRITE_LOG(LOG_WARN, "invalid data size for fileIO: %d", clearSize);
break;
}
if (static_cast<uint32_t>(clearSize) != pld.uncompressSize) {
WRITE_LOG(LOG_WARN, "invalid data size for fileIO: %d", clearSize);
break;
}
if (pld.compressType == COMPRESS_NONE && dataSize - payloadPrefixReserve < clearSize) {
WRITE_LOG(LOG_WARN, "not enough data size for fileIO dataSize: %d, clearSize: %d",
dataSize - payloadPrefixReserve, clearSize);
break;
}
if (SimpleFileIO(context, pld.index, clearBuf, clearSize) < 0) {
WRITE_LOG(LOG_WARN, "RecvIOPayload SimpleFileIO fail.");
break;
}
ret = true;
break;
}
if (isClearBufAllocated && (clearBuf != nullptr)) {
delete[] clearBuf;
}
return ret;
}
bool ReportFileCommandEvent(const std::string &localPath, bool master, bool serverOrDaemon)
{
#ifdef HDC_SUPPORT_REPORT_COMMAND_EVENT
if (!DelayedSingleton<CommandEventReport>::GetInstance()->ReportFileCommandEvent(
localPath, master, serverOrDaemon)) {
WRITE_LOG(LOG_FATAL,
"[E00C002]Execution intercepted due to inaccessibility of reporting command event.");
return false;
}
#endif
return true;
}
bool HdcTransferBase::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
{
StartTraceScope("HdcTransferBase::CommandDispatch");
bool ret = true;
while (true) {
if (command == commandBegin) {
CtxFile *context = &ctxNow;
if (!CheckFeatures(context, payload, payloadSize)) {
WRITE_LOG(LOG_FATAL, "CommandDispatch CheckFeatures command:%u", command);
ret = false;
break;
}
if (!CheckSandboxOptionCompatibility(cmdBundleName, context)) {
ret = false;
break;
}
int ioRet = SimpleFileIO(context, context->indexIO, nullptr, (context->isStableBufSize) ?
Base::GetMaxBufSizeStable() * maxTransferBufFactor :
Base::GetMaxBufSize() * maxTransferBufFactor);
if (ioRet < 0) {
WRITE_LOG(LOG_FATAL, "CommandDispatch SimpleFileIO ioRet:%d", ioRet);
ret = false;
break;
}
context->transferBegin = Base::GetRuntimeMSec();
if (!ReportFileCommandEvent(context->localPath, context->master, taskInfo->serverOrDaemon)) {
ret = false;
break;
}
} else if (command == commandData) {
if (static_cast<uint32_t>(payloadSize) > HDC_BUF_MAX_BYTES || payloadSize < 0) {
WRITE_LOG(LOG_FATAL, "CommandDispatch payloadSize:%d", payloadSize);
ret = false;
break;
}
CtxFile *context = &ctxNow;
if (!RecvIOPayload(context, payload, payloadSize)) {
WRITE_LOG(LOG_DEBUG, "RecvIOPayload return false. channelId:%u lastErrno:%u result:%d isFdOpen %d",
taskInfo->channelId, ctxNow.lastErrno, ctxNow.openFd, ctxNow.isFdOpen);
CloseCtxFd(&ctxNow);
ret = false;
break;
}
} else {
}
break;
}
return ret;
}
void HdcTransferBase::ExtractRelativePath(string &cwd, string &path)
{
bool absPath = Base::IsAbsolutePath(path);
if (absPath) {
return;
}
path = cwd + path;
}
bool HdcTransferBase::AddFeatures(FeatureFlagsUnion &feature)
{
feature.bits.hugeBuf = !isStableBuf;
feature.bits.reserveBits1 = 1;
return true;
}
bool HdcTransferBase::CheckFeatures(CtxFile *context, uint8_t *payload, const int payloadSize)
{
if (payloadSize == FEATURE_FLAG_MAX_SIZE) {
union FeatureFlagsUnion feature{};
if (memcpy_s(&feature, sizeof(feature), payload, payloadSize) != EOK) {
WRITE_LOG(LOG_FATAL, "CheckFeatures memcpy_s failed");
return false;
}
WRITE_LOG(LOG_DEBUG, "isStableBuf:%d, hugeBuf:%d", isStableBuf, feature.bits.hugeBuf);
context->isStableBufSize = isStableBuf ? true : (!feature.bits.hugeBuf);
context->isOtherSideSandboxSupported = feature.bits.reserveBits1 > 0;
return true;
} else if (payloadSize == 0) {
WRITE_LOG(LOG_DEBUG, "FileBegin CheckFeatures payloadSize:%d, use default feature.", payloadSize);
context->isStableBufSize = true;
context->isOtherSideSandboxSupported = false;
return true;
} else {
WRITE_LOG(LOG_FATAL, "CheckFeatures payloadSize:%d", payloadSize);
return false;
}
}
bool HdcTransferBase::CheckSandboxOptionCompatibility(const string &option, CtxFile *context)
{
if (option == cmdBundleName && !context->isOtherSideSandboxSupported && context->sandboxMode) {
const char* name = taskInfo->serverOrDaemon ? "Device ROM" : "SDK";
WRITE_LOG(LOG_DEBUG, "%s doesn't support %s option.", name, option.c_str());
LogMsg(MSG_FAIL, "[E005004] %s doesn't support %s option.", name, option.c_str());
OnFileOpenFailed(context);
TaskFinish();
return false;
}
return true;
}
}