• 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 "file.h"
16 #include "serial_struct.h"
17 
18 namespace Hdc {
HdcFile(HTaskInfo hTaskInfo)19 HdcFile::HdcFile(HTaskInfo hTaskInfo)
20     : HdcTransferBase(hTaskInfo)
21 {
22     commandBegin = CMD_FILE_BEGIN;
23     commandData = CMD_FILE_DATA;
24     isStableBuf = hTaskInfo->isStableBuf;
25 }
26 
~HdcFile()27 HdcFile::~HdcFile()
28 {
29     WRITE_LOG(LOG_DEBUG, "~HdcFile channelId:%u", taskInfo->channelId);
30 };
31 
StopTask()32 void HdcFile::StopTask()
33 {
34     WRITE_LOG(LOG_DEBUG, "StopTask channelId:%u", taskInfo->channelId);
35     singalStop = true;
36 };
37 
BeginTransfer(CtxFile * context,const string & command)38 bool HdcFile::BeginTransfer(CtxFile *context, const string &command)
39 {
40     int argc = 0;
41     bool ret = false;
42     StartTraceScope("HdcFile::BeginTransfer");
43     char **argv = Base::SplitCommandToArgs(command.c_str(), &argc);
44     if (argc < CMD_ARG1_COUNT || argv == nullptr) {
45         LogMsg(MSG_FAIL, "Transfer path split failed");
46         if (argv) {
47             delete[](reinterpret_cast<char *>(argv));
48         }
49         return false;
50     }
51     if (!SetMasterParameters(context, command.c_str(), argc, argv)) {
52         delete[](reinterpret_cast<char *>(argv));
53         return false;
54     }
55     do {
56         ++refCount;
57         uv_fs_open(loopTask, &context->fsOpenReq, context->localPath.c_str(), O_RDONLY, S_IWUSR | S_IRUSR, OnFileOpen);
58         context->master = true;
59         ret = true;
60     } while (false);
61     if (!ret) {
62         LogMsg(MSG_FAIL, "Transfer path failed, Master:%s Slave:%s", context->localPath.c_str(),
63                context->remotePath.c_str());
64     }
65     delete[](reinterpret_cast<char *>(argv));
66     return ret;
67 }
68 
SetMasterParameters(CtxFile * context,const char * command,int argc,char ** argv)69 bool HdcFile::SetMasterParameters(CtxFile *context, const char *command, int argc, char **argv)
70 {
71     int srcArgvIndex = 0;
72     string errStr;
73     const string cmdOptionTstmp = "-a";
74     const string cmdOptionSync = "-sync";
75     const string cmdOptionZip = "-z";
76     const string cmdOptionModeSync = "-m";
77 
78     for (int i = 0; i < argc; i++) {
79         if (argv[i] == cmdOptionZip) {
80             context->transferConfig.compressType = COMPRESS_LZ4;
81             ++srcArgvIndex;
82         } else if (argv[i] == cmdOptionSync) {
83             context->transferConfig.updateIfNew = true;
84             ++srcArgvIndex;
85         } else if (argv[i] == cmdOptionTstmp) {
86             // The time zone difference may cause the display time on the PC and the
87             // device to differ by several hours
88             //
89             // ls -al --full-time
90             context->transferConfig.holdTimestamp = true;
91             ++srcArgvIndex;
92         } else if (argv[i] == CMD_OPTION_CLIENTCWD) {
93             context->transferConfig.clientCwd = argv[i + 1];
94             srcArgvIndex += CMD_ARG1_COUNT;  // skip 2args
95         } else if (argv[i] == cmdOptionModeSync) {
96             context->fileModeSync = true;
97             ++srcArgvIndex;
98         } else if (argv[i] == CMDSTR_REMOTE_PARAMETER) {
99             ++srcArgvIndex;
100         } else if (argv[i][0] == '-') {
101             LogMsg(MSG_FAIL, "Unknown file option: %s", argv[i]);
102             return false;
103         }
104     }
105     if (argc == srcArgvIndex) {
106         LogMsg(MSG_FAIL, "There is no local and remote path");
107         return false;
108     }
109     context->remotePath = argv[argc - 1];
110     context->localPath = argv[argc - CMD_FILE_PENULT_PARAM];
111     if (taskInfo->serverOrDaemon) {
112         // master and server
113         if ((srcArgvIndex + 1) == argc) {
114             LogMsg(MSG_FAIL, "There is no remote path");
115             return false;
116         }
117         ExtractRelativePath(context->transferConfig.clientCwd, context->localPath);
118     } else {
119         if ((srcArgvIndex + 1) == argc) {
120             context->remotePath = ".";
121             context->localPath = argv[argc - 1];
122         }
123     }
124 
125     context->localName = Base::GetFullFilePath(context->localPath);
126 
127     mode_t mode = mode_t(~S_IFMT);
128     if (!Base::CheckDirectoryOrPath(context->localPath.c_str(), true, true, errStr, mode) && (mode & S_IFDIR)) {
129         context->isDir = true;
130         GetSubFilesRecursively(context->localPath, context->localName, &context->taskQueue);
131         if (context->taskQueue.size() == 0) {
132             LogMsg(MSG_FAIL, "Operation failed, because the source folder is empty.");
133             return false;
134         }
135         context->fileCnt = 0;
136         context->dirSize = 0;
137         context->localDirName = Base::GetPathWithoutFilename(context->localPath);
138 
139         WRITE_LOG(LOG_DEBUG, "localDirName = %s", context->localDirName.c_str());
140 
141         context->localName = context->taskQueue.back();
142         context->localPath = context->localDirName + context->localName;
143 
144         WRITE_LOG(LOG_DEBUG, "localPath = %s", context->localPath.c_str());
145         context->taskQueue.pop_back();
146     }
147     return true;
148 }
149 
CheckMaster(CtxFile * context)150 void HdcFile::CheckMaster(CtxFile *context)
151 {
152     StartTraceScope("HdcFile::CheckMaster");
153     if (context->fileModeSync) {
154         string s = SerialStruct::SerializeToString(context->fileMode);
155         SendToAnother(CMD_FILE_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
156     } else {
157         string s = SerialStruct::SerializeToString(context->transferConfig);
158         SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
159     }
160 }
161 
WhenTransferFinish(CtxFile * context)162 void HdcFile::WhenTransferFinish(CtxFile *context)
163 {
164     WRITE_LOG(LOG_DEBUG, "WhenTransferFinish fileCnt:%d", context->fileCnt);
165     uint8_t flag = 1;
166     context->fileCnt++;
167     context->dirSize += context->indexIO;
168     SendToAnother(CMD_FILE_FINISH, &flag, 1);
169 }
170 
TransferSummary(CtxFile * context)171 void HdcFile::TransferSummary(CtxFile *context)
172 {
173     uint64_t nMSec = Base::GetRuntimeMSec() -
174                      (context->fileCnt > 1 ? context->transferDirBegin : context->transferBegin);
175     uint64_t fSize = context->fileCnt > 1 ? context->dirSize : context->indexIO;
176     double fRate = static_cast<double>(fSize) / nMSec; // / /1000 * 1000 = 0
177     if (context->indexIO >= context->fileSize || context->lastErrno == 0) {
178         LogMsg(MSG_OK, "FileTransfer finish, Size:%lld, File count = %d, time:%lldms rate:%.2lfkB/s",
179                fSize, context->fileCnt, nMSec, fRate);
180     } else {
181         constexpr int bufSize = 1024;
182         char buf[bufSize] = { 0 };
183         uv_strerror_r(static_cast<int>(-context->lastErrno), buf, bufSize);
184         LogMsg(MSG_FAIL, "Transfer Stop at:%lld/%lld(Bytes), Reason: %s", context->indexIO, context->fileSize,
185                buf);
186     }
187 }
188 
FileModeSync(const uint16_t cmd,uint8_t * payload,const int payloadSize)189 bool HdcFile::FileModeSync(const uint16_t cmd, uint8_t *payload, const int payloadSize)
190 {
191     StartTraceScope("HdcFile::FileModeSync");
192     if (ctxNow.master) {
193         WRITE_LOG(LOG_DEBUG, "FileModeSync master ctxNow.fileModeSync = %d size = %zu", ctxNow.fileModeSync,
194                   ctxNow.dirMode.size());
195         if (ctxNow.dirMode.size() > 0) {
196             auto mode = ctxNow.dirMode.back();
197             WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u conext = %s",
198                 mode.fullName.c_str(), mode.perm, mode.uId, mode.gId, mode.context.c_str());
199             string s = SerialStruct::SerializeToString(mode);
200             ctxNow.dirMode.pop_back();
201             SendToAnother(CMD_DIR_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
202         } else {
203             string s = SerialStruct::SerializeToString(ctxNow.transferConfig);
204             SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
205         }
206     } else {
207         ctxNow.fileModeSync = true;
208         string serialString(reinterpret_cast<char *>(payload), payloadSize);
209         if (cmd == CMD_FILE_MODE) {
210             SerialStruct::ParseFromString(ctxNow.fileMode, serialString);
211         } else {
212             FileMode dirMode;
213             SerialStruct::ParseFromString(dirMode, serialString);
214 
215             WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u context = %s",
216                 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
217 
218             vector<string> dirsOfOptName;
219             if (dirMode.fullName.find('/') != string::npos) {
220                 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from linux system");
221                 Base::SplitString(dirMode.fullName, "/", dirsOfOptName);
222             } else if (dirMode.fullName.find('\\') != string::npos) {
223                 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from windows system");
224                 Base::SplitString(dirMode.fullName, "\\", dirsOfOptName);
225             } else {
226                 dirsOfOptName.emplace_back(dirMode.fullName);
227             }
228 
229             dirMode.fullName = "";
230             for (auto s : dirsOfOptName) {
231                 if (dirMode.fullName.empty()) {
232                     dirMode.fullName = s;
233                 } else {
234                     dirMode.fullName = dirMode.fullName + Base::GetPathSep() + s;
235                 }
236             }
237             WRITE_LOG(LOG_DEBUG, "dir = %s permissions: %o uId = %u, gId = %u context = %s",
238                 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
239             ctxNow.dirModeMap.insert(std::make_pair(dirMode.fullName, dirMode));
240         }
241         SendToAnother(CMD_FILE_MODE, nullptr, 0);
242     }
243     return true;
244 }
245 
SlaveCheck(uint8_t * payload,const int payloadSize)246 bool HdcFile::SlaveCheck(uint8_t *payload, const int payloadSize)
247 {
248     bool ret = true;
249     bool childRet = false;
250     string errStr;
251     // parse option
252     string serialString(reinterpret_cast<char *>(payload), payloadSize);
253     TransferConfig &stat = ctxNow.transferConfig;
254     SerialStruct::ParseFromString(stat, serialString);
255     ctxNow.fileSize = stat.fileSize;
256     ctxNow.localPath = stat.path;
257     ctxNow.master = false;
258     ctxNow.fsOpenReq.data = &ctxNow;
259 #ifdef HDC_DEBUG
260     WRITE_LOG(LOG_DEBUG, "HdcFile fileSize got %" PRIu64 "", ctxNow.fileSize);
261 #endif
262 
263     if (!CheckLocalPath(ctxNow.localPath, stat.optionalName, errStr)) {
264         LogMsg(MSG_FAIL, "%s", errStr.c_str());
265         return false;
266     }
267 
268     if (!CheckFilename(ctxNow.localPath, stat.optionalName, errStr)) {
269         LogMsg(MSG_FAIL, "%s", errStr.c_str());
270         return false;
271     }
272     // check path
273     childRet = SmartSlavePath(stat.clientCwd, ctxNow.localPath, stat.optionalName.c_str());
274     if (childRet && ctxNow.transferConfig.updateIfNew) {  // file exist and option need update
275         // if is newer
276         uv_fs_t fs = {};
277         uv_fs_stat(nullptr, &fs, ctxNow.localPath.c_str(), nullptr);
278         uv_fs_req_cleanup(&fs);
279         if ((uint64_t)fs.statbuf.st_mtim.tv_sec >= ctxNow.transferConfig.mtime) {
280             LogMsg(MSG_FAIL, "Target file is the same date or newer,path: %s", ctxNow.localPath.c_str());
281             return false;
282         }
283     }
284     // begin work
285     ++refCount;
286     uv_fs_open(loopTask, &ctxNow.fsOpenReq, ctxNow.localPath.c_str(), UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY,
287                S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, OnFileOpen);
288     if (ctxNow.transferDirBegin == 0) {
289         ctxNow.transferDirBegin = Base::GetRuntimeMSec();
290     }
291     ctxNow.transferBegin = Base::GetRuntimeMSec();
292     return ret;
293 }
294 
TransferNext(CtxFile * context)295 void HdcFile::TransferNext(CtxFile *context)
296 {
297     context->localName = context->taskQueue.back();
298     context->localPath = context->localDirName + context->localName;
299     context->taskQueue.pop_back();
300     WRITE_LOG(LOG_DEBUG, "TransferNext localPath = %s queuesize:%d",
301               context->localPath.c_str(), ctxNow.taskQueue.size());
302     do {
303         ++refCount;
304         uv_fs_open(loopTask, &context->fsOpenReq, context->localPath.c_str(), O_RDONLY, S_IWUSR | S_IRUSR, OnFileOpen);
305     } while (false);
306 
307     return;
308 }
309 
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)310 bool HdcFile::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
311 {
312     HdcTransferBase::CommandDispatch(command, payload, payloadSize);
313     bool ret = true;
314     StartTraceScope("HdcFile::CommandDispatch");
315     switch (command) {
316         case CMD_FILE_INIT: {  // initial
317             string s = string(reinterpret_cast<char *>(payload), payloadSize);
318             ret = BeginTransfer(&ctxNow, s);
319             ctxNow.transferBegin = Base::GetRuntimeMSec();
320             break;
321         }
322         case CMD_FILE_CHECK: {
323             ret = SlaveCheck(payload, payloadSize);
324             break;
325         }
326         case CMD_FILE_MODE:
327         case CMD_DIR_MODE: {
328             ret = FileModeSync(command, payload, payloadSize);
329             break;
330         }
331         case CMD_FILE_FINISH: {
332             if (*payload) {  // close-step3
333                 if (ctxNow.isFdOpen) {
334                     WRITE_LOG(LOG_DEBUG, "OnFileIO fs_close, localPath:%s result:%d, closeReqSubmitted:%d",
335                               ctxNow.localPath.c_str(), ctxNow.fsOpenReq.result, ctxNow.closeReqSubmitted);
336                     uv_fs_close(nullptr, &ctxNow.fsCloseReq, ctxNow.fsOpenReq.result, nullptr);
337                     // solve the fd leak caused by early exit due to illegal operation on a directory.
338                     ctxNow.isFdOpen = false;
339                 }
340                 WRITE_LOG(LOG_DEBUG, "Dir = %d taskQueue size = %d", ctxNow.isDir, ctxNow.taskQueue.size());
341                 if (ctxNow.isDir && (ctxNow.taskQueue.size() > 0)) {
342                     TransferNext(&ctxNow);
343                 } else {
344                     ctxNow.ioFinish = true;
345                     ctxNow.transferDirBegin = 0;
346                     --(*payload);
347                     SendToAnother(CMD_FILE_FINISH, payload, 1);
348                 }
349             } else {  // close-step3
350                 TransferSummary(&ctxNow);
351                 TaskFinish();
352             }
353             break;
354         }
355         default:
356             break;
357     }
358     return ret;
359 }
360 }  // namespace Hdc
361