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