1/*
2 * Copyright (c) 2024 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
16#include "copy.h"
17#include <fcntl.h>
18#include <filesystem>
19#include <poll.h>
20#include <sys/eventfd.h>
21#include <sys/inotify.h>
22#include <sys/prctl.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <thread>
26#include "datashare_helper.h"
27#include "file_uri.h"
28#include "file_utils.h"
29#include "iservice_registry.h"
30#include "translistener.h"
31
32namespace OHOS {
33namespace CJSystemapi {
34using namespace FileFs;
35namespace fs = std::filesystem;
36const std::string NETWORK_PARA = "?networkid=";
37const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
38constexpr int DISMATCH = 0;
39constexpr int MATCH = 1;
40constexpr int BUF_SIZE = 1024;
41constexpr size_t MAX_SIZE = 1024 * 1024 * 128;
42constexpr std::chrono::milliseconds NOTIFY_PROGRESS_DELAY(300);
43std::recursive_mutex CopyImpl::mutex_;
44std::map<FileInfos, std::shared_ptr<CjCallbackObject>> CopyImpl::cjCbMap_;
45
46static int OpenSrcFile(const std::string &srcPth, std::shared_ptr<FileInfos> infos, int32_t &srcFd)
47{
48    Uri uri(infos->srcUri);
49    if (uri.GetAuthority() == "media") {
50        sptr<FileIoToken> remote = new (std::nothrow) IRemoteStub<FileIoToken>();
51        if (!remote) {
52            LOGE("Failed to get remote object");
53            return ENOMEM;
54        }
55        auto dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
56        if (!dataShareHelper) {
57            LOGE("Failed to connect to datashare");
58            return E_PERMISSION;
59        }
60        srcFd = dataShareHelper->OpenFile(uri, CommonFunc::GetModeFromFlags(O_RDONLY));
61        if (srcFd < 0) {
62            LOGE("Open media uri by data share fail. ret = %{public}d", srcFd);
63            return EPERM;
64        }
65    } else {
66        srcFd = open(srcPth.c_str(), O_RDONLY);
67        if (srcFd < 0) {
68            LOGE("Error opening src file descriptor. errno = %{public}d", errno);
69            return errno;
70        }
71    }
72    return ERRNO_NOERR;
73}
74
75static int SendFileCore(std::unique_ptr<DistributedFS::FDGuard> srcFdg,
76                        std::unique_ptr<DistributedFS::FDGuard> destFdg,
77                        std::shared_ptr<FileInfos> infos)
78{
79    std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> sendFileReq = {
80        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
81    if (sendFileReq == nullptr) {
82        LOGE("Failed to request heap memory.");
83        return ENOMEM;
84    }
85    int64_t offset = 0;
86    struct stat srcStat{};
87    if (fstat(srcFdg->GetFD(), &srcStat) < 0) {
88        LOGE("Failed to get stat of file by fd: %{public}d ,errno = %{public}d", srcFdg->GetFD(), errno);
89        return errno;
90    }
91    int32_t ret = 0;
92    int64_t size = static_cast<int64_t>(srcStat.st_size);
93    while (size >= 0) {
94        ret = uv_fs_sendfile(nullptr, sendFileReq.get(), destFdg->GetFD(), srcFdg->GetFD(),
95            offset, MAX_SIZE, nullptr);
96        if (ret < 0) {
97            LOGE("Failed to sendfile by errno : %{public}d", errno);
98            return errno;
99        }
100        if (infos != nullptr && infos->taskSignal != nullptr) {
101            if (infos->taskSignal->CheckCancelIfNeed(infos->srcPath)) {
102                return ECANCELED;
103            }
104        }
105        offset += static_cast<int64_t>(ret);
106        size -= static_cast<int64_t>(ret);
107        if (ret == 0) {
108            break;
109        }
110    }
111    if (size != 0) {
112        LOGE("The execution of the sendfile task was terminated, remaining file size %{public}" PRIu64, size);
113        return EIO;
114    }
115    return ERRNO_NOERR;
116}
117
118static int FilterFunc(const struct dirent *filename)
119{
120    if (std::string_view(filename->d_name) == "." || std::string_view(filename->d_name) == "..") {
121        return DISMATCH;
122    }
123    return MATCH;
124}
125
126struct NameList {
127    struct dirent **namelist = { nullptr };
128    int direntNum = 0;
129};
130
131static void Deleter(struct NameList *arg)
132{
133    for (int i = 0; i < arg->direntNum; i++) {
134        free((arg->namelist)[i]);
135        (arg->namelist)[i] = nullptr;
136    }
137    free(arg->namelist);
138}
139
140std::string CopyImpl::GetRealPath(const std::string& path)
141{
142    fs::path tempPath(path);
143    fs::path realPath{};
144    for (const auto& component : tempPath) {
145        if (component == ".") {
146            continue;
147        } else if (component == "..") {
148            realPath = realPath.parent_path();
149        } else {
150            realPath /= component;
151        }
152    }
153    return realPath.string();
154}
155
156bool CopyImpl::IsFile(const std::string &path)
157{
158    struct stat buf {};
159    int ret = stat(path.c_str(), &buf);
160    if (ret == -1) {
161        LOGI("stat failed, errno is %{public}d, ", errno);
162        return false;
163    }
164    return (buf.st_mode & S_IFMT) == S_IFREG;
165}
166
167std::tuple<int, uint64_t> CopyImpl::GetFileSize(const std::string &path)
168{
169    struct stat buf {};
170    int ret = stat(path.c_str(), &buf);
171    if (ret == -1) {
172        LOGI("Stat failed.");
173        return { errno, 0 };
174    }
175    return { ERRNO_NOERR, buf.st_size };
176}
177
178bool CopyImpl::CheckFileValid(const std::string &filePath, std::shared_ptr<FileInfos> infos)
179{
180    return infos->filePaths.count(filePath) != 0;
181}
182
183int CopyImpl::UpdateProgressSize(const std::string &filePath,
184    std::shared_ptr<ReceiveInfo> receivedInfo, std::shared_ptr<CjCallbackObject> callback)
185{
186    auto [err, fileSize] = GetFileSize(filePath);
187    if (err != ERRNO_NOERR) {
188        LOGE("GetFileSize failed, err: %{public}d.", err);
189        return err;
190    }
191    auto size = fileSize;
192    auto iter = receivedInfo->fileList.find(filePath);
193    if (iter == receivedInfo->fileList.end()) {
194        receivedInfo->fileList.insert({ filePath, size });
195        callback->progressSize += size;
196    } else { // file
197        if (size > iter->second) {
198            callback->progressSize += (size - iter->second);
199            iter->second = size;
200        }
201    }
202    return ERRNO_NOERR;
203}
204
205void CopyImpl::CheckOrCreatePath(const std::string &destPath)
206{
207    if (!std::filesystem::exists(destPath)) {
208        LOGI("destPath not exist, destPath = %{public}s", destPath.c_str());
209        auto file = open(destPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
210        if (file < 0) {
211            LOGE("Error opening file descriptor. errno = %{public}d", errno);
212        }
213        close(file);
214    }
215}
216
217int CopyImpl::MakeDir(const std::string &path)
218{
219    std::filesystem::path destDir(path);
220    std::error_code errCode;
221    if (!std::filesystem::create_directory(destDir, errCode)) {
222        LOGE("Failed to create directory, error code: %{public}d", errCode.value());
223        return errCode.value();
224    }
225    return ERRNO_NOERR;
226}
227
228int CopyImpl::CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)
229{
230    if (!std::filesystem::exists(destPath)) {
231        int res = MakeDir(destPath);
232        if (res != ERRNO_NOERR) {
233            LOGE("Failed to mkdir");
234            return res;
235        }
236    }
237    uint32_t watchEvents = IN_MODIFY;
238    if (infos->notifyFd >= 0) {
239        int newWd = inotify_add_watch(infos->notifyFd, destPath.c_str(), watchEvents);
240        if (newWd < 0) {
241            LOGE("inotify_add_watch, newWd is unvaild, newWd = %{public}d", newWd);
242            return errno;
243        }
244        {
245            std::lock_guard<std::recursive_mutex> lock(CopyImpl::mutex_);
246            auto iter = CopyImpl::cjCbMap_.find(*infos);
247            auto receiveInfo = FileManagement::CreateSharedPtr<ReceiveInfo>();
248            if (receiveInfo == nullptr) {
249                LOGE("Failed to request heap memory.");
250                return ENOMEM;
251            }
252            receiveInfo->path = destPath;
253            if (iter == CopyImpl::cjCbMap_.end() || iter->second == nullptr) {
254                LOGE("Failed to find infos, srcPath = %{public}s, destPath = %{public}s", infos->srcPath.c_str(),
255                    infos->destPath.c_str());
256                return UNKNOWN_ERR;
257            }
258            iter->second->wds.push_back({ newWd, receiveInfo });
259        }
260    }
261    return RecurCopyDir(srcPath, destPath, infos);
262}
263
264int CopyImpl::RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)
265{
266    std::unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (std::nothrow) struct NameList, Deleter };
267    if (pNameList == nullptr) {
268        LOGE("Failed to request heap memory.");
269        return ENOMEM;
270    }
271    int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
272    pNameList->direntNum = num;
273
274    for (int i = 0; i < num; i++) {
275        std::string src = srcPath + '/' + std::string((pNameList->namelist[i])->d_name);
276        std::string dest = destPath + '/' + std::string((pNameList->namelist[i])->d_name);
277        if ((pNameList->namelist[i])->d_type == DT_LNK) {
278            continue;
279        }
280        int ret = ERRNO_NOERR;
281        if ((pNameList->namelist[i])->d_type == DT_DIR) {
282            ret = CopySubDir(src, dest, infos);
283        } else {
284            infos->filePaths.insert(dest);
285            ret = CopyFile(src, dest, infos);
286        }
287        if (ret != ERRNO_NOERR) {
288            return ret;
289        }
290    }
291    return ERRNO_NOERR;
292}
293
294bool CopyImpl::IsDirectory(const std::string &path)
295{
296    struct stat buf {};
297    int ret = stat(path.c_str(), &buf);
298    if (ret == -1) {
299        LOGE("stat failed, errno is %{public}d, path is %{public}s", errno, path.c_str());
300        return false;
301    }
302    return (buf.st_mode & S_IFMT) == S_IFDIR;
303}
304
305int CopyImpl::CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
306{
307    LOGI("CopyDirFunc in, src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str());
308    size_t found = dest.find(src);
309    if (found != std::string::npos && found == 0) {
310        return EINVAL;
311    }
312    fs::path srcPath = fs::u8path(src);
313    std::string dirName;
314    if (srcPath.has_parent_path()) {
315        dirName = srcPath.parent_path().filename();
316    }
317    std::string destStr = dest + "/" + dirName;
318    return CopySubDir(src, destStr, infos);
319}
320
321uint64_t CopyImpl::GetDirSize(std::shared_ptr<FileInfos> infos, std::string path)
322{
323    std::unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (std::nothrow) struct NameList, Deleter };
324    if (pNameList == nullptr) {
325        LOGE("Failed to request heap memory.");
326        return ENOMEM;
327    }
328    int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
329    pNameList->direntNum = num;
330
331    long int size = 0;
332    for (int i = 0; i < num; i++) {
333        std::string dest = path + '/' + std::string((pNameList->namelist[i])->d_name);
334        if ((pNameList->namelist[i])->d_type == DT_LNK) {
335            continue;
336        }
337        if ((pNameList->namelist[i])->d_type == DT_DIR) {
338            size += static_cast<int64_t>(GetDirSize(infos, dest));
339        } else {
340            struct stat st {};
341            if (stat(dest.c_str(), &st) == -1) {
342                return size;
343            }
344            size += st.st_size;
345        }
346    }
347    return size;
348}
349
350std::shared_ptr<FileInfos> CopyImpl::InitCjFileInfo(
351    const std::string& srcUri, const std::string& destUri, sptr<CopyInfo> info)
352{
353    auto infos = FileManagement::CreateSharedPtr<FileInfos>();
354    if (infos == nullptr) {
355        LOGE("Failed to request heap memory by create FileInfos struct.");
356        return nullptr;
357    }
358    infos->srcUri = srcUri;
359    infos->destUri = destUri;
360    infos->listenerId = info->listenerId;
361    infos->copySignalId = info->signalId;
362    AppFileService::ModuleFileUri::FileUri srcFileUri(srcUri);
363    AppFileService::ModuleFileUri::FileUri destFileUri(destUri);
364    infos->srcPath = srcFileUri.GetRealPath();
365    infos->destPath = destFileUri.GetPath();
366    infos->srcPath = GetRealPath(infos->srcPath);
367    infos->destPath = GetRealPath(infos->destPath);
368    infos->notifyTime = std::chrono::steady_clock::now() + NOTIFY_PROGRESS_DELAY;
369    if (info->listenerId > 0) {
370        infos->hasListener = true;
371    }
372    auto signal = FFI::FFIData::GetData<TaskSignalImpl>(infos->copySignalId);
373    if (signal != nullptr && signal->signalEntity != nullptr) {
374        infos->taskSignal = signal->signalEntity->taskSignal_;
375    }
376    return infos;
377}
378
379void CopyImpl::ReceiveComplete(CProgress data,
380    std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
381{
382    if (callback == nullptr) {
383        LOGE("callback pointer is nullptr.");
384        return;
385    }
386    auto processedSize = data.processedSize;
387    if (processedSize < callback->maxProgressSize) {
388        return;
389    }
390    callback->maxProgressSize = processedSize;
391
392    if (callback->callback == nullptr) {
393        LOGI("Empty callback.");
394        return;
395    }
396    callback->callback(data);
397}
398
399void CopyImpl::OnFileReceive(std::shared_ptr<FileInfos> infos)
400{
401    auto callback = GetRegisteredListener(infos);
402    if (callback == nullptr) {
403        LOGE("failed to get listener progress");
404        return;
405    }
406    CProgress data = {.processedSize = callback->progressSize,
407                      .totalSize = callback->totalSize};
408    ReceiveComplete(data, infos, callback);
409}
410
411std::shared_ptr<ReceiveInfo> CopyImpl::GetReceivedInfo(int wd, std::shared_ptr<CjCallbackObject> callback)
412{
413    auto it = find_if(callback->wds.begin(), callback->wds.end(), [wd](const auto& item) {
414        return item.first == wd;
415    });
416    if (it != callback->wds.end()) {
417        return it->second;
418    }
419    return nullptr;
420}
421
422int CopyImpl::CopyFile(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
423{
424    LOGI("src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str());
425    int32_t srcFd = -1;
426    int32_t ret = OpenSrcFile(src, infos, srcFd);
427    if (srcFd < 0) {
428        return ret;
429    }
430    auto destFd = open(dest.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
431    if (destFd < 0) {
432        LOGE("Error opening dest file descriptor. errno = %{public}d", errno);
433        close(srcFd);
434        return errno;
435    }
436    auto srcFdg = FileManagement::CreateUniquePtr<DistributedFS::FDGuard>(srcFd, true);
437    auto destFdg = FileManagement::CreateUniquePtr<DistributedFS::FDGuard>(destFd, true);
438    if (srcFdg == nullptr || destFdg == nullptr) {
439        LOGE("Failed to request heap memory.");
440        close(srcFd);
441        close(destFd);
442        return ENOMEM;
443    }
444    return SendFileCore(move(srcFdg), move(destFdg), infos);
445}
446
447std::shared_ptr<CjCallbackObject> CopyImpl::GetRegisteredListener(std::shared_ptr<FileInfos> infos)
448{
449    std::lock_guard<std::recursive_mutex> lock(mutex_);
450    auto iter = cjCbMap_.find(*infos);
451    if (iter == cjCbMap_.end()) {
452        LOGE("It is not registered.");
453        return nullptr;
454    }
455    return iter->second;
456}
457
458std::tuple<bool, int, bool> CopyImpl::HandleProgress(
459    inotify_event *event, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
460{
461    if (callback == nullptr) {
462        return { true, EINVAL, false };
463    }
464    auto receivedInfo = GetReceivedInfo(event->wd, callback);
465    if (receivedInfo == nullptr) {
466        return { true, EINVAL, false };
467    }
468    std::string fileName = receivedInfo->path;
469    if (event->len > 0) { // files under subdir
470        fileName += "/" + std::string(event->name);
471        if (!CheckFileValid(fileName, infos)) {
472            return { true, EINVAL, false };
473        }
474        auto err = UpdateProgressSize(fileName, receivedInfo, callback);
475        if (err != ERRNO_NOERR) {
476            return { false, err, false };
477        }
478    } else {
479        auto [err, fileSize] = GetFileSize(fileName);
480        if (err != ERRNO_NOERR) {
481            return { false, err, false };
482        }
483        callback->progressSize = fileSize;
484    }
485    return { true, callback->errorCode, true };
486}
487
488void CopyImpl::ReadNotifyEvent(std::shared_ptr<FileInfos> infos)
489{
490    char buf[BUF_SIZE] = { 0 };
491    struct inotify_event *event = nullptr;
492    int len = 0;
493    int64_t index = 0;
494    auto callback = GetRegisteredListener(infos);
495    while (((len = read(infos->notifyFd, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {}
496    while (infos->run && index < len) {
497        event = reinterpret_cast<inotify_event *>(buf + index);
498        auto [needContinue, errCode, needSend] = HandleProgress(event, infos, callback);
499        if (!needContinue) {
500            infos->exceptionCode = errCode;
501            return;
502        }
503        if (needContinue && !needSend) {
504            index += static_cast<int64_t>(sizeof(struct inotify_event) + event->len);
505            continue;
506        }
507        if (callback->progressSize == callback->totalSize) {
508            infos->run = false;
509            return;
510        }
511        auto currentTime = std::chrono::steady_clock::now();
512        if (currentTime >= infos->notifyTime) {
513            OnFileReceive(infos);
514            infos->notifyTime = currentTime + NOTIFY_PROGRESS_DELAY;
515        }
516        index += static_cast<int64_t>(sizeof(struct inotify_event) + event->len);
517    }
518}
519
520void CopyImpl::GetNotifyEvent(std::shared_ptr<FileInfos> infos)
521{
522    auto callback = GetRegisteredListener(infos);
523    if (callback == nullptr) {
524        infos->exceptionCode = EINVAL;
525        return;
526    }
527    prctl(PR_SET_NAME, "NotifyThread");
528    nfds_t nfds = 2;
529    struct pollfd fds[2];
530    fds[0].events = 0;
531    fds[1].events = POLLIN;
532    fds[0].fd = infos->eventFd;
533    fds[1].fd = infos->notifyFd;
534    while (infos->run && infos->exceptionCode == ERRNO_NOERR && infos->eventFd != -1 && infos->notifyFd != -1) {
535        auto ret = poll(fds, nfds, -1);
536        if (ret > 0) {
537            if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
538                infos->run = false;
539                return;
540            }
541            if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
542                ReadNotifyEvent(infos);
543            }
544        } else if (ret < 0 && errno == EINTR) {
545            continue;
546        } else {
547            infos->exceptionCode = errno;
548            return;
549        }
550    }
551}
552
553void CopyImpl::StartNotify(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
554{
555    if (infos->hasListener && callback != nullptr) {
556        callback->notifyHandler = std::thread([infos] {
557            GetNotifyEvent(infos);
558            });
559    }
560}
561
562int CopyImpl::ExecCopy(std::shared_ptr<FileInfos> infos)
563{
564    if (IsFile(infos->srcPath) && IsFile(infos->destPath)) {
565        // copyFile
566        return CopyFile(infos->srcPath.c_str(), infos->destPath.c_str(), infos);
567    }
568    if (IsDirectory(infos->srcPath) && IsDirectory(infos->destPath)) {
569        if (infos->srcPath.back() != '/') {
570            infos->srcPath += '/';
571        }
572        if (infos->destPath.back() != '/') {
573            infos->destPath += '/';
574        }
575        // copyDir
576        return CopyDirFunc(infos->srcPath.c_str(), infos->destPath.c_str(), infos);
577    }
578    return EINVAL;
579}
580
581std::shared_ptr<CjCallbackObject> CopyImpl::RegisterListener(std::shared_ptr<FileInfos>& infos)
582{
583    auto callback = FileManagement::CreateSharedPtr<CjCallbackObject>(infos->listenerId);
584    if (callback == nullptr) {
585        LOGE("Failed to request heap memory by create callback object.");
586        return nullptr;
587    }
588    std::lock_guard<std::recursive_mutex> lock(mutex_);
589    auto iter = cjCbMap_.find(*infos);
590    if (iter != cjCbMap_.end()) {
591        LOGE("Regist copy listener, already registered.")
592        return nullptr;
593    }
594    cjCbMap_.insert({*infos, callback});
595    return callback;
596}
597
598void CopyImpl::UnregisterListener(std::shared_ptr<FileInfos> infos)
599{
600    if (infos == nullptr) {
601        LOGE("infos is nullptr");
602        return;
603    }
604    std::lock_guard<std::recursive_mutex> lock(mutex_);
605    auto iter = cjCbMap_.find(*infos);
606    if (iter == cjCbMap_.end()) {
607        LOGI("It is not be registered.");
608        return;
609    }
610    cjCbMap_.erase(*infos);
611}
612
613bool CopyImpl::IsRemoteUri(const std::string& uri)
614{
615    return uri.find(NETWORK_PARA) != uri.npos;
616}
617
618int64_t CopyImpl::DoCopy(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
619{
620    if (IsRemoteUri(infos->srcUri)) {
621        if (infos->taskSignal != nullptr) {
622            infos->taskSignal->MarkRemoteTask();
623        }
624        auto ret = TransListener::CopyFileFromSoftBus(
625            infos->srcUri, infos->destUri, infos, std::move(callback));
626        return ret;
627    }
628    auto result = ExecLocal(infos, callback);
629    CloseNotifyFd(infos, callback);
630    infos->run = false;
631    WaitNotifyFinished(callback);
632    if (result != ERRNO_NOERR) {
633        infos->exceptionCode = result;
634        return infos->exceptionCode;
635    }
636    CopyComplete(infos, callback);
637    return infos->exceptionCode;
638}
639
640int64_t CopyImpl::ExecLocal(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
641{
642    if (IsFile(infos->srcPath)) {
643        if (infos->srcPath == infos->destPath) {
644            LOGE("The src and dest is same, path = %{public}s", infos->srcPath.c_str());
645            return EINVAL;
646        }
647        CheckOrCreatePath(infos->destPath);
648    }
649    if (!infos->hasListener) {
650        return ExecCopy(infos);
651    }
652    auto ret = SubscribeLocalListener(infos, callback);
653    if (ret != ERRNO_NOERR) {
654        LOGE("Failed to subscribe local listener, errno = %{public}" PRIu64, ret);
655        return ret;
656    }
657    StartNotify(infos, callback);
658    return ExecCopy(infos);
659}
660
661int64_t CopyImpl::SubscribeLocalListener(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
662{
663    infos->notifyFd = inotify_init();
664    if (infos->notifyFd < 0) {
665        LOGE("Failed to init inotify, errno:%{public}d", errno);
666        return errno;
667    }
668    infos->eventFd = eventfd(0, EFD_CLOEXEC);
669    if (infos->eventFd < 0) {
670        LOGE("Failed to init eventFd, errno:%{public}d", errno);
671        return errno;
672    }
673    callback->notifyFd = infos->notifyFd;
674    callback->eventFd = infos->eventFd;
675    int newWd = inotify_add_watch(infos->notifyFd, infos->destPath.c_str(), IN_MODIFY);
676    if (newWd < 0) {
677        LOGE("Failed to add watch, errno = %{public}d, notifyFd: %{public}d, destPath: %{public}s",
678             errno, infos->notifyFd, infos->destPath.c_str());
679        CloseNotifyFd(infos, callback);
680        return errno;
681    }
682    auto receiveInfo = FileManagement::CreateSharedPtr<ReceiveInfo>();
683    if (receiveInfo == nullptr) {
684        LOGE("Failed to request heap memory.");
685        inotify_rm_watch(infos->notifyFd, newWd);
686        CloseNotifyFd(infos, callback);
687        return ENOMEM;
688    }
689    receiveInfo->path = infos->destPath;
690    callback->wds.push_back({ newWd, receiveInfo });
691    if (IsDirectory(infos->srcPath)) {
692        callback->totalSize = GetDirSize(infos, infos->srcPath);
693        return ERRNO_NOERR;
694    }
695    auto [err, fileSize] = GetFileSize(infos->srcPath);
696    if (err == ERRNO_NOERR) {
697        callback->totalSize = fileSize;
698    }
699    return err;
700}
701
702void CopyImpl::CloseNotifyFd(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
703{
704    callback->CloseFd();
705    infos->eventFd = -1;
706    infos->notifyFd = -1;
707}
708
709void CopyImpl::WaitNotifyFinished(std::shared_ptr<CjCallbackObject>& callback)
710{
711    if (callback != nullptr) {
712        if (callback->notifyHandler.joinable()) {
713            callback->notifyHandler.join();
714        }
715    }
716}
717
718void CopyImpl::CopyComplete(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
719{
720    if (callback != nullptr && infos->hasListener) {
721        callback->progressSize = callback->totalSize;
722        OnFileReceive(infos);
723    }
724}
725
726void CopyImpl::Copy(const char* srcUri, const char* destUri, sptr<CopyInfo> info)
727{
728    if (srcUri == nullptr || destUri == nullptr) {
729        LOGE("Invalid input.");
730        return;
731    }
732    std::string src(srcUri);
733    std::string dest(destUri);
734    std::shared_ptr<FileInfos> infos = InitCjFileInfo(src, dest, info);
735    if (infos == nullptr) {
736        return;
737    }
738    auto callback = RegisterListener(infos);
739    if (callback == nullptr) {
740        return;
741    }
742    DoCopy(infos, callback);
743    UnregisterListener(infos);
744}
745
746CopyInfo::~CopyInfo() {}
747}
748}