1 /*
2 * Copyright (c) 2023 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.h"
17
18 #include <cstring>
19 #include <fcntl.h>
20 #include <filesystem>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <tuple>
24 #include <unistd.h>
25
26 #include "common_func.h"
27 #include "file_utils.h"
28 #include "filemgmt_libhilog.h"
29
30 namespace OHOS {
31 namespace FileManagement {
32 namespace ModuleFileIO {
33 using namespace std;
34 using namespace OHOS::FileManagement::LibN;
35
IsAllPath(FileInfo & srcFile,FileInfo & destFile)36 static NError IsAllPath(FileInfo& srcFile, FileInfo& destFile)
37 {
38 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
39 filesystem::path srcPath(string(srcFile.path.get()));
40 filesystem::path dstPath(string(destFile.path.get()));
41 error_code errCode;
42 if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
43 HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
44 return NError(errCode.value());
45 }
46 #else
47 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> copyfile_req = {
48 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
49 if (!copyfile_req) {
50 HILOGE("Failed to request heap memory.");
51 return NError(ENOMEM);
52 }
53 int ret = uv_fs_copyfile(nullptr, copyfile_req.get(), srcFile.path.get(), destFile.path.get(),
54 UV_FS_COPYFILE_FICLONE, nullptr);
55 if (ret < 0) {
56 HILOGE("Failed to copy file when all parameters are paths");
57 return NError(ret);
58 }
59 #endif
60 return NError(ERRNO_NOERR);
61 }
62
SendFileCore(FileInfo & srcFdg,FileInfo & destFdg,struct stat & statbf)63 static NError SendFileCore(FileInfo& srcFdg, FileInfo& destFdg, struct stat& statbf)
64 {
65 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> sendfile_req = {
66 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
67 if (!sendfile_req) {
68 HILOGE("Failed to request heap memory.");
69 return NError(ENOMEM);
70 }
71 int64_t offset = 0;
72 size_t size = static_cast<size_t>(statbf.st_size);
73 int ret = 0;
74 while (size > 0) {
75 ret = uv_fs_sendfile(nullptr, sendfile_req.get(), destFdg.fdg->GetFD(), srcFdg.fdg->GetFD(),
76 offset, std::min(MAX_SIZE, size), nullptr);
77 if (ret < 0) {
78 HILOGE("Failed to sendfile by ret : %{public}d", ret);
79 return NError(ret);
80 }
81 if (static_cast<size_t>(ret) > size) {
82 HILOGE("More bytes returned than the size of the file. The file size is %{public}zu" \
83 "The bytes returned is %{public}d", size, ret);
84 return NError(EIO);
85 }
86 offset += static_cast<int64_t>(ret);
87 size -= static_cast<size_t>(ret);
88 if (ret == 0) {
89 break;
90 }
91 }
92 if (size != 0) {
93 HILOGE("The execution of the sendfile task was terminated, remaining file size %{public}zu", size);
94 return NError(EIO);
95 }
96 return NError(ERRNO_NOERR);
97 }
98
TruncateCore(const FileInfo & fileInfo)99 static NError TruncateCore(const FileInfo& fileInfo)
100 {
101 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> ftruncate_req = {
102 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
103 if (!ftruncate_req) {
104 HILOGE("Failed to request heap memory.");
105 return NError(ENOMEM);
106 }
107 int ret = uv_fs_ftruncate(nullptr, ftruncate_req.get(), fileInfo.fdg->GetFD(), 0, nullptr);
108 if (ret < 0) {
109 HILOGE("Failed to truncate dstFile with ret: %{public}d", ret);
110 return NError(ret);
111 }
112 return NError(ERRNO_NOERR);
113 }
114
OpenCore(FileInfo & fileInfo,const int flags,const int mode)115 static NError OpenCore(FileInfo& fileInfo, const int flags, const int mode)
116 {
117 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
118 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
119 if (!open_req) {
120 HILOGE("Failed to request heap memory.");
121 return NError(ENOMEM);
122 }
123 int ret = uv_fs_open(nullptr, open_req.get(), fileInfo.path.get(), flags, mode, nullptr);
124 if (ret < 0) {
125 HILOGE("Failed to open srcFile with ret: %{public}d", ret);
126 return NError(ret);
127 }
128 fileInfo.fdg = CreateUniquePtr<DistributedFS::FDGuard>(ret, true);
129 if (fileInfo.fdg == nullptr) {
130 HILOGE("Failed to request heap memory.");
131 close(ret);
132 return NError(ENOMEM);
133 }
134 return NError(ERRNO_NOERR);
135 }
136
OpenFile(FileInfo & srcFile,FileInfo & destFile)137 static NError OpenFile(FileInfo& srcFile, FileInfo& destFile)
138 {
139 if (srcFile.isPath) {
140 auto openResult = OpenCore(srcFile, UV_FS_O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
141 if (openResult) {
142 return openResult;
143 }
144 }
145 struct stat statbf;
146 if (fstat(srcFile.fdg->GetFD(), &statbf) < 0) {
147 HILOGE("Failed to get stat of file by fd: %{public}d", srcFile.fdg->GetFD());
148 return NError(errno);
149 }
150 if (destFile.isPath) {
151 auto openResult = OpenCore(destFile, UV_FS_O_RDWR | UV_FS_O_CREAT |
152 UV_FS_O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
153 if (openResult) {
154 return openResult;
155 }
156 } else {
157 auto truncateResult = TruncateCore(destFile);
158 if (truncateResult) {
159 return truncateResult;
160 }
161 auto ret = lseek(destFile.fdg->GetFD(), 0, SEEK_SET);
162 if (ret < 0) {
163 HILOGE("Failed to lseek destFile, errno: %{public}d", errno);
164 return NError(errno);
165 }
166 }
167 if (statbf.st_size == 0) {
168 return NError(ERRNO_NOERR);
169 }
170 return SendFileCore(srcFile, destFile, statbf);
171 }
172
ParseJsMode(napi_env env,const NFuncArg & funcArg)173 static tuple<bool, int> ParseJsMode(napi_env env, const NFuncArg& funcArg)
174 {
175 bool succ = false;
176 int mode = 0;
177 if (funcArg.GetArgc() >= NARG_CNT::THREE) {
178 tie(succ, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32(mode);
179 if (!succ || mode) {
180 return { false, mode };
181 }
182 }
183 return { true, mode };
184 }
185
ParseJsOperand(napi_env env,NVal pathOrFdFromJsArg)186 static tuple<bool, FileInfo> ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg)
187 {
188 auto [isPath, path, ignore] = pathOrFdFromJsArg.ToUTF8StringPath();
189 if (isPath) {
190 return { true, FileInfo { true, move(path), {} } };
191 }
192
193 auto [isFd, fd] = pathOrFdFromJsArg.ToInt32();
194 if (isFd) {
195 auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
196 if (fdg == nullptr) {
197 HILOGE("Failed to request heap memory.");
198 close(fd);
199 NError(ENOMEM).ThrowErr(env);
200 return { false, FileInfo { false, {}, {} } };
201 }
202 return { true, FileInfo { false, {}, move(fdg) } };
203 }
204
205 return { false, FileInfo { false, {}, {} } };
206 };
207
Sync(napi_env env,napi_callback_info info)208 napi_value CopyFile::Sync(napi_env env, napi_callback_info info)
209 {
210 NFuncArg funcArg(env, info);
211 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
212 HILOGE("Number of arguments unmatched");
213 NError(EINVAL).ThrowErr(env);
214 return nullptr;
215 }
216
217 auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
218 auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] });
219 if (!succSrc || !succDest) {
220 HILOGE("The first/second argument requires filepath/fd");
221 NError(EINVAL).ThrowErr(env);
222 return nullptr;
223 }
224
225 auto [succMode, mode] = ParseJsMode(env, funcArg);
226 if (!succMode) {
227 HILOGE("Failed to convert mode to int32");
228 NError(EINVAL).ThrowErr(env);
229 return nullptr;
230 }
231
232 if (src.isPath && dest.isPath) {
233 auto err = IsAllPath(src, dest);
234 if (err) {
235 err.ThrowErr(env);
236 return nullptr;
237 }
238 } else {
239 auto err = OpenFile(src, dest);
240 if (err) {
241 err.ThrowErr(env);
242 return nullptr;
243 }
244 }
245 return NVal::CreateUndefined(env).val_;
246 }
247
Async(napi_env env,napi_callback_info info)248 napi_value CopyFile::Async(napi_env env, napi_callback_info info)
249 {
250 NFuncArg funcArg(env, info);
251 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
252 HILOGE("Number of arguments unmatched");
253 NError(EINVAL).ThrowErr(env);
254 return nullptr;
255 }
256
257 auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
258 auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] });
259 if (!succSrc || !succDest) {
260 HILOGE("The first/second argument requires filepath/fd");
261 NError(EINVAL).ThrowErr(env);
262 return nullptr;
263 }
264
265 auto [succMode, mode] = ParseJsMode(env, funcArg);
266 if (!succMode) {
267 HILOGE("Failed to convert mode to int32");
268 NError(EINVAL).ThrowErr(env);
269 return nullptr;
270 }
271
272 auto para = CreateSharedPtr<Para>(move(src), move(dest));
273 if (para == nullptr) {
274 HILOGE("Failed to request heap memory.");
275 NError(ENOMEM).ThrowErr(env);
276 return nullptr;
277 }
278 auto cbExec = [para]() -> NError {
279 if (para->src_.isPath && para->dest_.isPath) {
280 return IsAllPath(para->src_, para->dest_);
281 }
282 return OpenFile(para->src_, para->dest_);
283 };
284
285 auto cbCompl = [](napi_env env, NError err) -> NVal {
286 if (err) {
287 return { env, err.GetNapiErr(env) };
288 }
289 return { NVal::CreateUndefined(env) };
290 };
291
292 NVal thisVar(env, funcArg.GetThisVar());
293 if (funcArg.GetArgc() == NARG_CNT::TWO || (funcArg.GetArgc() == NARG_CNT::THREE &&
294 !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function))) {
295 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
296 } else {
297 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH)]);
298 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
299 }
300 }
301 } // namespace ModuleFileIO
302 } // namespace FileManagement
303 } // namespace OHOS
304