1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "transfer.h"
16 #include "serial_struct.h"
17 #include <sys/stat.h>
18 #ifdef HARMONY_PROJECT
19 #include <lz4.h>
20 #endif
21 #if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
22 #include <selinux/selinux.h>
23 #endif
24 #include <memory>
25 namespace Hdc {
26 constexpr uint64_t HDC_TIME_CONVERT_BASE = 1000000000;
27
28
HdcTransferBase(HTaskInfo hTaskInfo)29 HdcTransferBase::HdcTransferBase(HTaskInfo hTaskInfo)
30 : HdcTaskBase(hTaskInfo)
31 {
32 ResetCtx(&ctxNow, true);
33 commandBegin = 0;
34 commandData = 0;
35 isStableBuf = false;
36 }
37
~HdcTransferBase()38 HdcTransferBase::~HdcTransferBase()
39 {
40 if (ctxNow.isFdOpen) {
41 WRITE_LOG(LOG_DEBUG, "~HdcTransferBase channelId:%u lastErrno:%u result:%d ioFinish:%d",
42 taskInfo->channelId, ctxNow.lastErrno, ctxNow.openFd, ctxNow.ioFinish);
43
44 if (ctxNow.lastErrno != 0 || (ctxNow.openFd > 0 && !ctxNow.ioFinish)) {
45 CloseCtxFd(&ctxNow);
46 }
47 } else {
48 WRITE_LOG(LOG_DEBUG, "~HdcTransferBase channelId:%u lastErrno:%u ioFinish:%d",
49 taskInfo->channelId, ctxNow.lastErrno, ctxNow.ioFinish);
50 }
51 };
52
ResetCtx(CtxFile * context,bool full)53 bool HdcTransferBase::ResetCtx(CtxFile *context, bool full)
54 {
55 if (full) {
56 *context = {};
57 context->thisClass = this;
58 context->loop = loopTask;
59 context->cb = OnFileIO;
60 }
61 context->closeNotify = false;
62 context->indexIO = 0;
63 context->lastErrno = 0;
64 context->ioFinish = false;
65 context->closeReqSubmitted = false;
66 context->openFd = -1;
67 return true;
68 }
69
SimpleFileIO(CtxFile * context,uint64_t index,uint8_t * sendBuf,int bytes)70 int HdcTransferBase::SimpleFileIO(CtxFile *context, uint64_t index, uint8_t *sendBuf, int bytes)
71 {
72 StartTraceScope("HdcTransferBase::SimpleFileIO");
73 // The first 8 bytes file offset
74 #ifndef CONFIG_USE_JEMALLOC_DFX_INIF
75 uint8_t *buf = cirbuf.Malloc();
76 #else
77 uint8_t *buf = new uint8_t[bytes + payloadPrefixReserve]();
78 #endif
79 if (buf == nullptr) {
80 WRITE_LOG(LOG_FATAL, "SimpleFileIO buf nullptr cid:%u sid:%u", taskInfo->channelId, taskInfo->sessionId);
81 return -1;
82 }
83 CtxFileIO *ioContext = new(std::nothrow) CtxFileIO();
84 if (ioContext == nullptr) {
85 #ifndef CONFIG_USE_JEMALLOC_DFX_INIF
86 cirbuf.Free(buf);
87 #else
88 delete[] buf;
89 #endif
90 WRITE_LOG(LOG_FATAL, "SimpleFileIO ioContext nullptr cid:%u sid:%u",
91 taskInfo->channelId, taskInfo->sessionId);
92 return -1;
93 }
94 bool ret = false;
95 while (true) {
96 size_t bufMaxSize = context->isStableBufSize ?
97 static_cast<size_t>(Base::GetUsbffsBulkSizeStable() - payloadPrefixReserve) :
98 static_cast<size_t>(Base::GetUsbffsBulkSize() - payloadPrefixReserve);
99 if (bytes < 0 || static_cast<size_t>(bytes) > bufMaxSize) {
100 WRITE_LOG(LOG_DEBUG, "SimpleFileIO param check failed cid:%u sid:%u", taskInfo->channelId,
101 taskInfo->sessionId);
102 break;
103 }
104 if (context->ioFinish) {
105 WRITE_LOG(LOG_DEBUG, "SimpleFileIO to closed IOStream cid:%u sid:%u", taskInfo->channelId,
106 taskInfo->sessionId);
107 break;
108 }
109 uv_fs_t *req = &ioContext->fs;
110 ioContext->bytes = static_cast<uint64_t>(bytes);
111 ioContext->bufIO = buf + payloadPrefixReserve;
112 ioContext->context = context;
113 req->data = ioContext;
114 if (context->master) { // master just read, and slave just write.when master/read, sendBuf can be nullptr
115 uv_buf_t iov = uv_buf_init(reinterpret_cast<char *>(ioContext->bufIO), bytes);
116 int rc = uv_fs_read(context->loop, req, context->openFd, &iov, 1, index, context->cb);
117 if (rc < 0) {
118 WRITE_LOG(LOG_DEBUG, "uv_fs_read failed rc:%d", rc);
119 break;
120 } else {
121 ++refCount;
122 }
123 } else {
124 // The US_FS_WRITE here must be brought into the actual file offset, which cannot be incorporated with local
125 // accumulated index because UV_FS_WRITE will be executed multiple times and then trigger a callback.
126 if (bytes > 0 && memcpy_s(ioContext->bufIO, bufMaxSize, sendBuf, bytes) != EOK) {
127 WRITE_LOG(LOG_WARN, "SimpleFileIO memcpy error cid:%u sid:%u", taskInfo->channelId,
128 taskInfo->sessionId);
129 break;
130 }
131 uv_buf_t iov = uv_buf_init(reinterpret_cast<char *>(ioContext->bufIO), bytes);
132 int rc = uv_fs_write(context->loop, req, context->openFd, &iov, 1, index, context->cb);
133 if (rc < 0) {
134 WRITE_LOG(LOG_DEBUG, "uv_fs_write failed rc:%d", rc);
135 break;
136 } else {
137 ++refCount;
138 }
139 }
140 ret = true;
141 break;
142 }
143 if (!ret) {
144 if (ioContext != nullptr) {
145 delete ioContext;
146 ioContext = nullptr;
147 WRITE_LOG(LOG_WARN, "SimpleFileIO ret=false, delete context, cid:%u sid:%u", taskInfo->channelId,
148 taskInfo->sessionId);
149 }
150 #ifndef CONFIG_USE_JEMALLOC_DFX_INIF
151 cirbuf.Free(buf);
152 #else
153 delete[] buf;
154 #endif
155 return -1;
156 }
157 return bytes;
158 }
159
OnFileClose(CtxFile * context)160 void HdcTransferBase::OnFileClose(CtxFile *context)
161 {
162 StartTraceScope("HdcTransferBase::OnFileClose");
163 context->closeReqSubmitted = false;
164 HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
165 WRITE_LOG(LOG_DEBUG, "OnFileClose channelId:%u closeNotify:%d",
166 thisClass->taskInfo->channelId, context->closeNotify);
167 if (context->closeNotify) {
168 // close-step2
169 // maybe successful finish or failed finish
170 thisClass->WhenTransferFinish(context);
171 }
172 --thisClass->refCount;
173 return;
174 }
175
SetFileTime(CtxFile * context)176 void HdcTransferBase::SetFileTime(CtxFile *context)
177 {
178 if (!context->transferConfig.holdTimestamp) {
179 return;
180 }
181 if (!context->transferConfig.mtime) {
182 return;
183 }
184 uv_fs_t fs;
185 double aTimeSec = static_cast<long double>(context->transferConfig.atime) / HDC_TIME_CONVERT_BASE;
186 double mTimeSec = static_cast<long double>(context->transferConfig.mtime) / HDC_TIME_CONVERT_BASE;
187 uv_fs_futime(nullptr, &fs, context->openFd, aTimeSec, mTimeSec, nullptr);
188 uv_fs_req_cleanup(&fs);
189 }
190
InitTransferPayload(TransferPayload & payloadHead,uint64_t index,uint8_t compressType,uint32_t dataSize)191 bool HdcTransferBase::InitTransferPayload(TransferPayload &payloadHead, uint64_t index, uint8_t compressType,
192 uint32_t dataSize)
193 {
194 payloadHead.compressType = compressType;
195 payloadHead.uncompressSize = dataSize;
196 payloadHead.index = index;
197 return true;
198 }
199
SendIOPayload(CtxFile * context,uint64_t index,uint8_t * data,int dataSize)200 bool HdcTransferBase::SendIOPayload(CtxFile *context, uint64_t index, uint8_t *data, int dataSize)
201 {
202 TransferPayload payloadHead;
203 string head;
204 int compressSize = 0;
205 int sendBufSize = payloadPrefixReserve + dataSize;
206 uint8_t *sendBuf = data - payloadPrefixReserve;
207 bool ret = false;
208
209 StartTraceScope("HdcTransferBase::SendIOPayload");
210 InitTransferPayload(payloadHead, index, context->transferConfig.compressType, dataSize);
211 if (dataSize > 0) {
212 switch (payloadHead.compressType) {
213 #ifdef HARMONY_PROJECT
214 case COMPRESS_LZ4: {
215 sendBuf = new uint8_t[sendBufSize]();
216 if (!sendBuf) {
217 WRITE_LOG(LOG_FATAL, "alloc LZ4 buffer failed");
218 return false;
219 }
220 compressSize = LZ4_compress_default((const char *)data, (char *)sendBuf + payloadPrefixReserve,
221 dataSize, dataSize);
222 if (compressSize <= 0) {
223 WRITE_LOG(LOG_DEBUG, "LZ4 compress failed, path: %s compress none",
224 context->localPath.c_str());
225 delete[] sendBuf;
226 payloadHead.compressType = COMPRESS_NONE;
227 compressSize = dataSize;
228 sendBuf = data - payloadPrefixReserve;
229 }
230 break;
231 }
232 #endif
233 default: { // COMPRESS_NONE
234 compressSize = dataSize;
235 break;
236 }
237 }
238 }
239 payloadHead.compressSize = compressSize;
240 head = SerialStruct::SerializeToString(payloadHead);
241 if (head.size() + 1 > payloadPrefixReserve ||
242 EOK != memcpy_s(sendBuf, sendBufSize, head.c_str(), head.size() + 1)) {
243 WRITE_LOG(LOG_WARN, "fail, head size:%d, reserve:%d", head.size(), payloadPrefixReserve);
244 goto out;
245 }
246 ret = SendToAnother(commandData, sendBuf, payloadPrefixReserve + compressSize) > 0;
247 out:
248 if (dataSize > 0 && payloadHead.compressType == COMPRESS_LZ4) {
249 delete[] sendBuf;
250 }
251 return ret;
252 }
253
ProcressFileIOFinish(uv_fs_t * req,CtxFile * context,HdcTransferBase * thisClass)254 void HdcTransferBase::ProcressFileIOFinish(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
255 {
256 // close-step1
257 ++thisClass->refCount;
258 if (req->fs_type == UV_FS_WRITE && context->isFdOpen) {
259 uv_fs_t req = {};
260 uv_fs_fsync(nullptr, &req, context->openFd, nullptr);
261 uv_fs_req_cleanup(&req);
262 }
263 WRITE_LOG(LOG_DEBUG, "channelId:%u result:%d, closeReqSubmitted:%d, context->isFdOpen %d",
264 thisClass->taskInfo->channelId, context->openFd, context->closeReqSubmitted, context->isFdOpen);
265 CloseCtxFd(context);
266 if (context->lastErrno == 0 && !context->closeReqSubmitted) {
267 context->closeReqSubmitted = true;
268 WRITE_LOG(LOG_DEBUG, "OnFileIO fs_close, channelId:%u", thisClass->taskInfo->channelId);
269 OnFileClose(context);
270 } else {
271 thisClass->WhenTransferFinish(context);
272 --thisClass->refCount;
273 }
274 }
275 // return true: finished
ProcressFileIOWrite(uv_fs_t * req,CtxFile * context,HdcTransferBase * thisClass)276 bool HdcTransferBase::ProcressFileIOWrite(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
277 {
278 DEBUG_LOG("write file data %" PRIu64 "/%" PRIu64 "", context->indexIO, context->fileSize);
279 if (context->indexIO >= context->fileSize || req->result == 0) {
280 // The active end must first read it first, but you can't make Finish first, because Slave may not
281 // end.Only slave receives complete talents Finish
282 context->closeNotify = true;
283 thisClass->SetFileTime(context);
284 return true;
285 }
286 return false;
287 }
288 // return true: finished
ProcressFileIORead(uv_fs_t * req,CtxFile * context,HdcTransferBase * thisClass)289 bool HdcTransferBase::ProcressFileIORead(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass)
290 {
291 CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
292 uint8_t *bufIO = contextIO->bufIO;
293 DEBUG_LOG("read file data %" PRIu64 "/%" PRIu64 "", context->indexIO, context->fileSize);
294 if (!thisClass->SendIOPayload(context, context->indexIO - req->result, bufIO, req->result)) {
295 WRITE_LOG(LOG_WARN, "OnFileIO SendIOPayload fail.");
296 return true;
297 }
298 if (req->result == 0) {
299 WRITE_LOG(LOG_DEBUG, "path:%s fd:%d eof", context->localPath.c_str(), context->openFd);
300 return true;
301 }
302 if (context->indexIO < context->fileSize) {
303 thisClass->SimpleFileIO(context, context->indexIO, nullptr, context->isStableBufSize ?
304 (Base::GetMaxBufSizeStable() * thisClass->maxTransferBufFactor) :
305 (Base::GetMaxBufSize() * thisClass->maxTransferBufFactor));
306 return false;
307 }
308 return true;
309 }
310 // return true: finished
ProcressFileIO(uv_fs_t * req,CtxFile * context,HdcTransferBase * thisClass,uint64_t bytes)311 bool HdcTransferBase::ProcressFileIO(uv_fs_t *req, CtxFile *context, HdcTransferBase *thisClass,
312 uint64_t bytes)
313 {
314 if (context->ioFinish) {
315 WRITE_LOG(LOG_DEBUG, "OnFileIO finish is true.");
316 return true;
317 }
318 if (!ProcressFileIOIsSuccess(req, context, bytes)) {
319 context->closeNotify = true;
320 if (!context->master) {
321 uint8_t payload = 0;
322 thisClass->CommandDispatch(CMD_FILE_FINISH, &payload, sizeof(payload));
323 }
324 return true;
325 }
326 context->indexIO += static_cast<uint64_t>(req->result);
327 if (req->fs_type == UV_FS_READ) {
328 return ProcressFileIORead(req, context, thisClass);
329 }
330 if (req->fs_type == UV_FS_WRITE) { // write
331 return ProcressFileIOWrite(req, context, thisClass);
332 }
333
334 return true;
335 }
336 // check process file IO Error
ProcressFileIOIsSuccess(uv_fs_t * req,CtxFile * context,uint64_t bytes)337 bool HdcTransferBase::ProcressFileIOIsSuccess(uv_fs_t *req, CtxFile *context, uint64_t bytes)
338 {
339 if (req->result < 0) {
340 constexpr int bufSize = 1024;
341 char buf[bufSize] = { 0 };
342 uv_strerror_r((int)req->result, buf, bufSize);
343 WRITE_LOG(LOG_DEBUG, "OnFileIO error: %s", buf);
344 context->lastErrno = static_cast<uint32_t>(abs(req->result));
345 return false;
346 }
347 if (context->master) {
348 return true;
349 }
350 if (bytes == static_cast<uint64_t>(req->result)) {
351 return true;
352 }
353 // On Unix platforms, uv_fs_write invoke the write function multiple times.
354 // req->result only retains the errno of the first failed write attempt.
355 // If the first write succeeds but a subsequent one fails,
356 // req->result will reflect the total number of bytes written.
357 // Therefore, if the expected number of bytes does not match req->result,
358 // it indicates that a write failure has occurred.
359 WRITE_LOG(LOG_WARN, "OnFileIO read bytes:%llu not equal to req result:%d", bytes, req->result);
360 uv_fs_t fs = {};
361 int ret = uv_fs_statfs(nullptr, &fs, context->localPath.c_str(), nullptr);
362 if (ret < 0 || (!fs.ptr)) {
363 constexpr int size = 1024;
364 char buf[size] = { 0 };
365 uv_strerror_r(ret, buf, size);
366 WRITE_LOG(LOG_WARN, "CheckSpace error querying filesystem: %s, path: %s",
367 buf, context->localPath.c_str());
368 uv_fs_req_cleanup(&fs);
369 context->lastErrno = static_cast<uint32_t>(abs(ret));
370 return false;
371 }
372 uv_statfs_t* statfs = static_cast<uv_statfs_t*>(fs.ptr);
373 uint64_t freeBytes = statfs->f_bsize * statfs->f_bfree;
374 WRITE_LOG(LOG_DEBUG, "CheckSpace, path: %s, freeBytes: %llu",
375 context->localPath.c_str(), freeBytes);
376 uv_fs_req_cleanup(&fs);
377 context->lastErrno = static_cast<uint32_t>((freeBytes == 0) ? ENOSPC : EIO);
378 return false;
379 }
380 // return true: do io delayed
IODelayed(uv_fs_t * req)381 bool HdcTransferBase::IODelayed(uv_fs_t *req)
382 {
383 #ifndef HDC_HOST
384 return false;
385 #endif
386 if (req->fs_type != UV_FS_READ) {
387 return false;
388 }
389 CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
390 CtxFile *context = reinterpret_cast<CtxFile *>(contextIO->context);
391 HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
392 if (thisClass->taskInfo->channelTask) {
393 const int maxPackages = 64;
394 const int onePackageTransferTime = 2;
395 const int delayPackages = maxPackages / 2;
396 const int delayMs = delayPackages * onePackageTransferTime;
397 HdcChannelBase *channelBase = reinterpret_cast<HdcChannelBase *>(thisClass->taskInfo->channelClass);
398 if (channelBase->queuedPackages.load() >= maxPackages) {
399 WRITE_LOG(LOG_DEBUG, "queued packages:%d is full", channelBase->queuedPackages.load());
400 Base::DelayDo(req->loop, delayMs, 0, "ChannelFull", req,
401 [](const uint8_t flag, string &msg, const void *data) {
402 uv_fs_t *req = (uv_fs_t *)data;
403 OnFileIO(req);
404 });
405 return true;
406 }
407 channelBase->queuedPackages.fetch_add(1, std::memory_order_relaxed);
408 }
409 return false;
410 }
OnFileIO(uv_fs_t * req)411 void HdcTransferBase::OnFileIO(uv_fs_t *req)
412 {
413 StartTraceScope("HdcTransferBase::OnFileIO");
414 if (IODelayed(req)) {
415 return;
416 }
417 CtxFileIO *contextIO = reinterpret_cast<CtxFileIO *>(req->data);
418 CtxFile *context = reinterpret_cast<CtxFile *>(contextIO->context);
419 HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
420 CALLSTAT_GUARD(*(thisClass->loopTaskStatus), req->loop, "HdcTransferBase::OnFileIO");
421 uint8_t *bufIO = contextIO->bufIO;
422 uv_fs_req_cleanup(req);
423 context->ioFinish = ProcressFileIO(req, context, thisClass, contextIO->bytes);
424 if (context->ioFinish) {
425 ProcressFileIOFinish(req, context, thisClass);
426 }
427 #ifndef CONFIG_USE_JEMALLOC_DFX_INIF
428 thisClass->cirbuf.Free(bufIO - payloadPrefixReserve);
429 #else
430 delete [] (bufIO - payloadPrefixReserve);
431 #endif
432 --thisClass->refCount;
433 delete contextIO; // Req is part of the Contextio structure, no free release
434 }
435
OnFileOpenFailed(CtxFile * context)436 void HdcTransferBase::OnFileOpenFailed(CtxFile *context)
437 {
438 HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
439 if (context->isDir && context->master) {
440 uint8_t payload = 1;
441 thisClass->CommandDispatch(CMD_FILE_FINISH, &payload, 1);
442 } else if (context->isDir && !context->master) {
443 uint8_t payload = 1;
444 thisClass->SendToAnother(CMD_FILE_FINISH, &payload, 1);
445 } else {
446 thisClass->TaskFinish();
447 }
448 return;
449 }
450
IsValidBundlePath(const string & bundleName)451 bool HdcTransferBase::IsValidBundlePath(const string &bundleName)
452 {
453 string fullPath = SANDBOX_ROOT_DIR + bundleName + Base::GetPathSep();
454 return Base::CheckBundleName(bundleName) && access(fullPath.c_str(), F_OK) == 0;
455 }
456
RemoveSandboxRootPath(std::string & srcStr,const std::string & bundleName)457 void HdcTransferBase::RemoveSandboxRootPath(std::string &srcStr, const std::string &bundleName)
458 {
459 if (taskInfo->serverOrDaemon || !IsValidBundlePath(bundleName)) {
460 return;
461 }
462 string fullPath = SANDBOX_ROOT_DIR + bundleName + Base::GetPathSep();
463 size_t pos = 0;
464 if ((pos = srcStr.find(fullPath)) != std::string::npos) {
465 srcStr = srcStr.replace(pos, fullPath.length(), "");
466 } else {
467 WRITE_LOG(LOG_DEBUG, "fullPath:%s, srcStr:%s", Hdc::MaskString(fullPath).c_str(),
468 Hdc::MaskString(srcStr).c_str());
469 }
470 }
471
OnFileOpen(uv_fs_t * req)472 void HdcTransferBase::OnFileOpen(uv_fs_t *req)
473 {
474 StartTraceScope("HdcTransferBase::OnFileOpen");
475 std::unique_ptr<uv_fs_t> uptrReq(req);
476 CtxFile *context = (CtxFile *)req->data;
477 HdcTransferBase *thisClass = (HdcTransferBase *)context->thisClass;
478 CALLSTAT_GUARD(*(thisClass->loopTaskStatus), req->loop, "HdcTransferBase::OnFileOpen");
479 uv_fs_req_cleanup(req);
480 WRITE_LOG(LOG_DEBUG, "Filemod openfile:%s channelId:%u result:%d",
481 context->localPath.c_str(), thisClass->taskInfo->channelId, req->result);
482 --thisClass->refCount;
483 if (req->result <= 0) {
484 constexpr int bufSize = 1024;
485 char buf[bufSize] = { 0 };
486 uv_strerror_r((int)req->result, buf, bufSize);
487 string localPath = context->localPath;
488 thisClass->RemoveSandboxRootPath(localPath, context->bundleName);
489 thisClass->LogMsg(MSG_FAIL, "Error opening file: %s, path:%s", buf,
490 localPath.c_str());
491 WRITE_LOG(LOG_FATAL, "open path:%s, localPath:%s, error:%s, dir:%d, master:%d",
492 context->localPath.c_str(),
493 localPath.c_str(), buf, context->isDir, context->master);
494 OnFileOpenFailed(context);
495 return;
496 }
497 thisClass->ResetCtx(context);
498 context->isFdOpen = true;
499 context->openFd = req->result;
500 if (context->master) { // master just read, and slave just write.
501 // init master
502 uv_fs_t fs = {};
503 uv_fs_fstat(nullptr, &fs, context->openFd, nullptr);
504 WRITE_LOG(LOG_DEBUG, "uv_fs_fstat result:%d fileSize:%llu",
505 context->openFd, fs.statbuf.st_size);
506 TransferConfig &st = context->transferConfig;
507 st.fileSize = fs.statbuf.st_size;
508 st.optionalName = context->localName;
509 if (st.holdTimestamp) {
510 st.atime = fs.statbuf.st_atim.tv_sec * HDC_TIME_CONVERT_BASE + fs.statbuf.st_atim.tv_nsec;
511 st.mtime = fs.statbuf.st_mtim.tv_sec * HDC_TIME_CONVERT_BASE + fs.statbuf.st_mtim.tv_nsec;
512 }
513 st.path = context->remotePath;
514 // update ctxNow=context child value
515 context->fileSize = st.fileSize;
516 context->fileMode.perm = fs.statbuf.st_mode;
517 context->fileMode.uId = fs.statbuf.st_uid;
518 context->fileMode.gId = fs.statbuf.st_gid;
519 #if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
520 char *con = nullptr;
521 getfilecon(context->localPath.c_str(), &con);
522 if (con != nullptr) {
523 context->fileMode.context = con;
524 freecon(con);
525 }
526 #endif
527 uv_fs_req_cleanup(&fs);
528 thisClass->CheckMaster(context);
529 } else { // write
530 if (context->fileModeSync) {
531 FileMode &mode = context->fileMode;
532 uv_fs_t fs = {};
533 uv_fs_chmod(nullptr, &fs, context->localPath.c_str(), mode.perm, nullptr);
534 uv_fs_chown(nullptr, &fs, context->localPath.c_str(), mode.uId, mode.gId, nullptr);
535 uv_fs_req_cleanup(&fs);
536
537 #if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
538 if (!mode.context.empty()) {
539 WRITE_LOG(LOG_DEBUG, "setfilecon from master = %s", mode.context.c_str());
540 setfilecon(context->localPath.c_str(), mode.context.c_str());
541 }
542 #endif
543 }
544 union FeatureFlagsUnion f{};
545 if (!thisClass->AddFeatures(f)) {
546 WRITE_LOG(LOG_FATAL, "AddFeatureFlag failed");
547 thisClass->SendToAnother(thisClass->commandBegin, nullptr, 0);
548 } else {
549 thisClass->SendToAnother(thisClass->commandBegin, f.raw, sizeof(f));
550 }
551 }
552 }
553
MatchPackageExtendName(string fileName,string extName)554 bool HdcTransferBase::MatchPackageExtendName(string fileName, string extName)
555 {
556 bool match = false;
557 int subfixIndex = fileName.rfind(extName);
558 if ((fileName.size() - subfixIndex) != extName.size()) {
559 return false;
560 }
561 match = true;
562 return match;
563 }
564
565 // filter can be empty
GetSubFiles(const char * path,string filter,vector<string> * out)566 int HdcTransferBase::GetSubFiles(const char *path, string filter, vector<string> *out)
567 {
568 int retNum = 0;
569 uv_fs_t req = {};
570 uv_dirent_t dent;
571 vector<string> filterStrings;
572 if (!strlen(path)) {
573 return retNum;
574 }
575 if (filter.size()) {
576 Base::SplitString(filter, ";", filterStrings);
577 }
578
579 if (uv_fs_scandir(nullptr, &req, path, 0, nullptr) < 0) {
580 uv_fs_req_cleanup(&req);
581 return retNum;
582 }
583 while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
584 // Skip. File
585 if (strcmp(dent.name, ".") == 0 || strcmp(dent.name, "..") == 0) {
586 continue;
587 }
588 if (!(static_cast<uint32_t>(dent.type) & UV_DIRENT_FILE)) {
589 continue;
590 }
591 string fileName = dent.name;
592 for (auto &&s : filterStrings) {
593 int subfixIndex = fileName.rfind(s);
594 if ((fileName.size() - subfixIndex) != s.size())
595 continue;
596 string fullPath = string(path) + Base::GetPathSep();
597 fullPath += fileName;
598 out->push_back(fullPath);
599 ++retNum;
600 }
601 }
602 uv_fs_req_cleanup(&req);
603 return retNum;
604 }
605
606
GetSubFilesRecursively(string path,string currentDirname,vector<string> * out)607 int HdcTransferBase::GetSubFilesRecursively(string path, string currentDirname, vector<string> *out)
608 {
609 int retNum = 0;
610 uv_fs_t req = {};
611 uv_dirent_t dent;
612
613 WRITE_LOG(LOG_DEBUG, "GetSubFiles path = %s currentDirname = %s", path.c_str(), currentDirname.c_str());
614
615 if (!path.size()) {
616 return retNum;
617 }
618
619 if (uv_fs_scandir(nullptr, &req, path.c_str(), 0, nullptr) < 0) {
620 uv_fs_req_cleanup(&req);
621 return retNum;
622 }
623
624 uv_fs_t fs = {};
625 int ret = uv_fs_stat(nullptr, &fs, path.c_str(), nullptr);
626 if (ret == 0) {
627 FileMode mode;
628 mode.fullName = currentDirname;
629 mode.perm = fs.statbuf.st_mode;
630 mode.uId = fs.statbuf.st_uid;
631 mode.gId = fs.statbuf.st_gid;
632
633 #if (!(defined(HOST_MINGW)||defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
634 char *con = nullptr;
635 getfilecon(path.c_str(), &con);
636 if (con != nullptr) {
637 mode.context = con;
638 freecon(con);
639 }
640 #endif
641 ctxNow.dirMode.push_back(mode);
642 }
643 while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
644 // Skip. File
645 if (strcmp(dent.name, ".") == 0 || strcmp(dent.name, "..") == 0) {
646 continue;
647 }
648 if (!(static_cast<uint32_t>(dent.type) & UV_DIRENT_FILE)) {
649 WRITE_LOG(LOG_DEBUG, "subdir dent.name fileName = %s", dent.name);
650 GetSubFilesRecursively(path + Base::GetPathSep() + dent.name,
651 currentDirname + Base::GetPathSep() + dent.name, out);
652 continue;
653 }
654 string fileName = dent.name;
655 WRITE_LOG(LOG_DEBUG, "GetSubFiles fileName = %s", fileName.c_str());
656
657 out->push_back(currentDirname + Base::GetPathSep() + fileName);
658 }
659 uv_fs_req_cleanup(&req);
660 return retNum;
661 }
662
663
CheckLocalPath(string & localPath,string & optName,string & errStr)664 bool HdcTransferBase::CheckLocalPath(string &localPath, string &optName, string &errStr)
665 {
666 // If optName show this is directory mode, check localPath and try create each layer
667 WRITE_LOG(LOG_DEBUG, "CheckDirectory localPath = %s optName = %s",
668 localPath.c_str(), optName.c_str());
669 if ((optName.find('/') == string::npos) && (optName.find('\\') == string::npos)) {
670 WRITE_LOG(LOG_DEBUG, "Not directory mode optName = %s, return", optName.c_str());
671 return true;
672 } // when transfer single file, slaver will not create each layer directory.
673 ctxNow.isDir = true;
674 uv_fs_t req;
675 int r = uv_fs_lstat(nullptr, &req, localPath.c_str(), nullptr);
676 mode_t mode = req.statbuf.st_mode;
677 uv_fs_req_cleanup(&req);
678 if (r) {
679 vector<string> dirsOflocalPath;
680 string split(1, Base::GetPathSep());
681 Base::SplitString(localPath, split, dirsOflocalPath);
682 if (dirsOflocalPath.size() > 0 && !ctxNow.isDir && localPath.back() != Base::GetPathSep()) {
683 dirsOflocalPath.pop_back();
684 }
685 WRITE_LOG(LOG_DEBUG, "localPath = %s dir layers = %zu",
686 localPath.c_str(), dirsOflocalPath.size());
687 string makedirPath;
688 if (!Base::IsAbsolutePath(localPath)) {
689 makedirPath = ".";
690 }
691 for (auto dir : dirsOflocalPath) {
692 WRITE_LOG(LOG_DEBUG, "CheckLocalPath create dir = %s", dir.c_str());
693
694 if (dir == ".") {
695 continue;
696 } else {
697 #ifdef _WIN32
698 if (dir.find(":") == 1) {
699 makedirPath = dir;
700 continue;
701 }
702 #endif
703 makedirPath = makedirPath + Base::GetPathSep() + dir;
704 if (!Base::TryCreateDirectory(makedirPath, errStr)) {
705 return false;
706 }
707 }
708 }
709 // set flag to remove first layer directory of filename from master
710 ctxNow.targetDirNotExist = true;
711 } else if (ctxNow.isDir && !(mode & S_IFDIR)) {
712 WRITE_LOG(LOG_WARN, "Not a directory, path:%s", localPath.c_str());
713 errStr = "Not a directory, path:" + localPath;
714 return false;
715 }
716 return true;
717 }
718
CheckFilename(string & localPath,string & optName,string & errStr)719 bool HdcTransferBase::CheckFilename(string &localPath, string &optName, string &errStr)
720 {
721 string localPathBackup = localPath;
722 if (ctxNow.targetDirNotExist) {
723 // If target directory not exist, the first layer directory from master should remove
724 if (optName.find('/') != string::npos) {
725 optName = optName.substr(optName.find('/') + 1);
726 } else if (optName.find('\\') != string::npos) {
727 optName = optName.substr(optName.find('\\') + 1);
728 }
729 }
730 vector<string> dirsOfOptName;
731
732 if (optName.find('/') != string::npos) {
733 Base::SplitString(optName, "/", dirsOfOptName);
734 } else if (optName.find('\\') != string::npos) {
735 Base::SplitString(optName, "\\", dirsOfOptName);
736 } else {
737 WRITE_LOG(LOG_DEBUG, "No need create dir for file = %s", optName.c_str());
738 return true;
739 }
740
741 // If filename still include dir, try create each layer
742 optName = dirsOfOptName.back();
743 dirsOfOptName.pop_back();
744
745 for (auto s : dirsOfOptName) {
746 // Add each layer directory to localPath
747 localPath = localPath + Base::GetPathSep() + s;
748 if (!Base::TryCreateDirectory(localPath, errStr)) {
749 return false;
750 }
751 if (ctxNow.fileModeSync) {
752 string resolvedPath = Base::CanonicalizeSpecPath(localPath);
753 auto pos = resolvedPath.find(localPathBackup);
754 if (pos == 0) {
755 string shortPath = resolvedPath.substr(localPathBackup.size());
756 if (shortPath.at(0) == Base::GetPathSep()) {
757 shortPath = shortPath.substr(1);
758 }
759 WRITE_LOG(LOG_DEBUG, "pos = %zu, shortPath = %s", pos, shortPath.c_str());
760
761 // set mode
762 auto it = ctxNow.dirModeMap.find(shortPath);
763 if (it != ctxNow.dirModeMap.end()) {
764 auto mode = it->second;
765 uv_fs_t fs = {};
766 uv_fs_chmod(nullptr, &fs, localPath.c_str(), mode.perm, nullptr);
767 uv_fs_chown(nullptr, &fs, localPath.c_str(), mode.uId, mode.gId, nullptr);
768 uv_fs_req_cleanup(&fs);
769 #if (!(defined(HOST_MINGW) || defined(HOST_MAC))) && defined(SURPPORT_SELINUX)
770 if (!mode.context.empty()) {
771 WRITE_LOG(LOG_DEBUG, "setfilecon from master = %s", mode.context.c_str());
772 setfilecon(localPath.c_str(), mode.context.c_str());
773 }
774 #endif
775 }
776 }
777 }
778 }
779
780 WRITE_LOG(LOG_DEBUG, "CheckFilename finish localPath:%s optName:%s",
781 Hdc::MaskString(localPath).c_str(), optName.c_str());
782 return true;
783 }
784
785 // https://en.cppreference.com/w/cpp/filesystem/is_directory
786 // return true if file exist, false if file not exist
SmartSlavePath(string & cwd,string & localPath,const char * optName)787 bool HdcTransferBase::SmartSlavePath(string &cwd, string &localPath, const char *optName)
788 {
789 string errStr;
790 if (taskInfo->serverOrDaemon) {
791 // slave and server
792 ExtractRelativePath(cwd, localPath);
793 }
794 mode_t mode = mode_t(~S_IFMT);
795 if (Base::CheckDirectoryOrPath(localPath.c_str(), true, false, errStr, mode)) {
796 WRITE_LOG(LOG_DEBUG, "%s", errStr.c_str());
797 return true;
798 }
799
800 uv_fs_t req;
801 int r = uv_fs_lstat(nullptr, &req, localPath.c_str(), nullptr);
802 uv_fs_req_cleanup(&req);
803 if (r == 0 && (req.statbuf.st_mode & S_IFDIR)) { // is dir
804 localPath = localPath + Base::GetPathSep() + optName;
805 }
806 if (r != 0 && (localPath.back() == Base::GetPathSep())) { // not exist and is dir
807 localPath = localPath + optName;
808 }
809 return false;
810 }
811
RecvIOPayload(CtxFile * context,uint8_t * data,int dataSize)812 bool HdcTransferBase::RecvIOPayload(CtxFile *context, uint8_t *data, int dataSize)
813 {
814 if (dataSize < static_cast<int>(payloadPrefixReserve)) {
815 WRITE_LOG(LOG_WARN, "unable to parse TransferPayload: invalid dataSize %d", dataSize);
816 return false;
817 }
818 uint8_t *clearBuf = nullptr;
819 string serialString(reinterpret_cast<char *>(data), payloadPrefixReserve);
820 TransferPayload pld;
821 Base::ZeroStruct(pld);
822 bool ret = false;
823 SerialStruct::ParseFromString(pld, serialString);
824 int clearSize = 0;
825 StartTraceScope("HdcTransferBase::RecvIOPayload");
826 if (pld.compressSize > static_cast<uint32_t>(dataSize) || pld.uncompressSize > MAX_SIZE_IOBUF) {
827 WRITE_LOG(LOG_FATAL, "compress size is greater than the dataSize. pld.compressSize = %d", pld.compressSize);
828 return false;
829 }
830 if (pld.compressSize > 0) {
831 switch (pld.compressType) {
832 #ifdef HARMONY_PROJECT
833 case COMPRESS_LZ4: {
834 clearBuf = new uint8_t[pld.uncompressSize]();
835 if (!clearBuf) {
836 WRITE_LOG(LOG_FATAL, "alloc LZ4 buffer failed");
837 return false;
838 }
839 clearSize = LZ4_decompress_safe((const char *)data + payloadPrefixReserve, (char *)clearBuf,
840 pld.compressSize, pld.uncompressSize);
841 break;
842 }
843 #endif
844 default: { // COMPRESS_NONE
845 clearBuf = data + payloadPrefixReserve;
846 clearSize = pld.compressSize;
847 break;
848 }
849 }
850 }
851 while (true) {
852 if (static_cast<uint32_t>(clearSize) != pld.uncompressSize) {
853 WRITE_LOG(LOG_WARN, "invalid data size for fileIO: %d", clearSize);
854 break;
855 }
856 if (pld.compressType == COMPRESS_NONE && dataSize - payloadPrefixReserve < clearSize) {
857 WRITE_LOG(LOG_WARN, "not enough data size for fileIO dataSize: %d, clearSize: %d",
858 dataSize - payloadPrefixReserve, clearSize);
859 break;
860 }
861 if (SimpleFileIO(context, pld.index, clearBuf, clearSize) < 0) {
862 WRITE_LOG(LOG_WARN, "RecvIOPayload SimpleFileIO fail.");
863 break;
864 }
865 ret = true;
866 break;
867 }
868 if (pld.compressSize > 0 && pld.compressType != COMPRESS_NONE) {
869 delete[] clearBuf;
870 }
871 return ret;
872 }
873
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)874 bool HdcTransferBase::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
875 {
876 StartTraceScope("HdcTransferBase::CommandDispatch");
877 bool ret = true;
878 while (true) {
879 if (command == commandBegin) {
880 CtxFile *context = &ctxNow;
881 if (!CheckFeatures(context, payload, payloadSize)) {
882 WRITE_LOG(LOG_FATAL, "CommandDispatch CheckFeatures command:%u", command);
883 ret = false;
884 break;
885 }
886 if (!CheckSandboxOptionCompatibility(cmdBundleName, context)) {
887 ret = false;
888 break;
889 }
890 int ioRet = SimpleFileIO(context, context->indexIO, nullptr, (context->isStableBufSize) ?
891 Base::GetMaxBufSizeStable() * maxTransferBufFactor :
892 Base::GetMaxBufSize() * maxTransferBufFactor);
893 if (ioRet < 0) {
894 WRITE_LOG(LOG_FATAL, "CommandDispatch SimpleFileIO ioRet:%d", ioRet);
895 ret = false;
896 break;
897 }
898 context->transferBegin = Base::GetRuntimeMSec();
899 } else if (command == commandData) {
900 if (static_cast<uint32_t>(payloadSize) > HDC_BUF_MAX_BYTES || payloadSize < 0) {
901 WRITE_LOG(LOG_FATAL, "CommandDispatch payloadSize:%d", payloadSize);
902 ret = false;
903 break;
904 }
905 // Note, I will trigger FileIO after multiple times.
906 CtxFile *context = &ctxNow;
907 if (!RecvIOPayload(context, payload, payloadSize)) {
908 WRITE_LOG(LOG_DEBUG, "RecvIOPayload return false. channelId:%u lastErrno:%u result:%d isFdOpen %d",
909 taskInfo->channelId, ctxNow.lastErrno, ctxNow.openFd, ctxNow.isFdOpen);
910 CloseCtxFd(&ctxNow);
911 ret = false;
912 break;
913 }
914 } else {
915 // Other subclass commands
916 }
917 break;
918 }
919 return ret;
920 }
921
ExtractRelativePath(string & cwd,string & path)922 void HdcTransferBase::ExtractRelativePath(string &cwd, string &path)
923 {
924 bool absPath = Base::IsAbsolutePath(path);
925 if (!absPath) {
926 path = cwd + path;
927 }
928 }
929
AddFeatures(FeatureFlagsUnion & feature)930 bool HdcTransferBase::AddFeatures(FeatureFlagsUnion &feature)
931 {
932 feature.bits.hugeBuf = !isStableBuf;
933 feature.bits.reserveBits1 = 1;
934 return true;
935 }
936
CheckFeatures(CtxFile * context,uint8_t * payload,const int payloadSize)937 bool HdcTransferBase::CheckFeatures(CtxFile *context, uint8_t *payload, const int payloadSize)
938 {
939 if (payloadSize == FEATURE_FLAG_MAX_SIZE) {
940 union FeatureFlagsUnion feature{};
941 if (memcpy_s(&feature, sizeof(feature), payload, payloadSize) != EOK) {
942 WRITE_LOG(LOG_FATAL, "CheckFeatures memcpy_s failed");
943 return false;
944 }
945 WRITE_LOG(LOG_DEBUG, "isStableBuf:%d, hugeBuf:%d", isStableBuf, feature.bits.hugeBuf);
946 context->isStableBufSize = isStableBuf ? true : (!feature.bits.hugeBuf);
947 context->isOtherSideSandboxSupported = feature.bits.reserveBits1 > 0;
948 return true;
949 } else if (payloadSize == 0) {
950 WRITE_LOG(LOG_DEBUG, "FileBegin CheckFeatures payloadSize:%d, use default feature.", payloadSize);
951 context->isStableBufSize = true;
952 context->isOtherSideSandboxSupported = false;
953 return true;
954 } else {
955 WRITE_LOG(LOG_FATAL, "CheckFeatures payloadSize:%d", payloadSize);
956 return false;
957 }
958 }
959
CheckSandboxOptionCompatibility(const string & option,CtxFile * context)960 bool HdcTransferBase::CheckSandboxOptionCompatibility(const string &option, CtxFile *context)
961 {
962 if (option == cmdBundleName && !context->isOtherSideSandboxSupported && context->sandboxMode) {
963 const char* name = taskInfo->serverOrDaemon ? "Device ROM" : "SDK";
964 WRITE_LOG(LOG_DEBUG, "%s doesn't support %s option.", name, option.c_str());
965 LogMsg(MSG_FAIL, "[E005004] %s doesn't support %s option.", name, option.c_str());
966 OnFileOpenFailed(context);
967 TaskFinish();
968 return false;
969 }
970 return true;
971 }
972 } // namespace Hdc
973