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 <sys/sendfile.h>
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 "filemgmt_libhilog.h"
28
29 namespace OHOS {
30 namespace FileManagement {
31 namespace ModuleFileIO {
32 using namespace std;
33 using namespace OHOS::FileManagement::LibN;
34
IsAllPath(FileInfo & srcFile,FileInfo & destFile)35 static NError IsAllPath(FileInfo& srcFile, FileInfo& destFile)
36 {
37 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> copyfile_req = {
38 new uv_fs_t, CommonFunc::fs_req_cleanup };
39 if (!copyfile_req) {
40 HILOGE("Failed to request heap memory.");
41 return NError(ENOMEM);
42 }
43 int ret = uv_fs_copyfile(nullptr, copyfile_req.get(), srcFile.path.get(), destFile.path.get(),
44 UV_FS_COPYFILE_FICLONE, nullptr);
45 if (ret < 0) {
46 HILOGE("Failed to copy file when all parameters are paths");
47 return NError(errno);
48 }
49 return NError(ERRNO_NOERR);
50 }
51
SendFileCore(FileInfo & srcFdg,FileInfo & destFdg,struct stat & statbf)52 static NError SendFileCore(FileInfo& srcFdg, FileInfo& destFdg, struct stat& statbf)
53 {
54 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> sendfile_req = {
55 new uv_fs_t, CommonFunc::fs_req_cleanup };
56 if (!sendfile_req) {
57 HILOGE("Failed to request heap memory.");
58 return NError(ENOMEM);
59 }
60 int ret = uv_fs_sendfile(nullptr, sendfile_req.get(), destFdg.fdg->GetFD(), srcFdg.fdg->GetFD(), 0,
61 statbf.st_size, nullptr);
62 if (ret < 0) {
63 HILOGE("Failed to sendfile by ret : %{public}d", ret);
64 return NError(errno);
65 }
66 return NError(ERRNO_NOERR);
67 }
68
OpenFile(FileInfo & srcFile,FileInfo & destFile)69 static NError OpenFile(FileInfo& srcFile, FileInfo& destFile)
70 {
71 if (srcFile.isPath) {
72 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
73 new uv_fs_t, CommonFunc::fs_req_cleanup };
74 if (!open_req) {
75 HILOGE("Failed to request heap memory.");
76 return NError(ENOMEM);
77 }
78 int ret = uv_fs_open(nullptr, open_req.get(), srcFile.path.get(), O_RDWR,
79 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
80 if (ret < 0) {
81 HILOGE("Failed to open srcFile with ret: %{public}d", ret);
82 return NError(errno);
83 }
84 srcFile.fdg = make_unique<DistributedFS::FDGuard>(ret, true);
85 if (!srcFile.fdg) {
86 HILOGE("Failed to request heap memory for src file descriptor.");
87 return NError(ENOMEM);
88 }
89 }
90
91 struct stat statbf;
92 if (fstat(srcFile.fdg->GetFD(), &statbf) < 0) {
93 HILOGE("Failed to get stat of file by fd: %{public}d", srcFile.fdg->GetFD());
94 return NError(errno);
95 }
96
97 if (destFile.isPath) {
98 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
99 new uv_fs_t, CommonFunc::fs_req_cleanup };
100 if (!open_req) {
101 HILOGE("Failed to request heap memory.");
102 return NError(ENOMEM);
103 }
104 int ret = uv_fs_open(nullptr, open_req.get(), destFile.path.get(), O_RDWR | O_CREAT, statbf.st_mode, nullptr);
105 if (ret < 0) {
106 HILOGE("Failed to open destFile with ret: %{public}d", ret);
107 return NError(errno);
108 }
109 destFile.fdg = make_unique<DistributedFS::FDGuard>(ret, true);
110 if (!destFile.fdg) {
111 HILOGE("Failed to request heap memory for dest file descriptor.");
112 return NError(ENOMEM);
113 }
114 }
115 return SendFileCore(srcFile, destFile, statbf);
116 }
117
ParseJsModeAndProm(napi_env env,const NFuncArg & funcArg)118 static tuple<bool, int, bool> ParseJsModeAndProm(napi_env env, const NFuncArg& funcArg)
119 {
120 bool promise = false;
121 bool succ = false;
122 int mode = 0;
123 if (funcArg.GetArgc() == NARG_CNT::TWO) {
124 return { true, mode, true };
125 }
126 if (NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number)) {
127 if (funcArg.GetArgc() == NARG_CNT::THREE) {
128 promise = true;
129 }
130 tie(succ, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32();
131 if (succ && !mode) {
132 return { true, mode, promise };
133 }
134 }
135 if (NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function)) {
136 return { true, mode, promise };
137 }
138
139 return { false, mode, promise };
140 }
141
ParseJsOperand(napi_env env,NVal pathOrFdFromJsArg)142 static tuple<bool, FileInfo> ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg)
143 {
144 auto [isPath, path, ignore] = pathOrFdFromJsArg.ToUTF8String();
145 if (isPath) {
146 return { true, FileInfo { true, move(path), {} } };
147 }
148
149 auto [isFd, fd] = pathOrFdFromJsArg.ToInt32();
150 if (isFd) {
151 auto fdg = make_unique<DistributedFS::FDGuard>(fd, false);
152 return { true, FileInfo { false, {}, move(fdg) } };
153 }
154
155 return { false, FileInfo { false, {}, {} } };
156 };
157
Sync(napi_env env,napi_callback_info info)158 napi_value CopyFile::Sync(napi_env env, napi_callback_info info)
159 {
160 NFuncArg funcArg(env, info);
161 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
162 HILOGE("Number of arguments unmatched");
163 NError(EINVAL).ThrowErr(env);
164 return nullptr;
165 }
166
167 auto [succSrc, src] = ParseJsOperand(env, { env, funcArg[NARG_POS::FIRST] });
168 auto [succDest, dest] = ParseJsOperand(env, { env, funcArg[NARG_POS::SECOND] });
169 if (!succSrc || !succDest) {
170 HILOGE("The first/second argument requires filepath/fd");
171 NError(EINVAL).ThrowErr(env);
172 return nullptr;
173 }
174
175 auto [succMode, mode, unused] = ParseJsModeAndProm(env, funcArg);
176 if (!succMode || mode) {
177 HILOGE("Failed to convert mode to int32");
178 NError(EINVAL).ThrowErr(env);
179 return nullptr;
180 }
181
182 if (src.isPath && dest.isPath) {
183 auto err = IsAllPath(src, dest);
184 if (err) {
185 err.ThrowErr(env);
186 return nullptr;
187 }
188 } else {
189 auto err = OpenFile(src, dest);
190 if (err) {
191 err.ThrowErr(env);
192 return nullptr;
193 }
194 }
195 return NVal::CreateUndefined(env).val_;
196 }
197
Async(napi_env env,napi_callback_info info)198 napi_value CopyFile::Async(napi_env env, napi_callback_info info)
199 {
200 NFuncArg funcArg(env, info);
201 if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
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, promise] = ParseJsModeAndProm(env, funcArg);
216 if (!succMode || mode) {
217 HILOGE("Failed to convert mode to int32");
218 NError(EINVAL).ThrowErr(env);
219 return nullptr;
220 }
221
222 auto cbExec = [para = make_shared<Para>(move(src), move(dest))]() -> NError {
223 if (para->src_.isPath && para->dest_.isPath) {
224 return IsAllPath(para->src_, para->dest_);
225 }
226 return OpenFile(para->src_, para->dest_);
227 };
228
229 auto cbCompl = [](napi_env env, NError err) -> NVal {
230 if (err) {
231 return { env, err.GetNapiErr(env) };
232 }
233 return { NVal::CreateUndefined(env) };
234 };
235
236 NVal thisVar(env, funcArg.GetThisVar());
237 if (funcArg.GetArgc() == NARG_CNT::TWO || promise) {
238 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
239 } else {
240 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH)]);
241 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_COPYFILE_NAME, cbExec, cbCompl).val_;
242 }
243 }
244 } // namespace ModuleFileIO
245 } // namespace FileManagement
246 } // namespace OHOS