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