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