1 /*
2  * Copyright (c) 2024 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 #include "stat_impl.h"
18 #include "macro.h"
19 #include "n_error.h"
20 
21 #include <cstring>
22 #include <fcntl.h>
23 #include <filesystem>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <tuple>
27 #include <unistd.h>
28 
29 namespace OHOS {
30 namespace CJSystemapi {
31 using namespace std;
32 using namespace OHOS::FileManagement::LibN;
33 using namespace OHOS::FileManagement;
34 using namespace OHOS::CJSystemapi::FileFs;
35 
ParseOperand(int32_t file)36 std::tuple<int, FileInfo> ParseOperand(int32_t file)
37 {
38     LOGI("FS_TEST:: FS_TEST::ParseOperand");
39     if (file < 0) {
40         LOGE("Invalid fd");
41         return { EINVAL, FileInfo { false, {}, {} } };
42     }
43     auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(file, false);
44     if (fdg == nullptr) {
45         LOGE("Failed to request heap memory.");
46         return { ENOMEM, FileInfo { false, {}, {} } };
47     }
48     LOGI("FS_TEST:: FS_TEST::ParseOperand success");
49     return { SUCCESS_CODE, FileInfo { false, {}, move(fdg) } };
50 };
51 
ParseOperand(std::string file)52 std::tuple<int, FileInfo> ParseOperand(std::string file)
53 {
54     LOGI("FS_TEST:: ParseOperand");
55     std::unique_ptr<char[]> filePath = std::make_unique<char[]>(file.length() + 1);
56     if (!filePath) {
57         return { ENOMEM, FileInfo { true, {}, {} } };
58     }
59     for (size_t i = 0; i < file.length(); i++) {
60         filePath[i] = file[i];
61     }
62 
63     LOGI("FS_TEST:: ParseOperand success");
64     return { SUCCESS_CODE, FileInfo { true, move(filePath), {} } };
65 };
66 
IsAllPath(FileInfo & srcFile,FileInfo & destFile)67 static int IsAllPath(FileInfo& srcFile, FileInfo& destFile)
68 {
69 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
70     filesystem::path srcPath(string(srcFile.path.get()));
71     filesystem::path dstPath(string(destFile.path.get()));
72     error_code errCode;
73     LOGI("srcPath: %{public}s", srcPath.c_str());
74     LOGI("dstPath: %{public}s", dstPath.c_str());
75     if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
76         LOGE("Failed to copy file, error code: %{public}d", errCode.value());
77         return errCode.value();
78     }
79 #else
80     std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> copyfile_req = {
81         new (nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
82     if (!copyfile_req) {
83         LOGE("Failed to request heap memory.");
84         return ENOMEM;
85     }
86     int ret = uv_fs_copyfile(nullptr, copyfile_req.get(), srcFile.path.get(), destFile.path.get(),
87                              UV_FS_COPYFILE_FICLONE, nullptr);
88     if (ret < 0) {
89         LOGE("Failed to copy file when all parameters are paths");
90         return ret;
91     }
92 #endif
93     return OHOS::FileManagement::LibN::ERRNO_NOERR;
94 }
95 
SendFileCore(FileInfo & srcFdg,FileInfo & destFdg,struct stat & statbf)96 static int SendFileCore(FileInfo& srcFdg, FileInfo& destFdg, struct stat& statbf)
97 {
98     std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> sendfile_req = {
99         new (nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
100     if (!sendfile_req) {
101         LOGE("Failed to request heap memory.");
102         return ENOMEM;
103     }
104     int64_t offset = 0;
105     size_t size = static_cast<size_t>(statbf.st_size);
106     while (size > 0) {
107         int ret = uv_fs_sendfile(nullptr, sendfile_req.get(), destFdg.fdg->GetFD(), srcFdg.fdg->GetFD(),
108             offset, MAX_SIZE, nullptr);
109         if (ret < 0) {
110             LOGE("Failed to sendfile by ret : %{public}d", ret);
111             return ret;
112         }
113         offset += static_cast<int64_t>(ret);
114         size -= static_cast<size_t>(ret);
115         if (ret == 0) {
116             break;
117         }
118     }
119     if (size != 0) {
120         LOGE("The execution of the sendfile task was terminated, remaining file size %{public}zu", size);
121         return EIO;
122     }
123     return OHOS::FileManagement::LibN::ERRNO_NOERR;
124 }
125 
TruncateCore(const FileInfo & fileInfo)126 static int TruncateCore(const FileInfo& fileInfo)
127 {
128     std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> ftruncate_req = {
129         new (nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
130     if (!ftruncate_req) {
131         LOGE("Failed to request heap memory.");
132         return 1;
133     }
134     int ret = uv_fs_ftruncate(nullptr, ftruncate_req.get(), fileInfo.fdg->GetFD(), 0, nullptr);
135     if (ret < 0) {
136         LOGE("Failed to truncate dstFile with ret: %{public}d", ret);
137         return 1;
138     }
139     return 0;
140 }
141 
OpenCore(FileInfo & fileInfo,const int flags,const int mode)142 static int OpenCore(FileInfo& fileInfo, const int flags, const int mode)
143 {
144     std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> open_req = {
145         new (nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
146     if (!open_req) {
147         LOGE("Failed to request heap memory.");
148         return 1;
149     }
150     int ret = uv_fs_open(nullptr, open_req.get(), fileInfo.path.get(), flags, mode, nullptr);
151     if (ret < 0) {
152         LOGE("Failed to open srcFile with ret: %{public}d", ret);
153         return 1;
154     }
155     fileInfo.fdg = CreateUniquePtr<DistributedFS::FDGuard>(ret, true);
156     if (fileInfo.fdg == nullptr) {
157         LOGE("Failed to request heap memory.");
158         close(ret);
159         return 1;
160     }
161     return 0;
162 }
163 
OpenFile(FileInfo & srcFile,FileInfo & destFile)164 static int OpenFile(FileInfo& srcFile, FileInfo& destFile)
165 {
166     if (srcFile.isPath) {
167         auto openResult = OpenCore(srcFile, UV_FS_O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
168         if (openResult) {
169             return openResult;
170         }
171     }
172     struct stat statbf;
173     if (fstat(srcFile.fdg->GetFD(), &statbf) < 0) {
174         LOGE("Failed to get stat of file by fd: %{public}d", srcFile.fdg->GetFD());
175         return errno;
176     }
177     if (destFile.isPath) {
178         auto openResult = OpenCore(destFile, UV_FS_O_RDWR | UV_FS_O_CREAT |
179             UV_FS_O_TRUNC, statbf.st_mode);
180         if (openResult) {
181             return openResult;
182         }
183     } else {
184         auto truncateResult = TruncateCore(destFile);
185         if (truncateResult) {
186             return truncateResult;
187         }
188         auto ret = lseek(destFile.fdg->GetFD(), 0, SEEK_SET);
189         if (ret < 0) {
190             LOGE("Failed to lseek destFile, errno: %{public}d", errno);
191             return errno;
192         }
193     }
194     return SendFileCore(srcFile, destFile, statbf);
195 }
196 
CopyFile(const std::string & src,const std::string & dest,int mode)197 int CopyFileImpl::CopyFile(const std::string& src, const std::string& dest, int mode)
198 {
199     LOGI("FS_TEST:: CopyFile::CopyFile start");
200     auto [succSrc, srcFileInfo] = ParseOperand(src);
201     if (succSrc != SUCCESS_CODE) {
202         return succSrc;
203     }
204     auto [succDest, destFileInfo] = ParseOperand(dest);
205     if (succDest != SUCCESS_CODE) {
206         return succDest;
207     }
208     return IsAllPath(srcFileInfo, destFileInfo);
209 }
210 
CopyFile(const std::string & src,int32_t dest,int mode)211 int CopyFileImpl::CopyFile(const std::string& src, int32_t dest, int mode)
212 {
213     LOGI("FS_TEST:: CopyFile::CopyFile start");
214     auto [succSrc, srcFileInfo] = ParseOperand(src);
215     if (succSrc != SUCCESS_CODE) {
216         return succSrc;
217     }
218     auto [succDest, destFileInfo] = ParseOperand(dest);
219     if (succDest != SUCCESS_CODE) {
220         return succDest;
221     }
222     return OpenFile(srcFileInfo, destFileInfo);
223 }
224 
CopyFile(int32_t src,const std::string & dest,int mode)225 int CopyFileImpl::CopyFile(int32_t src, const std::string& dest, int mode)
226 {
227     LOGI("FS_TEST:: CopyFile::CopyFile start");
228     auto [succSrc, srcFileInfo] = ParseOperand(src);
229     if (succSrc != SUCCESS_CODE) {
230         return succSrc;
231     }
232     auto [succDest, destFileInfo] = ParseOperand(dest);
233     if (succDest != SUCCESS_CODE) {
234         return succDest;
235     }
236     return OpenFile(srcFileInfo, destFileInfo);
237 }
238 
CopyFile(int32_t src,int32_t dest,int mode)239 int CopyFileImpl::CopyFile(int32_t src, int32_t dest, int mode)
240 {
241     LOGI("FS_TEST:: CopyFile::CopyFile start");
242     auto [succSrc, srcFileInfo] = ParseOperand(src);
243     if (succSrc != SUCCESS_CODE) {
244         return succSrc;
245     }
246     auto [succDest, destFileInfo] = ParseOperand(dest);
247     if (succDest != SUCCESS_CODE) {
248         return succDest;
249     }
250     return OpenFile(srcFileInfo, destFileInfo);
251 }
252 
253 }
254 }