• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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