• 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     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     do {
56         ++refCount;
57         uv_fs_open(loopTask, &context->fsOpenReq, context->localPath.c_str(), O_RDONLY, S_IWUSR | S_IRUSR, OnFileOpen);
58         context->master = true;
59         ret = true;
60     } while (false);
61     if (!ret) {
62         LogMsg(MSG_FAIL, "Transfer path failed, Master:%s Slave:%s", context->localPath.c_str(),
63                context->remotePath.c_str());
64     }
65     delete[](reinterpret_cast<char *>(argv));
66     return ret;
67 }
68 
ParseMasterParameters(CtxFile * context,int argc,char ** argv,int & srcArgvIndex)69 bool HdcFile::ParseMasterParameters(CtxFile *context, int argc, char **argv, int &srcArgvIndex)
70 {
71     for (int i = 0; i < argc; i++) {
72         if (argv[i] == cmdOptionZip) {
73             context->transferConfig.compressType = COMPRESS_LZ4;
74             ++srcArgvIndex;
75         } else if (argv[i] == cmdOptionSync) {
76             context->transferConfig.updateIfNew = true;
77             ++srcArgvIndex;
78         } else if (argv[i] == cmdOptionTstmp) {
79             // The time zone difference may cause the display time on the PC and the
80             // device to differ by several hours
81             //
82             // ls -al --full-time
83             context->transferConfig.holdTimestamp = true;
84             ++srcArgvIndex;
85         } else if (argv[i] == CMD_OPTION_CLIENTCWD) {
86             context->transferConfig.clientCwd = argv[i + 1];
87             srcArgvIndex += CMD_ARG1_COUNT;  // skip 2args
88         } else if (argv[i] == cmdOptionModeSync) {
89             context->fileModeSync = true;
90             ++srcArgvIndex;
91         } else if (argv[i] == CMDSTR_REMOTE_PARAMETER) {
92             ++srcArgvIndex;
93         } else if (argv[i] == cmdBundleName) {
94             context->sandboxMode = true;
95             if (argc == srcArgvIndex + 1) {
96                 LogMsg(MSG_FAIL, "[E005003] The parameter is missing, correct your input by referring below:\n%s",
97                     taskInfo->serverOrDaemon ? "Usage: hdc file send [-b bundlename] local remote" :
98                     "Usage: hdc file recv [-b bundlename] remote local");
99                 WRITE_LOG(LOG_DEBUG, "There is no bundle name.");
100                 return false;
101             }
102             context->transferConfig.reserve1 = argv[i + 1];
103             context->bundleName = argv[i + 1];
104             srcArgvIndex += CMD_ARG1_COUNT;  // skip 2args
105         } else if (argv[i][0] == '-') {
106             LogMsg(MSG_FAIL, "Unknown file option: %s", argv[i]);
107             return false;
108         }
109     }
110 
111     return ValidateAndSetPaths(context, argc, argv, srcArgvIndex);
112 }
113 
ValidateAndSetPaths(CtxFile * context,int argc,char ** argv,int & srcArgvIndex)114 bool HdcFile::ValidateAndSetPaths(CtxFile *context, int argc, char **argv, int &srcArgvIndex)
115 {
116     if (argc == srcArgvIndex) {
117         LogMsg(MSG_FAIL, "There is no local and remote path");
118         return false;
119     }
120 
121     if (context->sandboxMode) {
122         if ((srcArgvIndex + 1) == argc) {
123             context->remotePath = ".";
124             context->localPath = argv[argc - 1];
125             context->inputLocalPath = context->localPath;
126         } else if ((srcArgvIndex + CMD_FILE_PENULT_PARAM) == argc) {
127             context->remotePath = argv[argc - 1];
128             context->localPath = argv[argc - CMD_FILE_PENULT_PARAM];
129             context->inputLocalPath = context->localPath;
130         } else {
131             context->remotePath = argv[argc - 1];
132             context->localPath = argv[argc - CMD_FILE_PENULT_PARAM];
133             context->inputLocalPath = context->localPath;
134         }
135     } else {
136         context->remotePath = argv[argc - 1];
137         context->localPath = argv[argc - CMD_FILE_PENULT_PARAM];
138         context->inputLocalPath = context->localPath;
139     }
140     return true;
141 }
142 
CheckSandboxSubPath(CtxFile * context,string & resolvedPath)143 bool HdcFile::CheckSandboxSubPath(CtxFile *context, string &resolvedPath)
144 {
145     string fullPath = SANDBOX_ROOT_DIR + context->bundleName;
146     string appDir(fullPath);
147     appDir = Base::CanonicalizeSpecPath(appDir);
148     fullPath = fullPath + Base::GetPathSep() + context->inputLocalPath;
149     // remove the postfix char '/', make sure that the method Base::GetPathWithoutFilename
150     // returns a path without the last dir node name.
151     // if input local path ends with "../" likes "data/storage/el1/base/../../../../", the final value
152     // of fullpath will be the parent dir of the expected path. it won't be unexcepted, so correct it.
153     const int lastTwoIndex = 2;
154     while (fullPath.back() == Base::GetPathSep() && fullPath[fullPath.size() - lastTwoIndex] != '.') {
155         fullPath.pop_back();
156     }
157     fullPath = Base::GetPathWithoutFilename(fullPath);
158     resolvedPath = Base::CanonicalizeSpecPath(fullPath);
159     if (resolvedPath.size() <= 0 ||
160         strncmp(resolvedPath.c_str(), appDir.c_str(), appDir.size()) != 0) {
161         LogMsg(MSG_FAIL, "[E005102] Remote path: %s is invalid, no such file/directory or it's out of "
162             "the application directory.", context->inputLocalPath.c_str());
163         WRITE_LOG(LOG_DEBUG, "Invalid path:%s, fullpath:%s, resolvedPath:%s, errno:%d",
164             context->inputLocalPath.c_str(), fullPath.c_str(), resolvedPath.c_str(), errno);
165         return false;
166     }
167     return true;
168 }
169 
SetMasterParameters(CtxFile * context,const char * command,int argc,char ** argv)170 bool HdcFile::SetMasterParameters(CtxFile *context, const char *command, int argc, char **argv)
171 {
172     int srcArgvIndex = 0;
173     string errStr;
174     if (!ParseMasterParameters(context, argc, argv, srcArgvIndex)) {
175         return false;
176     }
177 
178     if (taskInfo->serverOrDaemon) {
179         // master and server
180         if ((srcArgvIndex + 1) == argc) {
181             LogMsg(MSG_FAIL, "There is no remote path");
182             return false;
183         }
184         ExtractRelativePath(context->transferConfig.clientCwd, context->localPath);
185     } else if (!SetMasterParametersOnDaemon(context, argc, argv, srcArgvIndex)) {
186         return false;
187     }
188     context->localName = Base::GetFullFilePath(context->localPath);
189     mode_t mode = mode_t(~S_IFMT);
190     if (!Base::CheckDirectoryOrPath(context->localPath.c_str(), true, true, errStr, mode) && (mode & S_IFDIR)) {
191         context->isDir = true;
192         GetSubFilesRecursively(context->localPath, context->localName, &context->taskQueue);
193         if (context->taskQueue.size() == 0) {
194             LogMsg(MSG_FAIL, "Operation failed, because the source folder is empty.");
195             return false;
196         }
197         context->fileCnt = 0;
198         context->dirSize = 0;
199         context->localDirName = Base::GetPathWithoutFilename(context->localPath);
200 
201         context->localName = context->taskQueue.back();
202         context->localPath = context->localDirName + context->localName;
203 
204         context->taskQueue.pop_back();
205     }
206     return true;
207 }
208 
SetMasterParametersOnDaemon(CtxFile * context,int argc,char ** argv,int srcArgvIndex)209 bool HdcFile::SetMasterParametersOnDaemon(CtxFile *context, int argc, char **argv, int srcArgvIndex)
210 {
211     if (context->sandboxMode &&
212         context->transferConfig.reserve1.size() > 0 &&
213         !IsValidBundlePath(context->transferConfig.reserve1)) {
214         LogMsg(MSG_FAIL, "[E005101] Invalid bundle name: %s",
215             context->transferConfig.reserve1.c_str());
216         WRITE_LOG(LOG_DEBUG, "SetMasterParameters invalid bundleName:%s",
217             context->transferConfig.reserve1.c_str());
218         return false;
219     }
220     if ((srcArgvIndex + 1) == argc) {
221         context->remotePath = ".";
222         context->localPath = argv[argc - 1];
223     }
224     context->localName = Base::GetFullFilePath(context->localPath);
225 
226     if (context->sandboxMode && IsValidBundlePath(context->transferConfig.reserve1)) {
227         string resolvedPath;
228         if (CheckSandboxSubPath(context, resolvedPath)) {
229             context->localPath = resolvedPath + Base::GetPathSep() + context->localName;
230         } else {
231             WRITE_LOG(LOG_WARN, "SetMasterParameters, CheckSandboxSubPath false.");
232             return false;
233         }
234     }
235     return true;
236 }
237 
CheckMaster(CtxFile * context)238 void HdcFile::CheckMaster(CtxFile *context)
239 {
240     StartTraceScope("HdcFile::CheckMaster");
241     if (context->fileModeSync) {
242         string s = SerialStruct::SerializeToString(context->fileMode);
243         SendToAnother(CMD_FILE_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
244     } else {
245         string s = SerialStruct::SerializeToString(context->transferConfig);
246         SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
247     }
248 }
249 
WhenTransferFinish(CtxFile * context)250 void HdcFile::WhenTransferFinish(CtxFile *context)
251 {
252     WRITE_LOG(LOG_DEBUG, "WhenTransferFinish fileCnt:%d", context->fileCnt);
253     uint8_t flag = 1;
254     context->fileCnt++;
255     context->dirSize += context->indexIO;
256     SendToAnother(CMD_FILE_FINISH, &flag, 1);
257 }
258 
TransferSummary(CtxFile * context)259 void HdcFile::TransferSummary(CtxFile *context)
260 {
261     uint64_t nMSec = Base::GetRuntimeMSec() -
262                      (context->fileCnt > 1 ? context->transferDirBegin : context->transferBegin);
263     uint64_t fSize = context->fileCnt > 1 ? context->dirSize : context->indexIO;
264     double fRate = static_cast<double>(fSize) / nMSec; // / /1000 * 1000 = 0
265     if (context->indexIO >= context->fileSize || context->lastErrno == 0) {
266         LogMsg(MSG_OK, "FileTransfer finish, Size:%lld, File count = %d, time:%lldms rate:%.2lfkB/s",
267                fSize, context->fileCnt, nMSec, fRate);
268     } else {
269         constexpr int bufSize = 1024;
270         char buf[bufSize] = { 0 };
271         uv_strerror_r(static_cast<int>(-context->lastErrno), buf, bufSize);
272         LogMsg(MSG_FAIL, "Transfer Stop at:%lld/%lld(Bytes), Reason: %s", context->indexIO, context->fileSize,
273                buf);
274     }
275 }
276 
FileModeSync(const uint16_t cmd,uint8_t * payload,const int payloadSize)277 bool HdcFile::FileModeSync(const uint16_t cmd, uint8_t *payload, const int payloadSize)
278 {
279     StartTraceScope("HdcFile::FileModeSync");
280     if (ctxNow.master) {
281         WRITE_LOG(LOG_DEBUG, "FileModeSync master ctxNow.fileModeSync = %d size = %zu", ctxNow.fileModeSync,
282                   ctxNow.dirMode.size());
283         if (ctxNow.dirMode.size() > 0) {
284             auto mode = ctxNow.dirMode.back();
285             WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u conext = %s",
286                 mode.fullName.c_str(), mode.perm, mode.uId, mode.gId, mode.context.c_str());
287             string s = SerialStruct::SerializeToString(mode);
288             ctxNow.dirMode.pop_back();
289             SendToAnother(CMD_DIR_MODE, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
290         } else {
291             string s = SerialStruct::SerializeToString(ctxNow.transferConfig);
292             SendToAnother(CMD_FILE_CHECK, reinterpret_cast<uint8_t *>(const_cast<char *>(s.c_str())), s.size());
293         }
294     } else {
295         ctxNow.fileModeSync = true;
296         string serialString(reinterpret_cast<char *>(payload), payloadSize);
297         if (cmd == CMD_FILE_MODE) {
298             SerialStruct::ParseFromString(ctxNow.fileMode, serialString);
299         } else {
300             FileMode dirMode;
301             SerialStruct::ParseFromString(dirMode, serialString);
302 
303             WRITE_LOG(LOG_DEBUG, "file = %s permissions: %o uId = %u, gId = %u context = %s",
304                 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
305 
306             vector<string> dirsOfOptName;
307             if (dirMode.fullName.find('/') != string::npos) {
308                 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from linux system");
309                 Base::SplitString(dirMode.fullName, "/", dirsOfOptName);
310             } else if (dirMode.fullName.find('\\') != string::npos) {
311                 WRITE_LOG(LOG_DEBUG, "dir mode create parent dir from windows system");
312                 Base::SplitString(dirMode.fullName, "\\", dirsOfOptName);
313             } else {
314                 dirsOfOptName.emplace_back(dirMode.fullName);
315             }
316 
317             dirMode.fullName = "";
318             for (auto s : dirsOfOptName) {
319                 if (dirMode.fullName.empty()) {
320                     dirMode.fullName = s;
321                 } else {
322                     dirMode.fullName = dirMode.fullName + Base::GetPathSep() + s;
323                 }
324             }
325             WRITE_LOG(LOG_DEBUG, "dir = %s permissions: %o uId = %u, gId = %u context = %s",
326                 dirMode.fullName.c_str(), dirMode.perm, dirMode.uId, dirMode.gId, dirMode.context.c_str());
327             ctxNow.dirModeMap.insert(std::make_pair(dirMode.fullName, dirMode));
328         }
329         SendToAnother(CMD_FILE_MODE, nullptr, 0);
330     }
331     return true;
332 }
333 
SlaveCheck(uint8_t * payload,const int payloadSize)334 bool HdcFile::SlaveCheck(uint8_t *payload, const int payloadSize)
335 {
336     if (!ParseAndValidateOptions(payload, payloadSize)) {
337         return false;
338     }
339 
340     if (!CheckBundleAndPath()) {
341         return false;
342     }
343 
344     if (!CheckLocalPathAndFilename()) {
345         return false;
346     }
347 
348     if (!HandleFileExistenceAndNewness()) {
349         return false;
350     }
351 
352     return BeginFileOperations();
353 }
354 
ParseAndValidateOptions(uint8_t * payload,const int payloadSize)355 bool HdcFile::ParseAndValidateOptions(uint8_t *payload, const int payloadSize)
356 {
357     string serialString(reinterpret_cast<char *>(payload), payloadSize);
358     TransferConfig &stat = ctxNow.transferConfig;
359     if (!SerialStruct::ParseFromString(stat, serialString)) {
360         WRITE_LOG(LOG_DEBUG, "ParseFromString failed, serialString: %s", serialString.c_str());
361         return false;
362     }
363     ctxNow.fileSize = stat.fileSize;
364     ctxNow.localPath = stat.path;
365     ctxNow.inputLocalPath = ctxNow.localPath;
366     ctxNow.master = false;
367     ctxNow.fsOpenReq.data = &ctxNow;
368     ctxNow.bundleName = stat.reserve1;
369 
370 #ifdef HDC_DEBUG
371     WRITE_LOG(LOG_DEBUG, "HdcFile fileSize got %" PRIu64 "", ctxNow.fileSize);
372 #endif
373 
374     return true;
375 }
376 
CheckBundleAndPath()377 bool HdcFile::CheckBundleAndPath()
378 {
379     if (!taskInfo->serverOrDaemon && IsValidBundlePath(ctxNow.bundleName)) {
380         string fullPath = SANDBOX_ROOT_DIR + ctxNow.bundleName + Base::GetPathSep();
381         fullPath.append(ctxNow.inputLocalPath);
382         ctxNow.localPath = fullPath;
383 
384         string resolvedPath;
385         if (!CheckSandboxSubPath(&ctxNow, resolvedPath)) {
386             WRITE_LOG(LOG_DEBUG, "SlaveCheck CheckSandboxSubPath false.");
387             return false;
388         }
389     } else if (!taskInfo->serverOrDaemon && ctxNow.bundleName.size() > 0) {
390         LogMsg(MSG_FAIL, "[E005101] Invalid bundle name: %s",
391             ctxNow.bundleName.c_str());
392         WRITE_LOG(LOG_DEBUG, "Invalid bundle name: %s", ctxNow.bundleName.c_str());
393         return false;
394     }
395     return true;
396 }
397 
CheckLocalPathAndFilename()398 bool HdcFile::CheckLocalPathAndFilename()
399 {
400     string errStr;
401     if (!CheckLocalPath(ctxNow.localPath, ctxNow.transferConfig.optionalName, errStr)) {
402         RemoveSandboxRootPath(errStr, ctxNow.transferConfig.reserve1);
403         LogMsg(MSG_FAIL, "%s", errStr.c_str());
404         WRITE_LOG(LOG_DEBUG, "SlaveCheck CheckLocalPath error:%s", errStr.c_str());
405         return false;
406     }
407 
408     if (!CheckFilename(ctxNow.localPath, ctxNow.transferConfig.optionalName, errStr)) {
409         RemoveSandboxRootPath(errStr, ctxNow.transferConfig.reserve1);
410         LogMsg(MSG_FAIL, "%s", errStr.c_str());
411         WRITE_LOG(LOG_DEBUG, "SlaveCheck CheckFilename error:%s", errStr.c_str());
412         return false;
413     }
414     return true;
415 }
416 
HandleFileExistenceAndNewness()417 bool HdcFile::HandleFileExistenceAndNewness()
418 {
419     bool childRet = SmartSlavePath(ctxNow.transferConfig.clientCwd, ctxNow.localPath,
420         ctxNow.transferConfig.optionalName.c_str());
421     if (childRet && ctxNow.transferConfig.updateIfNew) {  // file exist and option need update
422         uv_fs_t fs = {};
423         uv_fs_stat(nullptr, &fs, ctxNow.localPath.c_str(), nullptr);
424         uv_fs_req_cleanup(&fs);
425         if ((uint64_t)fs.statbuf.st_mtim.tv_sec >= ctxNow.transferConfig.mtime) {
426             LogMsg(MSG_FAIL, "Target file is the same date or newer,path: %s", ctxNow.localPath.c_str());
427             return false;
428         }
429     }
430     return true;
431 }
432 
BeginFileOperations()433 bool HdcFile::BeginFileOperations()
434 {
435     uv_fs_t *openReq = new uv_fs_t;
436     if (openReq == nullptr) {
437         LogMsg(MSG_FAIL, "HdcFile::SlaveCheck new openReq failed");
438         return false;
439     }
440     memset_s(openReq, sizeof(uv_fs_t), 0, sizeof(uv_fs_t));
441     openReq->data = &ctxNow;
442     ++refCount;
443     uv_fs_open(loopTask, &ctxNow.fsOpenReq, ctxNow.localPath.c_str(), UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY,
444                S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, OnFileOpen);
445     if (ctxNow.transferDirBegin == 0) {
446         ctxNow.transferDirBegin = Base::GetRuntimeMSec();
447     }
448     ctxNow.transferBegin = Base::GetRuntimeMSec();
449     return true;
450 }
451 
TransferNext(CtxFile * context)452 void HdcFile::TransferNext(CtxFile *context)
453 {
454     context->localName = context->taskQueue.back();
455     context->localPath = context->localDirName + context->localName;
456     context->taskQueue.pop_back();
457     WRITE_LOG(LOG_DEBUG, "TransferNext localPath = %s queuesize:%d",
458               context->localPath.c_str(), ctxNow.taskQueue.size());
459     do {
460         ++refCount;
461         uv_fs_open(loopTask, &context->fsOpenReq, context->localPath.c_str(), O_RDONLY, S_IWUSR | S_IRUSR, OnFileOpen);
462     } while (false);
463 
464     return;
465 }
466 
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)467 bool HdcFile::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
468 {
469     HdcTransferBase::CommandDispatch(command, payload, payloadSize);
470     bool ret = true;
471     StartTraceScope("HdcFile::CommandDispatch");
472     switch (command) {
473         case CMD_FILE_INIT: {  // initial
474             string s = string(reinterpret_cast<char *>(payload), payloadSize);
475             ret = BeginTransfer(&ctxNow, s);
476             ctxNow.transferBegin = Base::GetRuntimeMSec();
477             break;
478         }
479         case CMD_FILE_CHECK: {
480             ret = SlaveCheck(payload, payloadSize);
481             break;
482         }
483         case CMD_FILE_MODE:
484         case CMD_DIR_MODE: {
485             ret = FileModeSync(command, payload, payloadSize);
486             break;
487         }
488         case CMD_FILE_FINISH: {
489             if (*payload) {  // close-step3
490                 if (ctxNow.isFdOpen) {
491                     WRITE_LOG(LOG_DEBUG, "OnFileIO fs_close, localPath:%s result:%d, closeReqSubmitted:%d",
492                               ctxNow.localPath.c_str(), ctxNow.fsOpenReq.result, ctxNow.closeReqSubmitted);
493                     uv_fs_close(nullptr, &ctxNow.fsCloseReq, ctxNow.fsOpenReq.result, nullptr);
494                     // solve the fd leak caused by early exit due to illegal operation on a directory.
495                     ctxNow.isFdOpen = false;
496                 }
497                 WRITE_LOG(LOG_DEBUG, "Dir = %d taskQueue size = %d", ctxNow.isDir, ctxNow.taskQueue.size());
498                 if (ctxNow.isDir && (ctxNow.taskQueue.size() > 0)) {
499                     TransferNext(&ctxNow);
500                 } else {
501                     ctxNow.ioFinish = true;
502                     ctxNow.transferDirBegin = 0;
503                     --(*payload);
504                     SendToAnother(CMD_FILE_FINISH, payload, 1);
505                 }
506             } else {  // close-step3
507                 TransferSummary(&ctxNow);
508                 TaskFinish();
509             }
510             break;
511         }
512         default:
513             break;
514     }
515     return ret;
516 }
517 }  // namespace Hdc
518