xref: /developtools/hdc/src/common/file.cpp (revision cc290419)
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