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/file_copy_manager.h"
17
18 #include <cstring>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <filesystem>
22 #include <limits>
23 #include <memory>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include "copy/distributed_file_fd_guard.h"
29 #include "copy/file_copy_listener.h"
30 #include "copy/file_size_utils.h"
31 #include "datashare_helper.h"
32 #include "dfs_error.h"
33 #include "distributed_file_daemon_proxy.h"
34 #include "file_uri.h"
35 #include "iremote_stub.h"
36 #include "sandbox_helper.h"
37 #include "utils_log.h"
38
39 #undef LOG_DOMAIN
40 #undef LOG_TAG
41 #define LOG_DOMAIN 0xD001600
42 #define LOG_TAG "distributedfile_daemon"
43
44 namespace OHOS {
45 namespace Storage {
46 namespace DistributedFile {
47 using namespace AppFileService;
48 using namespace FileManagement;
49 static const std::string FILE_PREFIX_NAME = "file://";
50 static const std::string NETWORK_PARA = "?networkid=";
51 static const std::string MEDIALIBRARY_DATA_URI = "datashare:///media";
52 static const std::string MEDIA = "media";
53 static constexpr size_t MAX_SIZE = 1024 * 1024 * 4;
54 std::shared_ptr<FileCopyManager> FileCopyManager::instance_ = nullptr;
55
CheckPath(std::shared_ptr<FileInfos> & infos)56 static bool CheckPath(std::shared_ptr<FileInfos> &infos)
57 {
58 std::string destPath = infos->destPath;
59 if (infos->srcUriIsFile) {
60 auto pos = destPath.rfind("/");
61 if (pos == std::string::npos) {
62 return false;
63 }
64 destPath.resize(pos);
65 }
66 if (!(SandboxHelper::CheckValidPath(infos->srcPath) && SandboxHelper::CheckValidPath(destPath))) {
67 return false;
68 }
69 return true;
70 }
71
GetInstance()72 std::shared_ptr<FileCopyManager> FileCopyManager::GetInstance()
73 {
74 static std::once_flag once;
75 std::call_once(once, []() {
76 FileCopyManager::instance_ = std::make_shared<FileCopyManager>();
77 });
78
79 return instance_;
80 }
81
Copy(const std::string & srcUri,const std::string & destUri,ProcessCallback & processCallback)82 int32_t FileCopyManager::Copy(const std::string &srcUri, const std::string &destUri, ProcessCallback &processCallback)
83 {
84 LOGE("FileCopyManager Copy start ");
85 if (srcUri.empty() || destUri.empty()) {
86 return E_NOENT;
87 }
88
89 auto infos = std::make_shared<FileInfos>();
90 auto ret = CreateFileInfos(srcUri, destUri, infos);
91 if (ret != E_OK) {
92 return ret;
93 }
94
95 if (IsRemoteUri(infos->srcUri)) {
96 ret = ExecRemote(infos, processCallback);
97 RemoveFileInfos(infos);
98 return ret;
99 }
100
101 if (!CheckPath(infos)) {
102 LOGE("invalid srcPath : %{private}s, destPath: %{private}s", GetAnonyString(infos->srcPath).c_str(),
103 GetAnonyString(infos->destPath).c_str());
104 return E_NOENT;
105 }
106
107 infos->localListener = FileCopyLocalListener::GetLocalListener(infos->srcPath,
108 infos->srcUriIsFile, processCallback);
109 infos->localListener->StartListener();
110 auto result = ExecLocal(infos);
111 RemoveFileInfos(infos);
112 infos->localListener->StopListener();
113
114 if (result != E_OK) {
115 return result;
116 }
117 return infos->localListener->GetResult();
118 }
119
ExecRemote(std::shared_ptr<FileInfos> infos,ProcessCallback & processCallback)120 int32_t FileCopyManager::ExecRemote(std::shared_ptr<FileInfos> infos, ProcessCallback &processCallback)
121 {
122 LOGI("ExecRemote Copy start ");
123 sptr<TransListener> transListener (new (std::nothrow) TransListener(infos->destUri, processCallback));
124 if (transListener == nullptr) {
125 LOGE("new trans listener failed");
126 return ENOMEM;
127 }
128 infos->transListener = transListener;
129
130 auto networkId = transListener->GetNetworkIdFromUri(infos->srcUri);
131 auto distributedFileDaemonProxy = DistributedFileDaemonProxy::GetInstance();
132 if (distributedFileDaemonProxy == nullptr) {
133 LOGE("proxy is null");
134 return E_SA_LOAD_FAILED;
135 }
136 auto ret = distributedFileDaemonProxy->PrepareSession(infos->srcUri, infos->destUri,
137 networkId, transListener, transListener->hmdfsInfo_);
138 if (ret != E_OK) {
139 LOGE("PrepareSession failed, ret = %{public}d.", ret);
140 return ret;
141 }
142
143 auto copyResult = transListener->WaitForCopyResult();
144 if (copyResult == FAILED) {
145 return transListener->GetErrCode();
146 }
147 return transListener->CopyToSandBox(infos->srcUri);
148 }
149
Cancel()150 int32_t FileCopyManager::Cancel()
151 {
152 LOGI("Cancel all Copy");
153 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
154 for (auto &item : FileInfosVec_) {
155 item->needCancel.store(true);
156 if (item->transListener != nullptr) {
157 item->transListener->Cancel();
158 }
159 DeleteResFile(item);
160 }
161 FileInfosVec_.clear();
162 return E_OK;
163 }
164
Cancel(const std::string & srcUri,const std::string & destUri)165 int32_t FileCopyManager::Cancel(const std::string &srcUri, const std::string &destUri)
166 {
167 LOGI("Cancel Copy");
168 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
169 int32_t ret = 0;
170 for (auto item = FileInfosVec_.begin(); item != FileInfosVec_.end();) {
171 if ((*item)->srcUri != srcUri || (*item)->destUri != destUri) {
172 ++item;
173 continue;
174 }
175 (*item)->needCancel.store(true);
176 if ((*item)->transListener != nullptr) {
177 ret = (*item)->transListener->Cancel();
178 }
179 DeleteResFile(*item);
180 item = FileInfosVec_.erase(item);
181 return ret;
182 }
183 return E_OK;
184 }
185
DeleteResFile(std::shared_ptr<FileInfos> infos)186 void FileCopyManager::DeleteResFile(std::shared_ptr<FileInfos> infos)
187 {
188 std::error_code errCode;
189 //delete files in remote cancel
190 if (infos->transListener != nullptr) {
191 if (std::filesystem::exists(infos->destPath, errCode)) {
192 std::filesystem::remove(infos->destPath, errCode);
193 }
194 return;
195 }
196
197 //delete files&dirs in local cancel
198 auto filePaths = infos->localListener->GetFilePath();
199 for (auto path : filePaths) {
200 if (!std::filesystem::exists(path, errCode)) {
201 LOGE("Failed to find the file, errcode %{public}d", errCode.value());
202 continue;
203 }
204 std::filesystem::remove(path, errCode);
205 }
206
207 std::lock_guard<std::mutex> lock(infos->subDirsMutex);
208 for (auto subDir : infos->subDirs) {
209 if (!std::filesystem::exists(subDir, errCode)) {
210 LOGE("Failed to find the dir, errcode %{public}d", errCode.value());
211 continue;
212 }
213 std::filesystem::remove(subDir, errCode);
214 }
215 }
216
ExecLocal(std::shared_ptr<FileInfos> infos)217 int32_t FileCopyManager::ExecLocal(std::shared_ptr<FileInfos> infos)
218 {
219 LOGI("start ExecLocal");
220 // 文件到文件, 文件到目录的形式由上层改写为文件到文件的形式
221 if (infos->srcUriIsFile) {
222 if (infos->srcPath == infos->destPath) {
223 LOGE("The src and dest is same");
224 return E_OK;
225 }
226 int32_t ret = CheckOrCreatePath(infos->destPath);
227 if (ret != E_OK) {
228 LOGE("check or create fail, error code is %{public}d", ret);
229 return ret;
230 }
231 infos->localListener->AddListenerFile(infos->destPath, IN_MODIFY);
232 return CopyFile(infos->srcPath, infos->destPath, infos);
233 }
234
235 bool destIsDirectory;
236 auto ret = FileSizeUtils::IsDirectory(infos->destUri, false, destIsDirectory);
237 if (ret != E_OK) {
238 LOGE("destPath: %{public}s not find, error=%{public}d", infos->destPath.c_str(), ret);
239 return ret;
240 }
241 if (destIsDirectory) {
242 if (infos->srcPath.back() != '/') {
243 infos->srcPath += '/';
244 }
245 if (infos->destPath.back() != '/') {
246 infos->destPath += '/';
247 }
248 // copyDir
249 return CopyDirFunc(infos->srcPath, infos->destPath, infos);
250 }
251 LOGI("ExecLocal not support this srcUri and destUri");
252 return E_NOENT;
253 }
254
CopyFile(const std::string & src,const std::string & dest,std::shared_ptr<FileInfos> infos)255 int32_t FileCopyManager::CopyFile(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
256 {
257 LOGI("src = %{private}s, dest = %{private}s", src.c_str(), dest.c_str());
258 infos->localListener->AddFile(dest);
259 int32_t srcFd = -1;
260 int32_t ret = OpenSrcFile(src, infos, srcFd);
261 if (srcFd < 0) {
262 return ret;
263 }
264 auto destFd = open(dest.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
265 if (destFd < 0) {
266 LOGE("Error opening dest file descriptor. errno = %{public}d", errno);
267 close(srcFd);
268 return errno;
269 }
270 std::shared_ptr<FDGuard> srcFdg = std::make_shared<FDGuard>(srcFd, true);
271 std::shared_ptr<FDGuard> destFdg = std::make_shared<FDGuard>(destFd, true);
272 if (srcFdg == nullptr || destFdg == nullptr) {
273 LOGE("Failed to request heap memory.");
274 close(srcFd);
275 close(destFd);
276 return -1;
277 }
278 return SendFileCore(srcFdg, destFdg, infos);
279 }
280
fs_req_cleanup(uv_fs_t * req)281 void fs_req_cleanup(uv_fs_t* req)
282 {
283 uv_fs_req_cleanup(req);
284 if (req) {
285 delete req;
286 req = nullptr;
287 }
288 }
289
SendFileCore(std::shared_ptr<FDGuard> srcFdg,std::shared_ptr<FDGuard> destFdg,std::shared_ptr<FileInfos> infos)290 int32_t FileCopyManager::SendFileCore(std::shared_ptr<FDGuard> srcFdg,
291 std::shared_ptr<FDGuard> destFdg, std::shared_ptr<FileInfos> infos)
292 {
293 std::unique_ptr<uv_fs_t, decltype(fs_req_cleanup)*> sendFileReq = {
294 new (std::nothrow) uv_fs_t, fs_req_cleanup };
295 if (sendFileReq == nullptr) {
296 LOGE("Failed to request heap memory.");
297 return -1;
298 }
299 int64_t offset = 0;
300 struct stat srcStat{};
301 if (fstat(srcFdg->GetFD(), &srcStat) < 0) {
302 LOGE("Failed to get stat of file by fd: %{public}d ,errno = %{public}d", srcFdg->GetFD(), errno);
303 return errno;
304 }
305 int32_t ret = 0;
306 int64_t size = static_cast<int64_t>(srcStat.st_size);
307
308 while (size >= 0) {
309 ret = uv_fs_sendfile(nullptr, sendFileReq.get(), destFdg->GetFD(), srcFdg->GetFD(),
310 offset, MAX_SIZE, nullptr);
311 if (ret < 0) {
312 LOGE("Failed to sendfile by errno : %{public}d", errno);
313 return errno;
314 }
315
316 if (infos->needCancel.load()) {
317 LOGE("need cancel");
318 return FileManagement::E_DFS_CANCEL_SUCCESS; // 204
319 }
320
321 offset += static_cast<int64_t>(ret);
322 size -= static_cast<int64_t>(ret);
323 if (ret == 0) {
324 break;
325 }
326 }
327
328 if (size != 0) {
329 LOGE("The execution of the sendfile task was terminated, remaining file size %{public}" PRIu64, size);
330 return E_OK; // EIO
331 }
332 return E_OK;
333 }
334
CopyDirFunc(const std::string & src,const std::string & dest,std::shared_ptr<FileInfos> infos)335 int32_t FileCopyManager::CopyDirFunc(const std::string &src, const std::string &dest, std::shared_ptr<FileInfos> infos)
336 {
337 LOGI("CopyDirFunc in, src = %{private}s, dest = %{private}s", src.c_str(), dest.c_str());
338 size_t found = dest.find(src);
339 if (found != std::string::npos && found == 0) {
340 LOGE("not support copy src to dest");
341 return -1;
342 }
343
344 // 获取src目录的目录名称
345 std::filesystem::path srcPath = std::filesystem::u8path(src);
346 std::string dirName;
347 if (srcPath.has_parent_path()) {
348 dirName = srcPath.parent_path().filename();
349 }
350
351 // 构造要拷贝到的路径
352 std::string destStr = dest + "/" + dirName;
353 return CopySubDir(src, destStr, infos);
354 }
355
CopySubDir(const std::string & srcPath,const std::string & destPath,std::shared_ptr<FileInfos> infos)356 int32_t FileCopyManager::CopySubDir(const std::string &srcPath,
357 const std::string &destPath, std::shared_ptr<FileInfos> infos)
358 {
359 std::error_code errCode;
360 if (!std::filesystem::exists(destPath, errCode) && errCode.value() == E_OK) {
361 int res = MakeDir(destPath);
362 if (res != E_OK) {
363 LOGE("Failed to mkdir");
364 return res;
365 }
366 } else if (errCode.value() != E_OK) {
367 LOGE("fs exists fail, errcode is %{public}d", errCode.value());
368 return errCode.value();
369 }
370 {
371 std::lock_guard<std::mutex> lock(infos->subDirsMutex);
372 infos->subDirs.insert(destPath);
373 }
374 infos->localListener->AddListenerFile(destPath, IN_MODIFY);
375 return RecurCopyDir(srcPath, destPath, infos);
376 }
377
RecurCopyDir(const std::string & srcPath,const std::string & destPath,std::shared_ptr<FileInfos> infos)378 int32_t FileCopyManager::RecurCopyDir(const std::string &srcPath,
379 const std::string &destPath, std::shared_ptr<FileInfos> infos)
380 {
381 auto pNameList = FileSizeUtils::GetDirNameList(srcPath);
382 if (pNameList == nullptr) {
383 return ENOMEM;
384 }
385 for (int i = 0; i < pNameList->direntNum; i++) {
386 std::string src = srcPath + '/' + std::string((pNameList->namelist[i])->d_name);
387 std::string dest = destPath + '/' + std::string((pNameList->namelist[i])->d_name);
388 if ((pNameList->namelist[i])->d_type == DT_LNK) {
389 continue;
390 }
391 int ret = E_OK;
392 if ((pNameList->namelist[i])->d_type == DT_DIR) {
393 ret = CopySubDir(src, dest, infos);
394 } else {
395 ret = CopyFile(src, dest, infos);
396 }
397 if (ret != E_OK) {
398 return ret;
399 }
400 }
401 return E_OK;
402 }
403
AddFileInfos(std::shared_ptr<FileInfos> infos)404 void FileCopyManager::AddFileInfos(std::shared_ptr<FileInfos> infos)
405 {
406 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
407 FileInfosVec_.push_back(infos);
408 }
409
RemoveFileInfos(std::shared_ptr<FileInfos> infos)410 void FileCopyManager::RemoveFileInfos(std::shared_ptr<FileInfos> infos)
411 {
412 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
413 for (auto it = FileInfosVec_.begin(); it != FileInfosVec_.end();) {
414 if ((*it)->srcUri == infos->srcUri && (*it)->destUri == infos->destUri) {
415 it = FileInfosVec_.erase(it);
416 } else {
417 ++it;
418 }
419 }
420 }
421
CreateFileInfos(const std::string & srcUri,const std::string & destUri,std::shared_ptr<FileInfos> & infos)422 int32_t FileCopyManager::CreateFileInfos(const std::string &srcUri,
423 const std::string &destUri, std::shared_ptr<FileInfos> &infos)
424 {
425 infos->srcUri = srcUri;
426 infos->destUri = destUri;
427 infos->srcPath = FileSizeUtils::GetPathFromUri(srcUri, true);
428 infos->destPath = FileSizeUtils::GetPathFromUri(destUri, false);
429
430 bool isDirectory;
431 auto ret = FileSizeUtils::IsDirectory(infos->srcUri, true, isDirectory);
432 if (ret != E_OK) {
433 LOGE("srcPath: %{public}s not find, err=%{public}d", infos->srcPath.c_str(), ret);
434 return ret;
435 }
436 infos->srcUriIsFile = IsMediaUri(infos->srcUri) || !isDirectory;
437 AddFileInfos(infos);
438 return E_OK;
439 }
440
GetModeFromFlags(unsigned int flags)441 std::string GetModeFromFlags(unsigned int flags)
442 {
443 const std::string readMode = "r";
444 const std::string writeMode = "w";
445 const std::string appendMode = "a";
446 const std::string truncMode = "t";
447 std::string mode = readMode;
448 mode += (((flags & O_RDWR) == O_RDWR) ? writeMode : "");
449 mode = (((flags & O_WRONLY) == O_WRONLY) ? writeMode : mode);
450 if (mode != readMode) {
451 mode += ((flags & O_TRUNC) ? truncMode : "");
452 mode += ((flags & O_APPEND) ? appendMode : "");
453 }
454 return mode;
455 }
456
OpenSrcFile(const std::string & srcPth,std::shared_ptr<FileInfos> infos,int32_t & srcFd)457 int32_t FileCopyManager::OpenSrcFile(const std::string &srcPth, std::shared_ptr<FileInfos> infos, int32_t &srcFd)
458 {
459 Uri uri(infos->srcUri);
460
461 if (uri.GetAuthority() == MEDIA) {
462 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
463 sptr<FileIoToken> remote = new (std::nothrow) IRemoteStub<FileIoToken>();
464 if (!remote) {
465 LOGE("Failed to get remote object");
466 return -1;
467 }
468 dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
469 if (!dataShareHelper) {
470 LOGE("Failed to connect to datashare");
471 return -1;
472 }
473 srcFd = dataShareHelper->OpenFile(uri, GetModeFromFlags(O_RDONLY));
474 if (srcFd < 0) {
475 LOGE("Open media uri by data share fail. ret = %{public}d", srcFd);
476 return -1;
477 }
478 } else {
479 srcFd = open(srcPth.c_str(), O_RDONLY);
480 if (srcFd < 0) {
481 LOGE("Error opening src file descriptor. errno = %{public}d", errno);
482 return errno;
483 }
484 }
485 return 0;
486 }
487
MakeDir(const std::string & path)488 int FileCopyManager::MakeDir(const std::string &path)
489 {
490 std::filesystem::path destDir(path);
491 std::error_code errCode;
492 if (!std::filesystem::create_directory(destDir, errCode)) {
493 LOGE("Failed to create directory, error code: %{public}d", errCode.value());
494 return errCode.value();
495 }
496 return E_OK;
497 }
498
IsRemoteUri(const std::string & uri)499 bool FileCopyManager::IsRemoteUri(const std::string &uri)
500 {
501 // NETWORK_PARA
502 return uri.find(NETWORK_PARA) != uri.npos;
503 }
504
IsMediaUri(const std::string & uriPath)505 bool FileCopyManager::IsMediaUri(const std::string &uriPath)
506 {
507 Uri uri(uriPath);
508 std::string bundleName = uri.GetAuthority();
509 return bundleName == MEDIA;
510 }
511
CheckOrCreatePath(const std::string & destPath)512 int32_t FileCopyManager::CheckOrCreatePath(const std::string &destPath)
513 {
514 std::error_code errCode;
515 if (!std::filesystem::exists(destPath, errCode) && errCode.value() == E_OK) {
516 LOGI("destPath not exist");
517 auto file = open(destPath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
518 if (file < 0) {
519 LOGE("Error opening file descriptor. errno = %{public}d", errno);
520 return errno;
521 }
522 close(file);
523 } else if (errCode.value() != 0) {
524 return errCode.value();
525 }
526 return E_OK;
527 }
528 } // namespace DistributedFile
529 } // namespace Storage
530 } // namespace OHOS
531