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
29namespace OHOS {
30namespace CJSystemapi {
31using namespace std;
32using namespace OHOS::FileManagement::LibN;
33using namespace OHOS::FileManagement;
34using namespace OHOS::CJSystemapi::FileFs;
35
36std::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
52std::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
67static 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
96static 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
126static 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
142static 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
164static 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
197int 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
211int 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
225int 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
239int 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}