• 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 <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