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