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