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