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