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