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 18namespace Hdc { 19HdcFile::HdcFile(HTaskInfo hTaskInfo) 20 : HdcTransferBase(hTaskInfo) 21{ 22 commandBegin = CMD_FILE_BEGIN; 23 commandData = CMD_FILE_DATA; 24 isStableBuf = hTaskInfo->isStableBuf; 25} 26 27HdcFile::~HdcFile() 28{ 29 WRITE_LOG(LOG_DEBUG, "~HdcFile channelId:%u", taskInfo->channelId); 30}; 31 32void HdcFile::StopTask() 33{ 34 WRITE_LOG(LOG_DEBUG, "StopTask channelId:%u", taskInfo->channelId); 35 singalStop = true; 36}; 37 38bool 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 77bool 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 158void 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 170void 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 179void 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 197bool 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 254bool 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 309void 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 332bool 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