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