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