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