• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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