1 /*
2 * Copyright (c) 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 #include <fcntl.h>
18 #include <filesystem>
19 #include <poll.h>
20 #include <sys/eventfd.h>
21 #include <sys/inotify.h>
22 #include <sys/prctl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <thread>
26 #include "datashare_helper.h"
27 #include "file_uri.h"
28 #include "file_utils.h"
29 #include "iservice_registry.h"
30 #include "translistener.h"
31
32 namespace OHOS {
33 namespace CJSystemapi {
34 using namespace FileFs;
35 namespace fs = std::filesystem;
36 const std::string NETWORK_PARA = "?networkid=";
37 const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
38 constexpr int DISMATCH = 0;
39 constexpr int MATCH = 1;
40 constexpr int BUF_SIZE = 1024;
41 constexpr size_t MAX_SIZE = 1024 * 1024 * 128;
42 constexpr std::chrono::milliseconds NOTIFY_PROGRESS_DELAY(300);
43 std::recursive_mutex CopyImpl::mutex_;
44 std::map<FileInfos, std::shared_ptr<CjCallbackObject>> CopyImpl::cjCbMap_;
45
OpenSrcFile(const std::string &srcPth, std::shared_ptr<FileInfos> infos, int32_t &srcFd)46 static int OpenSrcFile(const std::string &srcPth, std::shared_ptr<FileInfos> infos, int32_t &srcFd)
47 {
48 Uri uri(infos->srcUri);
49 if (uri.GetAuthority() == "media") {
50 sptr<FileIoToken> remote = new (std::nothrow) IRemoteStub<FileIoToken>();
51 if (!remote) {
52 LOGE("Failed to get remote object");
53 return ENOMEM;
54 }
55 auto dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
56 if (!dataShareHelper) {
57 LOGE("Failed to connect to datashare");
58 return E_PERMISSION;
59 }
60 srcFd = dataShareHelper->OpenFile(uri, CommonFunc::GetModeFromFlags(O_RDONLY));
61 if (srcFd < 0) {
62 LOGE("Open media uri by data share fail. ret = %{public}d", srcFd);
63 return EPERM;
64 }
65 } else {
66 srcFd = open(srcPth.c_str(), O_RDONLY);
67 if (srcFd < 0) {
68 LOGE("Error opening src file descriptor. errno = %{public}d", errno);
69 return errno;
70 }
71 }
72 return ERRNO_NOERR;
73 }
74
SendFileCore(std::unique_ptr<DistributedFS::FDGuard> srcFdg, std::unique_ptr<DistributedFS::FDGuard> destFdg, std::shared_ptr<FileInfos> infos)75 static int SendFileCore(std::unique_ptr<DistributedFS::FDGuard> srcFdg,
76 std::unique_ptr<DistributedFS::FDGuard> destFdg,
77 std::shared_ptr<FileInfos> infos)
78 {
79 std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> sendFileReq = {
80 new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
81 if (sendFileReq == nullptr) {
82 LOGE("Failed to request heap memory.");
83 return ENOMEM;
84 }
85 int64_t offset = 0;
86 struct stat srcStat{};
87 if (fstat(srcFdg->GetFD(), &srcStat) < 0) {
88 LOGE("Failed to get stat of file by fd: %{public}d ,errno = %{public}d", srcFdg->GetFD(), errno);
89 return errno;
90 }
91 int32_t ret = 0;
92 int64_t size = static_cast<int64_t>(srcStat.st_size);
93 while (size >= 0) {
94 ret = uv_fs_sendfile(nullptr, sendFileReq.get(), destFdg->GetFD(), srcFdg->GetFD(),
95 offset, MAX_SIZE, nullptr);
96 if (ret < 0) {
97 LOGE("Failed to sendfile by errno : %{public}d", errno);
98 return errno;
99 }
100 if (infos != nullptr && infos->taskSignal != nullptr) {
101 if (infos->taskSignal->CheckCancelIfNeed(infos->srcPath)) {
102 return ECANCELED;
103 }
104 }
105 offset += static_cast<int64_t>(ret);
106 size -= static_cast<int64_t>(ret);
107 if (ret == 0) {
108 break;
109 }
110 }
111 if (size != 0) {
112 LOGE("The execution of the sendfile task was terminated, remaining file size %{public}" PRIu64, size);
113 return EIO;
114 }
115 return ERRNO_NOERR;
116 }
117
FilterFunc(const struct dirent *filename)118 static int FilterFunc(const struct dirent *filename)
119 {
120 if (std::string_view(filename->d_name) == "." || std::string_view(filename->d_name) == "..") {
121 return DISMATCH;
122 }
123 return MATCH;
124 }
125
126 struct NameList {
127 struct dirent **namelist = { nullptr };
128 int direntNum = 0;
129 };
130
Deleter(struct NameList *arg)131 static void Deleter(struct NameList *arg)
132 {
133 for (int i = 0; i < arg->direntNum; i++) {
134 free((arg->namelist)[i]);
135 (arg->namelist)[i] = nullptr;
136 }
137 free(arg->namelist);
138 }
139
GetRealPath(const std::string& path)140 std::string CopyImpl::GetRealPath(const std::string& path)
141 {
142 fs::path tempPath(path);
143 fs::path realPath{};
144 for (const auto& component : tempPath) {
145 if (component == ".") {
146 continue;
147 } else if (component == "..") {
148 realPath = realPath.parent_path();
149 } else {
150 realPath /= component;
151 }
152 }
153 return realPath.string();
154 }
155
IsFile(const std::string &path)156 bool CopyImpl::IsFile(const std::string &path)
157 {
158 struct stat buf {};
159 int ret = stat(path.c_str(), &buf);
160 if (ret == -1) {
161 LOGI("stat failed, errno is %{public}d, ", errno);
162 return false;
163 }
164 return (buf.st_mode & S_IFMT) == S_IFREG;
165 }
166
GetFileSize(const std::string &path)167 std::tuple<int, uint64_t> CopyImpl::GetFileSize(const std::string &path)
168 {
169 struct stat buf {};
170 int ret = stat(path.c_str(), &buf);
171 if (ret == -1) {
172 LOGI("Stat failed.");
173 return { errno, 0 };
174 }
175 return { ERRNO_NOERR, buf.st_size };
176 }
177
CheckFileValid(const std::string &filePath, std::shared_ptr<FileInfos> infos)178 bool CopyImpl::CheckFileValid(const std::string &filePath, std::shared_ptr<FileInfos> infos)
179 {
180 return infos->filePaths.count(filePath) != 0;
181 }
182
UpdateProgressSize(const std::string &filePath, std::shared_ptr<ReceiveInfo> receivedInfo, std::shared_ptr<CjCallbackObject> callback)183 int CopyImpl::UpdateProgressSize(const std::string &filePath,
184 std::shared_ptr<ReceiveInfo> receivedInfo, std::shared_ptr<CjCallbackObject> callback)
185 {
186 auto [err, fileSize] = GetFileSize(filePath);
187 if (err != ERRNO_NOERR) {
188 LOGE("GetFileSize failed, err: %{public}d.", err);
189 return err;
190 }
191 auto size = fileSize;
192 auto iter = receivedInfo->fileList.find(filePath);
193 if (iter == receivedInfo->fileList.end()) {
194 receivedInfo->fileList.insert({ filePath, size });
195 callback->progressSize += size;
196 } else { // file
197 if (size > iter->second) {
198 callback->progressSize += (size - iter->second);
199 iter->second = size;
200 }
201 }
202 return ERRNO_NOERR;
203 }
204
CheckOrCreatePath(const std::string &destPath)205 void CopyImpl::CheckOrCreatePath(const std::string &destPath)
206 {
207 if (!std::filesystem::exists(destPath)) {
208 LOGI("destPath not exist, destPath = %{public}s", destPath.c_str());
209 auto file = open(destPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
210 if (file < 0) {
211 LOGE("Error opening file descriptor. errno = %{public}d", errno);
212 }
213 close(file);
214 }
215 }
216
MakeDir(const std::string &path)217 int CopyImpl::MakeDir(const std::string &path)
218 {
219 std::filesystem::path destDir(path);
220 std::error_code errCode;
221 if (!std::filesystem::create_directory(destDir, errCode)) {
222 LOGE("Failed to create directory, error code: %{public}d", errCode.value());
223 return errCode.value();
224 }
225 return ERRNO_NOERR;
226 }
227
CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)228 int CopyImpl::CopySubDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)
229 {
230 if (!std::filesystem::exists(destPath)) {
231 int res = MakeDir(destPath);
232 if (res != ERRNO_NOERR) {
233 LOGE("Failed to mkdir");
234 return res;
235 }
236 }
237 uint32_t watchEvents = IN_MODIFY;
238 if (infos->notifyFd >= 0) {
239 int newWd = inotify_add_watch(infos->notifyFd, destPath.c_str(), watchEvents);
240 if (newWd < 0) {
241 LOGE("inotify_add_watch, newWd is unvaild, newWd = %{public}d", newWd);
242 return errno;
243 }
244 {
245 std::lock_guard<std::recursive_mutex> lock(CopyImpl::mutex_);
246 auto iter = CopyImpl::cjCbMap_.find(*infos);
247 auto receiveInfo = FileManagement::CreateSharedPtr<ReceiveInfo>();
248 if (receiveInfo == nullptr) {
249 LOGE("Failed to request heap memory.");
250 return ENOMEM;
251 }
252 receiveInfo->path = destPath;
253 if (iter == CopyImpl::cjCbMap_.end() || iter->second == nullptr) {
254 LOGE("Failed to find infos, srcPath = %{public}s, destPath = %{public}s", infos->srcPath.c_str(),
255 infos->destPath.c_str());
256 return UNKNOWN_ERR;
257 }
258 iter->second->wds.push_back({ newWd, receiveInfo });
259 }
260 }
261 return RecurCopyDir(srcPath, destPath, infos);
262 }
263
RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)264 int CopyImpl::RecurCopyDir(const std::string &srcPath, const std::string &destPath, std::shared_ptr<FileInfos> infos)
265 {
266 std::unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (std::nothrow) struct NameList, Deleter };
267 if (pNameList == nullptr) {
268 LOGE("Failed to request heap memory.");
269 return ENOMEM;
270 }
271 int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
272 pNameList->direntNum = num;
273
274 for (int i = 0; i < num; i++) {
275 std::string src = srcPath + '/' + std::string((pNameList->namelist[i])->d_name);
276 std::string dest = destPath + '/' + std::string((pNameList->namelist[i])->d_name);
277 if ((pNameList->namelist[i])->d_type == DT_LNK) {
278 continue;
279 }
280 int ret = ERRNO_NOERR;
281 if ((pNameList->namelist[i])->d_type == DT_DIR) {
282 ret = CopySubDir(src, dest, infos);
283 } else {
284 infos->filePaths.insert(dest);
285 ret = CopyFile(src, dest, infos);
286 }
287 if (ret != ERRNO_NOERR) {
288 return ret;
289 }
290 }
291 return ERRNO_NOERR;
292 }
293
IsDirectory(const std::string &path)294 bool CopyImpl::IsDirectory(const std::string &path)
295 {
296 struct stat buf {};
297 int ret = stat(path.c_str(), &buf);
298 if (ret == -1) {
299 LOGE("stat failed, errno is %{public}d, path is %{public}s", errno, path.c_str());
300 return false;
301 }
302 return (buf.st_mode & S_IFMT) == S_IFDIR;
303 }
304
CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)305 int CopyImpl::CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
306 {
307 LOGI("CopyDirFunc in, src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str());
308 size_t found = dest.find(src);
309 if (found != std::string::npos && found == 0) {
310 return EINVAL;
311 }
312 fs::path srcPath = fs::u8path(src);
313 std::string dirName;
314 if (srcPath.has_parent_path()) {
315 dirName = srcPath.parent_path().filename();
316 }
317 std::string destStr = dest + "/" + dirName;
318 return CopySubDir(src, destStr, infos);
319 }
320
GetDirSize(std::shared_ptr<FileInfos> infos, std::string path)321 uint64_t CopyImpl::GetDirSize(std::shared_ptr<FileInfos> infos, std::string path)
322 {
323 std::unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (std::nothrow) struct NameList, Deleter };
324 if (pNameList == nullptr) {
325 LOGE("Failed to request heap memory.");
326 return ENOMEM;
327 }
328 int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
329 pNameList->direntNum = num;
330
331 long int size = 0;
332 for (int i = 0; i < num; i++) {
333 std::string dest = path + '/' + std::string((pNameList->namelist[i])->d_name);
334 if ((pNameList->namelist[i])->d_type == DT_LNK) {
335 continue;
336 }
337 if ((pNameList->namelist[i])->d_type == DT_DIR) {
338 size += static_cast<int64_t>(GetDirSize(infos, dest));
339 } else {
340 struct stat st {};
341 if (stat(dest.c_str(), &st) == -1) {
342 return size;
343 }
344 size += st.st_size;
345 }
346 }
347 return size;
348 }
349
InitCjFileInfo( const std::string& srcUri, const std::string& destUri, sptr<CopyInfo> info)350 std::shared_ptr<FileInfos> CopyImpl::InitCjFileInfo(
351 const std::string& srcUri, const std::string& destUri, sptr<CopyInfo> info)
352 {
353 auto infos = FileManagement::CreateSharedPtr<FileInfos>();
354 if (infos == nullptr) {
355 LOGE("Failed to request heap memory by create FileInfos struct.");
356 return nullptr;
357 }
358 infos->srcUri = srcUri;
359 infos->destUri = destUri;
360 infos->listenerId = info->listenerId;
361 infos->copySignalId = info->signalId;
362 AppFileService::ModuleFileUri::FileUri srcFileUri(srcUri);
363 AppFileService::ModuleFileUri::FileUri destFileUri(destUri);
364 infos->srcPath = srcFileUri.GetRealPath();
365 infos->destPath = destFileUri.GetPath();
366 infos->srcPath = GetRealPath(infos->srcPath);
367 infos->destPath = GetRealPath(infos->destPath);
368 infos->notifyTime = std::chrono::steady_clock::now() + NOTIFY_PROGRESS_DELAY;
369 if (info->listenerId > 0) {
370 infos->hasListener = true;
371 }
372 auto signal = FFI::FFIData::GetData<TaskSignalImpl>(infos->copySignalId);
373 if (signal != nullptr && signal->signalEntity != nullptr) {
374 infos->taskSignal = signal->signalEntity->taskSignal_;
375 }
376 return infos;
377 }
378
ReceiveComplete(CProgress data, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)379 void CopyImpl::ReceiveComplete(CProgress data,
380 std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
381 {
382 if (callback == nullptr) {
383 LOGE("callback pointer is nullptr.");
384 return;
385 }
386 auto processedSize = data.processedSize;
387 if (processedSize < callback->maxProgressSize) {
388 return;
389 }
390 callback->maxProgressSize = processedSize;
391
392 if (callback->callback == nullptr) {
393 LOGI("Empty callback.");
394 return;
395 }
396 callback->callback(data);
397 }
398
OnFileReceive(std::shared_ptr<FileInfos> infos)399 void CopyImpl::OnFileReceive(std::shared_ptr<FileInfos> infos)
400 {
401 auto callback = GetRegisteredListener(infos);
402 if (callback == nullptr) {
403 LOGE("failed to get listener progress");
404 return;
405 }
406 CProgress data = {.processedSize = callback->progressSize,
407 .totalSize = callback->totalSize};
408 ReceiveComplete(data, infos, callback);
409 }
410
GetReceivedInfo(int wd, std::shared_ptr<CjCallbackObject> callback)411 std::shared_ptr<ReceiveInfo> CopyImpl::GetReceivedInfo(int wd, std::shared_ptr<CjCallbackObject> callback)
412 {
413 auto it = find_if(callback->wds.begin(), callback->wds.end(), [wd](const auto& item) {
414 return item.first == wd;
415 });
416 if (it != callback->wds.end()) {
417 return it->second;
418 }
419 return nullptr;
420 }
421
CopyFile(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)422 int CopyImpl::CopyFile(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
423 {
424 LOGI("src = %{public}s, dest = %{public}s", src.c_str(), dest.c_str());
425 int32_t srcFd = -1;
426 int32_t ret = OpenSrcFile(src, infos, srcFd);
427 if (srcFd < 0) {
428 return ret;
429 }
430 auto destFd = open(dest.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
431 if (destFd < 0) {
432 LOGE("Error opening dest file descriptor. errno = %{public}d", errno);
433 close(srcFd);
434 return errno;
435 }
436 auto srcFdg = FileManagement::CreateUniquePtr<DistributedFS::FDGuard>(srcFd, true);
437 auto destFdg = FileManagement::CreateUniquePtr<DistributedFS::FDGuard>(destFd, true);
438 if (srcFdg == nullptr || destFdg == nullptr) {
439 LOGE("Failed to request heap memory.");
440 close(srcFd);
441 close(destFd);
442 return ENOMEM;
443 }
444 return SendFileCore(move(srcFdg), move(destFdg), infos);
445 }
446
GetRegisteredListener(std::shared_ptr<FileInfos> infos)447 std::shared_ptr<CjCallbackObject> CopyImpl::GetRegisteredListener(std::shared_ptr<FileInfos> infos)
448 {
449 std::lock_guard<std::recursive_mutex> lock(mutex_);
450 auto iter = cjCbMap_.find(*infos);
451 if (iter == cjCbMap_.end()) {
452 LOGE("It is not registered.");
453 return nullptr;
454 }
455 return iter->second;
456 }
457
HandleProgress( inotify_event *event, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)458 std::tuple<bool, int, bool> CopyImpl::HandleProgress(
459 inotify_event *event, std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
460 {
461 if (callback == nullptr) {
462 return { true, EINVAL, false };
463 }
464 auto receivedInfo = GetReceivedInfo(event->wd, callback);
465 if (receivedInfo == nullptr) {
466 return { true, EINVAL, false };
467 }
468 std::string fileName = receivedInfo->path;
469 if (event->len > 0) { // files under subdir
470 fileName += "/" + std::string(event->name);
471 if (!CheckFileValid(fileName, infos)) {
472 return { true, EINVAL, false };
473 }
474 auto err = UpdateProgressSize(fileName, receivedInfo, callback);
475 if (err != ERRNO_NOERR) {
476 return { false, err, false };
477 }
478 } else {
479 auto [err, fileSize] = GetFileSize(fileName);
480 if (err != ERRNO_NOERR) {
481 return { false, err, false };
482 }
483 callback->progressSize = fileSize;
484 }
485 return { true, callback->errorCode, true };
486 }
487
ReadNotifyEvent(std::shared_ptr<FileInfos> infos)488 void CopyImpl::ReadNotifyEvent(std::shared_ptr<FileInfos> infos)
489 {
490 char buf[BUF_SIZE] = { 0 };
491 struct inotify_event *event = nullptr;
492 int len = 0;
493 int64_t index = 0;
494 auto callback = GetRegisteredListener(infos);
495 while (((len = read(infos->notifyFd, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {}
496 while (infos->run && index < len) {
497 event = reinterpret_cast<inotify_event *>(buf + index);
498 auto [needContinue, errCode, needSend] = HandleProgress(event, infos, callback);
499 if (!needContinue) {
500 infos->exceptionCode = errCode;
501 return;
502 }
503 if (needContinue && !needSend) {
504 index += static_cast<int64_t>(sizeof(struct inotify_event) + event->len);
505 continue;
506 }
507 if (callback->progressSize == callback->totalSize) {
508 infos->run = false;
509 return;
510 }
511 auto currentTime = std::chrono::steady_clock::now();
512 if (currentTime >= infos->notifyTime) {
513 OnFileReceive(infos);
514 infos->notifyTime = currentTime + NOTIFY_PROGRESS_DELAY;
515 }
516 index += static_cast<int64_t>(sizeof(struct inotify_event) + event->len);
517 }
518 }
519
GetNotifyEvent(std::shared_ptr<FileInfos> infos)520 void CopyImpl::GetNotifyEvent(std::shared_ptr<FileInfos> infos)
521 {
522 auto callback = GetRegisteredListener(infos);
523 if (callback == nullptr) {
524 infos->exceptionCode = EINVAL;
525 return;
526 }
527 prctl(PR_SET_NAME, "NotifyThread");
528 nfds_t nfds = 2;
529 struct pollfd fds[2];
530 fds[0].events = 0;
531 fds[1].events = POLLIN;
532 fds[0].fd = infos->eventFd;
533 fds[1].fd = infos->notifyFd;
534 while (infos->run && infos->exceptionCode == ERRNO_NOERR && infos->eventFd != -1 && infos->notifyFd != -1) {
535 auto ret = poll(fds, nfds, -1);
536 if (ret > 0) {
537 if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
538 infos->run = false;
539 return;
540 }
541 if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
542 ReadNotifyEvent(infos);
543 }
544 } else if (ret < 0 && errno == EINTR) {
545 continue;
546 } else {
547 infos->exceptionCode = errno;
548 return;
549 }
550 }
551 }
552
StartNotify(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)553 void CopyImpl::StartNotify(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
554 {
555 if (infos->hasListener && callback != nullptr) {
556 callback->notifyHandler = std::thread([infos] {
557 GetNotifyEvent(infos);
558 });
559 }
560 }
561
ExecCopy(std::shared_ptr<FileInfos> infos)562 int CopyImpl::ExecCopy(std::shared_ptr<FileInfos> infos)
563 {
564 if (IsFile(infos->srcPath) && IsFile(infos->destPath)) {
565 // copyFile
566 return CopyFile(infos->srcPath.c_str(), infos->destPath.c_str(), infos);
567 }
568 if (IsDirectory(infos->srcPath) && IsDirectory(infos->destPath)) {
569 if (infos->srcPath.back() != '/') {
570 infos->srcPath += '/';
571 }
572 if (infos->destPath.back() != '/') {
573 infos->destPath += '/';
574 }
575 // copyDir
576 return CopyDirFunc(infos->srcPath.c_str(), infos->destPath.c_str(), infos);
577 }
578 return EINVAL;
579 }
580
RegisterListener(std::shared_ptr<FileInfos>& infos)581 std::shared_ptr<CjCallbackObject> CopyImpl::RegisterListener(std::shared_ptr<FileInfos>& infos)
582 {
583 auto callback = FileManagement::CreateSharedPtr<CjCallbackObject>(infos->listenerId);
584 if (callback == nullptr) {
585 LOGE("Failed to request heap memory by create callback object.");
586 return nullptr;
587 }
588 std::lock_guard<std::recursive_mutex> lock(mutex_);
589 auto iter = cjCbMap_.find(*infos);
590 if (iter != cjCbMap_.end()) {
591 LOGE("Regist copy listener, already registered.")
592 return nullptr;
593 }
594 cjCbMap_.insert({*infos, callback});
595 return callback;
596 }
597
UnregisterListener(std::shared_ptr<FileInfos> infos)598 void CopyImpl::UnregisterListener(std::shared_ptr<FileInfos> infos)
599 {
600 if (infos == nullptr) {
601 LOGE("infos is nullptr");
602 return;
603 }
604 std::lock_guard<std::recursive_mutex> lock(mutex_);
605 auto iter = cjCbMap_.find(*infos);
606 if (iter == cjCbMap_.end()) {
607 LOGI("It is not be registered.");
608 return;
609 }
610 cjCbMap_.erase(*infos);
611 }
612
IsRemoteUri(const std::string& uri)613 bool CopyImpl::IsRemoteUri(const std::string& uri)
614 {
615 return uri.find(NETWORK_PARA) != uri.npos;
616 }
617
DoCopy(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)618 int64_t CopyImpl::DoCopy(std::shared_ptr<FileInfos> infos, std::shared_ptr<CjCallbackObject> callback)
619 {
620 if (IsRemoteUri(infos->srcUri)) {
621 if (infos->taskSignal != nullptr) {
622 infos->taskSignal->MarkRemoteTask();
623 }
624 auto ret = TransListener::CopyFileFromSoftBus(
625 infos->srcUri, infos->destUri, infos, std::move(callback));
626 return ret;
627 }
628 auto result = ExecLocal(infos, callback);
629 CloseNotifyFd(infos, callback);
630 infos->run = false;
631 WaitNotifyFinished(callback);
632 if (result != ERRNO_NOERR) {
633 infos->exceptionCode = result;
634 return infos->exceptionCode;
635 }
636 CopyComplete(infos, callback);
637 return infos->exceptionCode;
638 }
639
ExecLocal(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)640 int64_t CopyImpl::ExecLocal(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
641 {
642 if (IsFile(infos->srcPath)) {
643 if (infos->srcPath == infos->destPath) {
644 LOGE("The src and dest is same, path = %{public}s", infos->srcPath.c_str());
645 return EINVAL;
646 }
647 CheckOrCreatePath(infos->destPath);
648 }
649 if (!infos->hasListener) {
650 return ExecCopy(infos);
651 }
652 auto ret = SubscribeLocalListener(infos, callback);
653 if (ret != ERRNO_NOERR) {
654 LOGE("Failed to subscribe local listener, errno = %{public}" PRIu64, ret);
655 return ret;
656 }
657 StartNotify(infos, callback);
658 return ExecCopy(infos);
659 }
660
SubscribeLocalListener(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)661 int64_t CopyImpl::SubscribeLocalListener(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
662 {
663 infos->notifyFd = inotify_init();
664 if (infos->notifyFd < 0) {
665 LOGE("Failed to init inotify, errno:%{public}d", errno);
666 return errno;
667 }
668 infos->eventFd = eventfd(0, EFD_CLOEXEC);
669 if (infos->eventFd < 0) {
670 LOGE("Failed to init eventFd, errno:%{public}d", errno);
671 return errno;
672 }
673 callback->notifyFd = infos->notifyFd;
674 callback->eventFd = infos->eventFd;
675 int newWd = inotify_add_watch(infos->notifyFd, infos->destPath.c_str(), IN_MODIFY);
676 if (newWd < 0) {
677 LOGE("Failed to add watch, errno = %{public}d, notifyFd: %{public}d, destPath: %{public}s",
678 errno, infos->notifyFd, infos->destPath.c_str());
679 CloseNotifyFd(infos, callback);
680 return errno;
681 }
682 auto receiveInfo = FileManagement::CreateSharedPtr<ReceiveInfo>();
683 if (receiveInfo == nullptr) {
684 LOGE("Failed to request heap memory.");
685 inotify_rm_watch(infos->notifyFd, newWd);
686 CloseNotifyFd(infos, callback);
687 return ENOMEM;
688 }
689 receiveInfo->path = infos->destPath;
690 callback->wds.push_back({ newWd, receiveInfo });
691 if (IsDirectory(infos->srcPath)) {
692 callback->totalSize = GetDirSize(infos, infos->srcPath);
693 return ERRNO_NOERR;
694 }
695 auto [err, fileSize] = GetFileSize(infos->srcPath);
696 if (err == ERRNO_NOERR) {
697 callback->totalSize = fileSize;
698 }
699 return err;
700 }
701
CloseNotifyFd(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)702 void CopyImpl::CloseNotifyFd(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
703 {
704 callback->CloseFd();
705 infos->eventFd = -1;
706 infos->notifyFd = -1;
707 }
708
WaitNotifyFinished(std::shared_ptr<CjCallbackObject>& callback)709 void CopyImpl::WaitNotifyFinished(std::shared_ptr<CjCallbackObject>& callback)
710 {
711 if (callback != nullptr) {
712 if (callback->notifyHandler.joinable()) {
713 callback->notifyHandler.join();
714 }
715 }
716 }
717
CopyComplete(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)718 void CopyImpl::CopyComplete(std::shared_ptr<FileInfos>& infos, std::shared_ptr<CjCallbackObject>& callback)
719 {
720 if (callback != nullptr && infos->hasListener) {
721 callback->progressSize = callback->totalSize;
722 OnFileReceive(infos);
723 }
724 }
725
Copy(const char* srcUri, const char* destUri, sptr<CopyInfo> info)726 void CopyImpl::Copy(const char* srcUri, const char* destUri, sptr<CopyInfo> info)
727 {
728 if (srcUri == nullptr || destUri == nullptr) {
729 LOGE("Invalid input.");
730 return;
731 }
732 std::string src(srcUri);
733 std::string dest(destUri);
734 std::shared_ptr<FileInfos> infos = InitCjFileInfo(src, dest, info);
735 if (infos == nullptr) {
736 return;
737 }
738 auto callback = RegisterListener(infos);
739 if (callback == nullptr) {
740 return;
741 }
742 DoCopy(infos, callback);
743 UnregisterListener(infos);
744 }
745
~CopyInfo()746 CopyInfo::~CopyInfo() {}
747 }
748 }