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