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 channelId:%u", taskInfo->channelId);
29 };
30
StopTask()31 void HdcFile::StopTask()
32 {
33 WRITE_LOG(LOG_DEBUG, "StopTask channelId:%u", taskInfo->channelId);
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 - CMD_FILE_PENULT_PARAM];
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, "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, "localPath = %s", context->localPath.c_str());
144 context->taskQueue.pop_back();
145 }
146 return true;
147 }
148
CheckMaster(CtxFile * context)149 void HdcFile::CheckMaster(CtxFile *context)
150 {
151 StartTraceScope("HdcFile::CheckMaster");
152 if (context->fileModeSync) {
153 string s = SerialStruct::SerializeToString(context->fileMode);
154 SendToAnother(CMD_FILE_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
155 } else {
156 string s = SerialStruct::SerializeToString(context->transferConfig);
157 SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
158 }
159 }
160
WhenTransferFinish(CtxFile * context)161 void HdcFile::WhenTransferFinish(CtxFile *context)
162 {
163 WRITE_LOG(LOG_DEBUG, "WhenTransferFinish fileCnt:%d", context->fileCnt);
164 uint8_t flag = 1;
165 context->fileCnt++;
166 context->dirSize += context->indexIO;
167 SendToAnother(CMD_FILE_FINISH, &flag, 1);
168 }
169
TransferSummary(CtxFile * context)170 void HdcFile::TransferSummary(CtxFile *context)
171 {
172 uint64_t nMSec = Base::GetRuntimeMSec() -
173 (context->fileCnt > 1 ? context->transferDirBegin : context->transferBegin);
174 uint64_t fSize = context->fileCnt > 1 ? context->dirSize : context->indexIO;
175 double fRate = static_cast<double>(fSize) / nMSec; // / /1000 * 1000 = 0
176 if (context->indexIO >= context->fileSize) {
177 LogMsg(MSG_OK, "FileTransfer finish, Size:%lld, File count = %d, time:%lldms rate:%.2lfkB/s",
178 fSize, context->fileCnt, nMSec, fRate);
179 } else {
180 constexpr int bufSize = 1024;
181 char buf[bufSize] = { 0 };
182 uv_strerror_r(static_cast<int>(-context->lastErrno), buf, bufSize);
183 LogMsg(MSG_FAIL, "Transfer Stop at:%lld/%lld(Bytes), Reason: %s", context->indexIO, context->fileSize,
184 buf);
185 }
186 }
187
FileModeSync(const uint16_t cmd,uint8_t * payload,const int payloadSize)188 bool HdcFile::FileModeSync(const uint16_t cmd, uint8_t *payload, const int payloadSize)
189 {
190 StartTraceScope("HdcFile::FileModeSync");
191 if (ctxNow.master) {
192 WRITE_LOG(LOG_DEBUG, "FileModeSync master ctxNow.fileModeSync = %d size = %zu", ctxNow.fileModeSync,
193 ctxNow.dirMode.size());
194 if (ctxNow.dirMode.size() > 0) {
195 auto mode = ctxNow.dirMode.back();
196 WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u conext = %s",
197 mode.fullName.c_str(), mode.perm, mode.uId, mode.gId, mode.context.c_str());
198 string s = SerialStruct::SerializeToString(mode);
199 ctxNow.dirMode.pop_back();
200 SendToAnother(CMD_DIR_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
201 } else {
202 string s = SerialStruct::SerializeToString(ctxNow.transferConfig);
203 SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
204 }
205 } else {
206 ctxNow.fileModeSync = true;
207 string serialString(reinterpret_cast<char *>(payload), payloadSize);
208 if (cmd == CMD_FILE_MODE) {
209 SerialStruct::ParseFromString(ctxNow.fileMode, serialString);
210 } else {
211 FileMode dirMode;
212 SerialStruct::ParseFromString(dirMode, serialString);
213
214 WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u context = %s",
215 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
216
217 vector<string> dirsOfOptName;
218 if (dirMode.fullName.find('/') != string::npos) {
219 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from linux system");
220 Base::SplitString(dirMode.fullName, "/", dirsOfOptName);
221 } else if (dirMode.fullName.find('\\') != string::npos) {
222 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from windows system");
223 Base::SplitString(dirMode.fullName, "\\", dirsOfOptName);
224 } else {
225 dirsOfOptName.emplace_back(dirMode.fullName);
226 }
227
228 dirMode.fullName = "";
229 for (auto s : dirsOfOptName) {
230 if (dirMode.fullName.empty()) {
231 dirMode.fullName = s;
232 } else {
233 dirMode.fullName = dirMode.fullName + Base::GetPathSep() + s;
234 }
235 }
236 WRITE_LOG(LOG_DEBUG, "dir = %s permissions: %o uId = %u, gId = %u context = %s",
237 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
238 ctxNow.dirModeMap.insert(std::make_pair(dirMode.fullName, dirMode));
239 }
240 SendToAnother(CMD_FILE_MODE, nullptr, 0);
241 }
242 return true;
243 }
244
SlaveCheck(uint8_t * payload,const int payloadSize)245 bool HdcFile::SlaveCheck(uint8_t *payload, const int payloadSize)
246 {
247 bool ret = true;
248 bool childRet = false;
249 string errStr;
250 // parse option
251 string serialString(reinterpret_cast<char *>(payload), payloadSize);
252 TransferConfig &stat = ctxNow.transferConfig;
253 SerialStruct::ParseFromString(stat, serialString);
254 ctxNow.fileSize = stat.fileSize;
255 ctxNow.localPath = stat.path;
256 ctxNow.master = false;
257 ctxNow.fsOpenReq.data = &ctxNow;
258 #ifdef HDC_DEBUG
259 WRITE_LOG(LOG_DEBUG, "HdcFile fileSize got %" PRIu64 "", ctxNow.fileSize);
260 #endif
261
262 if (!CheckLocalPath(ctxNow.localPath, stat.optionalName, errStr)) {
263 LogMsg(MSG_FAIL, "%s", errStr.c_str());
264 return false;
265 }
266
267 if (!CheckFilename(ctxNow.localPath, stat.optionalName, errStr)) {
268 LogMsg(MSG_FAIL, "%s", errStr.c_str());
269 return false;
270 }
271 // check path
272 childRet = SmartSlavePath(stat.clientCwd, ctxNow.localPath, stat.optionalName.c_str());
273 if (childRet && ctxNow.transferConfig.updateIfNew) { // file exist and option need update
274 // if is newer
275 uv_fs_t fs = {};
276 uv_fs_stat(nullptr, &fs, ctxNow.localPath.c_str(), nullptr);
277 uv_fs_req_cleanup(&fs);
278 if ((uint64_t)fs.statbuf.st_mtim.tv_sec >= ctxNow.transferConfig.mtime) {
279 LogMsg(MSG_FAIL, "Target file is the same date or newer,path: %s", ctxNow.localPath.c_str());
280 return false;
281 }
282 }
283 // begin work
284 ++refCount;
285 uv_fs_open(loopTask, &ctxNow.fsOpenReq, ctxNow.localPath.c_str(), UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY,
286 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, OnFileOpen);
287 if (ctxNow.transferDirBegin == 0) {
288 ctxNow.transferDirBegin = Base::GetRuntimeMSec();
289 }
290 ctxNow.transferBegin = Base::GetRuntimeMSec();
291 return ret;
292 }
293
TransferNext(CtxFile * context)294 void HdcFile::TransferNext(CtxFile *context)
295 {
296 context->localName = context->taskQueue.back();
297 context->localPath = context->localDirName + context->localName;
298 context->taskQueue.pop_back();
299 WRITE_LOG(LOG_DEBUG, "TransferNext localPath = %s queuesize:%d",
300 context->localPath.c_str(), ctxNow.taskQueue.size());
301 do {
302 ++refCount;
303 uv_fs_open(loopTask, &context->fsOpenReq, context->localPath.c_str(), O_RDONLY, S_IWUSR | S_IRUSR, OnFileOpen);
304 } while (false);
305
306 return;
307 }
308
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)309 bool HdcFile::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
310 {
311 HdcTransferBase::CommandDispatch(command, payload, payloadSize);
312 bool ret = true;
313 StartTraceScope("HdcFile::CommandDispatch");
314 switch (command) {
315 case CMD_FILE_INIT: { // initial
316 string s = string(reinterpret_cast<char *>(payload), payloadSize);
317 ret = BeginTransfer(&ctxNow, s);
318 ctxNow.transferBegin = Base::GetRuntimeMSec();
319 break;
320 }
321 case CMD_FILE_CHECK: {
322 ret = SlaveCheck(payload, payloadSize);
323 break;
324 }
325 case CMD_FILE_MODE:
326 case CMD_DIR_MODE: {
327 ret = FileModeSync(command, payload, payloadSize);
328 break;
329 }
330 case CMD_FILE_FINISH: {
331 if (*payload) { // close-step3
332 WRITE_LOG(LOG_DEBUG, "Dir = %d taskQueue size = %d", ctxNow.isDir, ctxNow.taskQueue.size());
333 if (ctxNow.isDir && (ctxNow.taskQueue.size() > 0)) {
334 TransferNext(&ctxNow);
335 } else {
336 ctxNow.ioFinish = true;
337 ctxNow.transferDirBegin = 0;
338 --(*payload);
339 SendToAnother(CMD_FILE_FINISH, payload, 1);
340 }
341 } else { // close-step3
342 TransferSummary(&ctxNow);
343 TaskFinish();
344 }
345 break;
346 }
347 default:
348 break;
349 }
350 return ret;
351 }
352 } // namespace Hdc
353