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 
32 namespace OHOS {
33 namespace CJSystemapi {
34 using namespace FileFs;
35 namespace fs = std::filesystem;
36 const std::string NETWORK_PARA = "?networkid=";
37 const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
38 constexpr int DISMATCH = 0;
39 constexpr int MATCH = 1;
40 constexpr int BUF_SIZE = 1024;
41 constexpr size_t MAX_SIZE = 1024 * 1024 * 128;
42 constexpr std::chrono::milliseconds NOTIFY_PROGRESS_DELAY(300);
43 std::recursive_mutex CopyImpl::mutex_;
44 std::map<FileInfos, std::shared_ptr<CjCallbackObject>> CopyImpl::cjCbMap_;
45 
OpenSrcFile(const std::string &srcPth, std::shared_ptr<FileInfos> infos, int32_t &srcFd)46 static 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 
SendFileCore(std::unique_ptr<DistributedFS::FDGuard> srcFdg, std::unique_ptr<DistributedFS::FDGuard> destFdg, std::shared_ptr<FileInfos> infos)75 static 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 
FilterFunc(const struct dirent *filename)118 static 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 
126 struct NameList {
127     struct dirent **namelist = { nullptr };
128     int direntNum = 0;
129 };
130 
Deleter(struct NameList *arg)131 static 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 
GetRealPath(const std::string& path)140 std::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 
IsFile(const std::string &path)156 bool 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 
GetFileSize(const std::string &path)167 std::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 
CheckFileValid(const std::string &filePath, std::shared_ptr<FileInfos> infos)178 bool CopyImpl::CheckFileValid(const std::string &filePath, std::shared_ptr<FileInfos> infos)
179 {
180     return infos->filePaths.count(filePath) != 0;
181 }
182 
UpdateProgressSize(const std::string &filePath, std::shared_ptr<ReceiveInfo> receivedInfo, std::shared_ptr<CjCallbackObject> callback)183 int 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 
CheckOrCreatePath(const std::string &destPath)205 void 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 
MakeDir(const std::string &path)217 int 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 
CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)228 int 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 
RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)264 int 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 
IsDirectory(const std::string &path)294 bool 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 
CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)305 int 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 
GetDirSize(std::shared_ptr<FileInfos> infos, std::string path)321 uint64_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 
InitCjFileInfo( const std::string& srcUri, const std::string& destUri, sptr<CopyInfo> info)350 std::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 
ReceiveComplete(CProgress data, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)379 void 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 
OnFileReceive(std::shared_ptr<FileInfos> infos)399 void 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 
GetReceivedInfo(int wd, std::shared_ptr<CjCallbackObject> callback)411 std::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 
CopyFile(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)422 int 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 
GetRegisteredListener(std::shared_ptr<FileInfos> infos)447 std::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 
HandleProgress( inotify_event *event, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)458 std::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 
ReadNotifyEvent(std::shared_ptr<FileInfos> infos)488 void 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 
GetNotifyEvent(std::shared_ptr<FileInfos> infos)520 void 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 
StartNotify(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)553 void 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 
ExecCopy(std::shared_ptr<FileInfos> infos)562 int 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 
RegisterListener(std::shared_ptr<FileInfos>& infos)581 std::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 
UnregisterListener(std::shared_ptr<FileInfos> infos)598 void 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 
IsRemoteUri(const std::string& uri)613 bool CopyImpl::IsRemoteUri(const std::string& uri)
614 {
615     return uri.find(NETWORK_PARA) != uri.npos;
616 }
617 
DoCopy(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)618 int64_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 
ExecLocal(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)640 int64_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 
SubscribeLocalListener(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)661 int64_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 
CloseNotifyFd(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)702 void 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 
WaitNotifyFinished(std::shared_ptr<CjCallbackObject>& callback)709 void 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 
CopyComplete(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)718 void 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 
Copy(const char* srcUri, const char* destUri, sptr<CopyInfo> info)726 void 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 
~CopyInfo()746 CopyInfo::~CopyInfo() {}
747 }
748 }