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