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 "pasteboard_copy.h"
17
18 #include "copy/file_copy_manager.h"
19 #include "file_uri.h"
20 #include "pasteboard_error.h"
21 #include "pasteboard_hilog.h"
22
23 namespace OHOS {
24 namespace MiscServices {
25 using namespace AppFileService::ModuleFileUri;
26 const std::string NETWORK_PARA = "?networkid=";
27 constexpr int PERCENTAGE = 100;
28 constexpr int ERRNO_NOERR = 0;
29 constexpr int E_EXIST = 17;
30 constexpr float FILE_PERCENTAGE = 0.8;
31 constexpr int BEGIN_PERCENTAGE = 20;
32 constexpr int DFS_CANCEL_SUCCESS = 204;
33
34 static int32_t g_recordSize = 0;
35 ProgressListener PasteBoardCopyFile::progressListener_;
36 std::atomic_bool PasteBoardCopyFile::canCancel_{ true };
37
GetInstance()38 PasteBoardCopyFile &PasteBoardCopyFile::GetInstance()
39 {
40 static PasteBoardCopyFile instance;
41 return instance;
42 }
43
IsDirectory(const std::string & path)44 bool PasteBoardCopyFile::IsDirectory(const std::string &path)
45 {
46 struct stat buf {};
47 int ret = stat(path.c_str(), &buf);
48 if (ret == -1) {
49 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Stat failed. errno=%{public}d", errno);
50 return false;
51 }
52 return (buf.st_mode & S_IFMT) == S_IFDIR;
53 }
54
IsFile(const std::string & path)55 bool PasteBoardCopyFile::IsFile(const std::string &path)
56 {
57 struct stat buf {};
58 int ret = stat(path.c_str(), &buf);
59 if (ret == -1) {
60 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Stat failed. errno = %{public}d", errno);
61 return false;
62 }
63 return (buf.st_mode & S_IFMT) == S_IFREG;
64 }
65
OnProgressNotify(std::shared_ptr<GetDataParams> params)66 void PasteBoardCopyFile::OnProgressNotify(std::shared_ptr<GetDataParams> params)
67 {
68 if (params == nullptr) {
69 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "params is null!");
70 return;
71 }
72
73 if (params->info == nullptr) {
74 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "params->info is null!");
75 return;
76 }
77
78 if (params->info->percentage > PERCENTAGE) {
79 params->info->percentage = PERCENTAGE;
80 }
81 params->info->percentage = static_cast<int32_t>(params->info->percentage * FILE_PERCENTAGE + BEGIN_PERCENTAGE);
82 params->info->percentage = std::abs(params->info->percentage);
83 params->info->percentage = std::max(params->info->percentage, 0);
84 if (progressListener_.ProgressNotify != nullptr) {
85 progressListener_.ProgressNotify(params);
86 } else {
87 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "ProgressNotify is nullptr.");
88 }
89 }
90
GetRealPath(const std::string & path)91 std::string PasteBoardCopyFile::GetRealPath(const std::string& path)
92 {
93 std::filesystem::path tempPath(path);
94 std::filesystem::path realPath{};
95 for (const auto& component : tempPath) {
96 if (component == ".") {
97 continue;
98 } else if (component == "..") {
99 realPath = realPath.parent_path();
100 } else {
101 realPath /= component;
102 }
103 }
104 return realPath.string();
105 }
106
IsRemoteUri(const std::string & uri)107 bool PasteBoardCopyFile::IsRemoteUri(const std::string &uri)
108 {
109 return uri.find(NETWORK_PARA) != uri.npos;
110 }
111
CheckCopyParam(PasteData & pasteData,std::shared_ptr<GetDataParams> dataParams)112 int32_t PasteBoardCopyFile::CheckCopyParam(PasteData &pasteData, std::shared_ptr<GetDataParams> dataParams)
113 {
114 if (dataParams == nullptr) {
115 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Invalid dataParams");
116 return static_cast<int32_t>(PasteboardError::INVALID_PARAM_ERROR);
117 }
118 g_recordSize = (int32_t)pasteData.GetRecordCount();
119 if (g_recordSize <= 0) {
120 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Invalid records size");
121 return static_cast<int32_t>(PasteboardError::INVALID_PARAM_ERROR);
122 }
123 return static_cast<int32_t>(PasteboardError::E_OK);
124 }
125
InitCopyInfo(const std::string srcUri,std::shared_ptr<GetDataParams> dataParams,std::shared_ptr<CopyInfo> copyInfo,int32_t index)126 int32_t PasteBoardCopyFile::InitCopyInfo(const std::string srcUri, std::shared_ptr<GetDataParams> dataParams,
127 std::shared_ptr<CopyInfo> copyInfo, int32_t index)
128 {
129 copyInfo->srcUri = srcUri;
130 copyInfo->destUri = dataParams->destUri;
131 FileUri srcFileUri(copyInfo->srcUri);
132 copyInfo->srcPath = srcFileUri.GetRealPath();
133 FileUri destFileUri(copyInfo->destUri);
134 copyInfo->destPath = destFileUri.GetPath();
135 copyInfo->srcPath = GetRealPath(copyInfo->srcPath);
136 copyInfo->destPath = GetRealPath(copyInfo->destPath);
137 std::string realSrc = copyInfo->srcPath;
138 if (IsRemoteUri(copyInfo->srcUri)) {
139 uint32_t index = copyInfo->srcPath.rfind("?", 0);
140 realSrc = copyInfo->srcPath.substr(0, index);
141 }
142 if (IsDirectory(copyInfo->destPath)) {
143 std::filesystem::path filePath(realSrc);
144 auto fileName = filePath.filename();
145 if (copyInfo->destUri.back() != '/') {
146 copyInfo->destUri += '/';
147 }
148 copyInfo->destUri += fileName.string();
149 FileUri realDest(copyInfo->destUri);
150 copyInfo->destPath = realDest.GetPath();
151 copyInfo->destPath = GetRealPath(copyInfo->destPath);
152 }
153 std::error_code errCode;
154 if (std::filesystem::exists(copyInfo->destPath, errCode) && errCode.value() == ERRNO_NOERR &&
155 dataParams->fileConflictOption == FILE_SKIP) {
156 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "File has existed.");
157 return E_EXIST;
158 }
159 FileUri realFileUri(realSrc);
160 std::string realPath = realFileUri.GetRealPath();
161 realPath = GetRealPath(realPath);
162 if (!IsFile(realPath)) {
163 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "Softbus not support dir to remote.");
164 return ENOMEM;
165 }
166 return ERRNO_NOERR;
167 }
168
CopyFileData(PasteData & pasteData,std::shared_ptr<GetDataParams> dataParams)169 int32_t PasteBoardCopyFile::CopyFileData(PasteData &pasteData, std::shared_ptr<GetDataParams> dataParams)
170 {
171 int32_t ret = static_cast<int32_t>(PasteboardError::E_OK);
172 progressListener_ = dataParams->listener;
173 std::shared_ptr<PasteDataRecord> record = std::make_shared<PasteDataRecord>();
174 int32_t recordCount = 0;
175 int32_t recordProcessedIndex = 0;
176 for (size_t index = 0; index < pasteData.GetRecordCount();) {
177 recordProcessedIndex++;
178 if (ProgressSignalClient::GetInstance().CheckCancelIfNeed()) {
179 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "canceled success!");
180 pasteData.RemoveRecordAt(index);
181 continue;
182 }
183 record = pasteData.GetRecordAt(index);
184 if (record == nullptr) {
185 return static_cast<int32_t>(PasteboardError::INVALID_PARAM_ERROR);
186 }
187 std::shared_ptr<OHOS::Uri> uri = record->GetUriV0();
188 if (uri == nullptr) {
189 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "Record has no uri");
190 index++;
191 continue;
192 }
193 std::shared_ptr<CopyInfo> copyInfo = std::make_shared<CopyInfo>();
194 std::string srcUri = uri->ToString();
195 if (InitCopyInfo(srcUri, dataParams, copyInfo, recordCount) == E_EXIST) {
196 recordCount++;
197 pasteData.RemoveRecordAt(index);
198 continue;
199 }
200 recordCount++;
201 CopyInfo info = *copyInfo;
202 using ProcessCallBack = std::function<void(uint64_t processSize, uint64_t totalSize)>;
203 ProcessCallBack listener = [=](uint64_t processSize, uint64_t totalSize) {
204 HandleProgress(recordProcessedIndex, info, processSize, totalSize, dataParams);
205 };
206 ret = Storage::DistributedFile::FileCopyManager::GetInstance()->Copy(srcUri, copyInfo->destUri, listener);
207 if (!ShouldKeepRecord(ret, copyInfo->destUri, record)) {
208 pasteData.RemoveRecordAt(index);
209 } else {
210 index++;
211 }
212 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "DFS copy ret: %{public}d", ret);
213 }
214 return (ret == ERRNO_NOERR || ret == DFS_CANCEL_SUCCESS) ? static_cast<int32_t>(PasteboardError::E_OK) : ret;
215 }
216
ShouldKeepRecord(int32_t & ret,const std::string & destUri,std::shared_ptr<PasteDataRecord> record)217 bool PasteBoardCopyFile::ShouldKeepRecord(
218 int32_t &ret, const std::string &destUri, std::shared_ptr<PasteDataRecord> record)
219 {
220 if (record == nullptr) {
221 return false;
222 }
223 if ((ret == static_cast<int32_t>(PasteboardError::E_OK) || ret == ERRNO_NOERR) &&
224 !ProgressSignalClient::GetInstance().CheckCancelIfNeed()) {
225 auto sharedUri = std::make_shared<OHOS::Uri>(destUri);
226 record->SetUri(sharedUri);
227 record->SetConvertUri("");
228 return true;
229 } else if (record->GetFrom() > 0 && record->GetRecordId() != record->GetFrom() &&
230 !ProgressSignalClient::GetInstance().CheckCancelIfNeed()) {
231 ret = ERRNO_NOERR;
232 return true;
233 } else {
234 return false;
235 }
236 }
237
HandleProgress(int32_t index,const CopyInfo & info,uint64_t processSize,uint64_t totalSize,std::shared_ptr<GetDataParams> dataParams)238 void PasteBoardCopyFile::HandleProgress(int32_t index, const CopyInfo &info, uint64_t processSize, uint64_t totalSize,
239 std::shared_ptr<GetDataParams> dataParams)
240 {
241 if (dataParams == nullptr) {
242 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "dataParams is nullptr.");
243 return;
244 }
245
246 if (dataParams->info == nullptr) {
247 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "dataParams->info is nullptr.");
248 return;
249 }
250
251 if (index < 1 || totalSize == 0) {
252 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "invalid parameter");
253 return;
254 }
255
256 if (g_recordSize == 0) {
257 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "no record");
258 return;
259 }
260
261 if (ProgressSignalClient::GetInstance().CheckCancelIfNeed() && canCancel_.load()) {
262 PASTEBOARD_HILOGI(PASTEBOARD_MODULE_CLIENT, "Cancel copy.");
263 std::thread([info]() {
264 canCancel_.store(false);
265 auto ret = Storage::DistributedFile::FileCopyManager::GetInstance()->Cancel(info.srcUri, info.destUri);
266 if (ret != ERRNO_NOERR) {
267 canCancel_.store(true);
268 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Cancel failed. errno=%{public}d", ret);
269 }
270 }).detach();
271 return;
272 }
273
274 int32_t percentage = (int32_t)((PERCENTAGE * processSize) / totalSize);
275 int32_t totalProgress = ((index - 1) * PERCENTAGE + percentage) / g_recordSize;
276 dataParams->info->percentage = totalProgress;
277
278 PASTEBOARD_HILOGD(PASTEBOARD_MODULE_CLIENT, "process record index:%{public}d/%{public}d, progress=%{public}d",
279 index, g_recordSize, totalProgress);
280 OnProgressNotify(dataParams);
281 }
282
CopyPasteData(PasteData & pasteData,std::shared_ptr<GetDataParams> dataParams)283 int32_t PasteBoardCopyFile::CopyPasteData(PasteData &pasteData, std::shared_ptr<GetDataParams> dataParams)
284 {
285 g_recordSize = 0;
286 canCancel_.store(true);
287 int32_t ret = CheckCopyParam(pasteData, dataParams);
288 if (ret != static_cast<int32_t>(PasteboardError::E_OK)) {
289 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "Invalid copy params");
290 g_recordSize = 0;
291 return ret;
292 }
293 ret = CopyFileData(pasteData, dataParams);
294 if (ret != static_cast<int32_t>(PasteboardError::E_OK)) {
295 PASTEBOARD_HILOGE(PASTEBOARD_MODULE_CLIENT, "copy file failed, ret=%{public}d", ret);
296 ret = static_cast<int32_t>(PasteboardError::COPY_FILE_ERROR);
297 }
298 dataParams->info->percentage = PERCENTAGE;
299 OnProgressNotify(dataParams);
300 g_recordSize = 0;
301 return ret;
302 }
303 } // namespace MiscServices
304 } // namespace OHOS