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, MAX_SIZE, nullptr);
77 if (ret < 0) {
78 HILOGE("Failed to sendfile by ret : %{public}d", ret);
79 return NError(ret);
80 }
81 offset += static_cast<int64_t>(ret);
82 size -= static_cast<size_t>(ret);
83 if (ret == 0) {
84 break;
85 }
86 }
87 if (size != 0) {
88 HILOGE("The execution of the sendfile task was terminated, remaining file size %{public}zu", size);
89 return NError(EIO);
90 }
91 return NError(ERRNO_NOERR);
92 }
93
TruncateCore(const FileInfo & fileInfo)94 static NError TruncateCore(const FileInfo& fileInfo)
95 {
96 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> ftruncate_req = {
97 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
98 if (!ftruncate_req) {
99 HILOGE("Failed to request heap memory.");
100 return NError(ENOMEM);
101 }
102 int ret = uv_fs_ftruncate(nullptr, ftruncate_req.get(), fileInfo.fdg->GetFD(), 0, nullptr);
103 if (ret < 0) {
104 HILOGE("Failed to truncate dstFile with ret: %{public}d", ret);
105 return NError(ret);
106 }
107 return NError(ERRNO_NOERR);
108 }
109
OpenCore(FileInfo & fileInfo,const int flags,const int mode)110 static NError OpenCore(FileInfo& fileInfo, const int flags, const int mode)
111 {
112 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
113 new (nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
114 if (!open_req) {
115 HILOGE("Failed to request heap memory.");
116 return NError(ENOMEM);
117 }
118 int ret = uv_fs_open(nullptr, open_req.get(), fileInfo.path.get(), flags, mode, nullptr);
119 if (ret < 0) {
120 HILOGE("Failed to open srcFile with ret: %{public}d", ret);
121 return NError(ret);
122 }
123 fileInfo.fdg = CreateUniquePtr<DistributedFS::FDGuard>(ret, true);
124 if (fileInfo.fdg == nullptr) {
125 HILOGE("Failed to request heap memory.");
126 return NError(ENOMEM);
127 }
128 return NError(ERRNO_NOERR);
129 }
130
OpenFile(FileInfo & srcFile,FileInfo & destFile)131 static NError OpenFile(FileInfo& srcFile, FileInfo& destFile)
132 {
133 if (srcFile.isPath) {
134 auto openResult = OpenCore(srcFile, UV_FS_O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
135 if (openResult) {
136 return openResult;
137 }
138 }
139 struct stat statbf;
140 if (fstat(srcFile.fdg->GetFD(), &statbf) < 0) {
141 HILOGE("Failed to get stat of file by fd: %{public}d", srcFile.fdg->GetFD());
142 return NError(errno);
143 }
144 if (destFile.isPath) {
145 auto openResult = OpenCore(destFile, UV_FS_O_RDWR | UV_FS_O_CREAT |
146 UV_FS_O_TRUNC, statbf.st_mode);
147 if (openResult) {
148 return openResult;
149 }
150 } else {
151 auto truncateResult = TruncateCore(destFile);
152 if (truncateResult) {
153 return truncateResult;
154 }
155 auto ret = lseek(destFile.fdg->GetFD(), 0, SEEK_SET);
156 if (ret < 0) {
157 HILOGE("Failed to lseek destFile, errno: %{public}d", errno);
158 return NError(errno);
159 }
160 }
161 return SendFileCore(srcFile, destFile, statbf);
162 }
163
ParseJsMode(napi_env env,const NFuncArg & funcArg)164 static tuple<bool, int> ParseJsMode(napi_env env, const NFuncArg& funcArg)
165 {
166 bool succ = false;
167 int mode = 0;
168 if (funcArg.GetArgc() >= NARG_CNT::THREE) {
169 tie(succ, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32(mode);
170 if (!succ || mode) {
171 return { false, mode };
172 }
173 }
174 return { true, mode };
175 }
176
ParseJsOperand(napi_env env,NVal pathOrFdFromJsArg)177 static tuple<bool, FileInfo> ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg)
178 {
179 auto [isPath, path, ignore] = pathOrFdFromJsArg.ToUTF8String();
180 if (isPath) {
181 return { true, FileInfo { true, move(path), {} } };
182 }
183
184 auto [isFd, fd] = pathOrFdFromJsArg.ToInt32();
185 if (isFd) {
186 auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
187 if (fdg == nullptr) {
188 HILOGE("Failed to request heap memory.");
189 NError(ENOMEM).ThrowErr(env);
190 return { false, FileInfo { false, {}, {} } };
191 }
192 return { true, FileInfo { false, {}, move(fdg) } };
193 }
194
195 return { false, FileInfo { false, {}, {} } };
196 };
197
Sync(napi_env env,napi_callback_info info)198 napi_value CopyFile::Sync(napi_env env, napi_callback_info info)
199 {
200 NFuncArg funcArg(env, info);
201 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
202 HILOGE("Number of arguments unmatched");
203 NError(EINVAL).ThrowErr(env);
204 return nullptr;
205 }
206
207 auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
208 auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] });
209 if (!succSrc || !succDest) {
210 HILOGE("The first/second argument requires filepath/fd");
211 NError(EINVAL).ThrowErr(env);
212 return nullptr;
213 }
214
215 auto [succMode, mode] = ParseJsMode(env, funcArg);
216 if (!succMode) {
217 HILOGE("Failed to convert mode to int32");
218 NError(EINVAL).ThrowErr(env);
219 return nullptr;
220 }
221
222 if (src.isPath && dest.isPath) {
223 auto err = IsAllPath(src, dest);
224 if (err) {
225 err.ThrowErr(env);
226 return nullptr;
227 }
228 } else {
229 auto err = OpenFile(src, dest);
230 if (err) {
231 err.ThrowErr(env);
232 return nullptr;
233 }
234 }
235 return NVal::CreateUndefined(env).val_;
236 }
237
Async(napi_env env,napi_callback_info info)238 napi_value CopyFile::Async(napi_env env, napi_callback_info info)
239 {
240 NFuncArg funcArg(env, info);
241 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
242 HILOGE("Number of arguments unmatched");
243 NError(EINVAL).ThrowErr(env);
244 return nullptr;
245 }
246
247 auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
248 auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] });
249 if (!succSrc || !succDest) {
250 HILOGE("The first/second argument requires filepath/fd");
251 NError(EINVAL).ThrowErr(env);
252 return nullptr;
253 }
254
255 auto [succMode, mode] = ParseJsMode(env, funcArg);
256 if (!succMode) {
257 HILOGE("Failed to convert mode to int32");
258 NError(EINVAL).ThrowErr(env);
259 return nullptr;
260 }
261
262 auto para = CreateSharedPtr<Para>(move(src), move(dest));
263 if (para == nullptr) {
264 HILOGE("Failed to request heap memory.");
265 NError(ENOMEM).ThrowErr(env);
266 return nullptr;
267 }
268 auto cbExec = [para]() -> NError {
269 if (para->src_.isPath && para->dest_.isPath) {
270 return IsAllPath(para->src_, para->dest_);
271 }
272 return OpenFile(para->src_, para->dest_);
273 };
274
275 auto cbCompl = [](napi_env env, NError err) -> NVal {
276 if (err) {
277 return { env, err.GetNapiErr(env) };
278 }
279 return { NVal::CreateUndefined(env) };
280 };
281
282 NVal thisVar(env, funcArg.GetThisVar());
283 if (funcArg.GetArgc() == NARG_CNT::TWO || (funcArg.GetArgc() == NARG_CNT::THREE &&
284 !NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function))) {
285 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
286 } else {
287 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH)]);
288 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
289 }
290 }
291 } // namespace ModuleFileIO
292 } // namespace FileManagement
293 } // namespace OHOS