1 /*
2 * Copyright (c) 2025 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_core.h"
17
18 #include <cstdint>
19 #include <cstring>
20 #include <fcntl.h>
21 #include <filesystem>
22 #include <tuple>
23 #include <unistd.h>
24
25 #include <sys/stat.h>
26 #include <sys/types.h>
27
28 #include "file_utils.h"
29 #include "filemgmt_libhilog.h"
30
31 namespace OHOS::FileManagement::ModuleFileIO {
32 using namespace std;
33
IsAllPath(FileInfo & srcFile,FileInfo & destFile)34 static int32_t IsAllPath(FileInfo &srcFile, FileInfo &destFile)
35 {
36 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
37 filesystem::path srcPath(string(srcFile.path.get()));
38 filesystem::path dstPath(string(destFile.path.get()));
39 error_code errCode;
40 if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing,
41 errCode)) {
42 HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
43 return errCode.value();
44 }
45 #else
46 std::unique_ptr<uv_fs_t, decltype(FsUtils::FsReqCleanup) *> copyfileReq = {
47 new (nothrow) uv_fs_t, FsUtils::FsReqCleanup};
48 if (!copyfileReq) {
49 HILOGE("Failed to request heap memory.");
50 return ENOMEM;
51 }
52 int ret = uv_fs_copyfile(nullptr, copyfileReq.get(), srcFile.path.get(), destFile.path.get(),
53 UV_FS_COPYFILE_FICLONE, nullptr);
54 if (ret < 0) {
55 HILOGE("Failed to copy file when all parameters are paths");
56 return ret;
57 }
58 #endif
59 return ERRNO_NOERR;
60 }
61
SendFileCore(FileInfo & srcFdg,FileInfo & destFdg,struct stat & statbf)62 static int32_t SendFileCore(FileInfo &srcFdg, FileInfo &destFdg, struct stat &statbf)
63 {
64 std::unique_ptr<uv_fs_t, decltype(FsUtils::FsReqCleanup) *> sendfileReq = {
65 new (nothrow) uv_fs_t, FsUtils::FsReqCleanup};
66 if (!sendfileReq) {
67 HILOGE("Failed to request heap memory.");
68 return ENOMEM;
69 }
70 int64_t offset = 0;
71 size_t size = static_cast<size_t>(statbf.st_size);
72 int ret = 0;
73 while (size > 0) {
74 ret = uv_fs_sendfile(nullptr, sendfileReq.get(), destFdg.fdg->GetFD(), srcFdg.fdg->GetFD(),
75 offset, std::min(MAX_SIZE, size), nullptr);
76 if (ret < 0) {
77 HILOGE("Failed to sendfile by ret : %{public}d", ret);
78 return ret;
79 }
80 if (static_cast<size_t>(ret) > size) {
81 HILOGE("More bytes returned than the size of the file. The file size is "
82 "%{public}zu"
83 "The bytes returned is %{public}d",
84 size, ret);
85 return EIO;
86 }
87 offset += static_cast<int64_t>(ret);
88 size -= static_cast<size_t>(ret);
89 if (ret == 0) {
90 break;
91 }
92 }
93 if (size != 0) {
94 HILOGE("The execution of the sendfile task was terminated, remaining file "
95 "size %{public}zu", size);
96 return EIO;
97 }
98 return ERRNO_NOERR;
99 }
100
TruncateCore(const FileInfo & fileInfo)101 static int32_t TruncateCore(const FileInfo &fileInfo)
102 {
103 std::unique_ptr<uv_fs_t, decltype(FsUtils::FsReqCleanup) *> ftruncateReq = {
104 new (nothrow) uv_fs_t, FsUtils::FsReqCleanup};
105 if (!ftruncateReq) {
106 HILOGE("Failed to request heap memory.");
107 return ENOMEM;
108 }
109 int ret = uv_fs_ftruncate(nullptr, ftruncateReq.get(), fileInfo.fdg->GetFD(), 0, nullptr);
110 if (ret < 0) {
111 HILOGE("Failed to truncate dstFile with ret: %{public}d", ret);
112 return ret;
113 }
114 return ERRNO_NOERR;
115 }
116
OpenCore(FileInfo & fileInfo,const int flags,const int mode)117 static int32_t OpenCore(FileInfo &fileInfo, const int flags, const int mode)
118 {
119 std::unique_ptr<uv_fs_t, decltype(FsUtils::FsReqCleanup) *> openReq = {
120 new (nothrow) uv_fs_t, FsUtils::FsReqCleanup};
121 if (!openReq) {
122 HILOGE("Failed to request heap memory.");
123 return ENOMEM;
124 }
125 int ret = uv_fs_open(nullptr, openReq.get(), fileInfo.path.get(), flags, mode, nullptr);
126 if (ret < 0) {
127 HILOGE("Failed to open srcFile with ret: %{public}d", ret);
128 return ret;
129 }
130 fileInfo.fdg = CreateUniquePtr<DistributedFS::FDGuard>(ret, true);
131 if (fileInfo.fdg == nullptr) {
132 HILOGE("Failed to request heap memory.");
133 close(ret);
134 return ENOMEM;
135 }
136 return ERRNO_NOERR;
137 }
138
OpenFile(FileInfo & srcFile,FileInfo & destFile)139 static int32_t OpenFile(FileInfo &srcFile, FileInfo &destFile)
140 {
141 if (srcFile.isPath) {
142 auto openResult = OpenCore(srcFile, UV_FS_O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
143 if (openResult) {
144 return openResult;
145 }
146 }
147 struct stat statbf;
148 if (fstat(srcFile.fdg->GetFD(), &statbf) < 0) {
149 HILOGE("Failed to get stat of file by fd: %{public}d", srcFile.fdg->GetFD());
150 return errno;
151 }
152 if (destFile.isPath) {
153 auto openResult = OpenCore(destFile, UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC,
154 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
155 if (openResult) {
156 return openResult;
157 }
158 } else {
159 auto truncateResult = TruncateCore(destFile);
160 if (truncateResult) {
161 return truncateResult;
162 }
163 auto ret = lseek(destFile.fdg->GetFD(), 0, SEEK_SET);
164 if (ret < 0) {
165 HILOGE("Failed to lseek destFile, errno: %{public}d", errno);
166 return errno;
167 }
168 }
169 if (statbf.st_size == 0) {
170 return ERRNO_NOERR;
171 }
172 return SendFileCore(srcFile, destFile, statbf);
173 }
174
ValidMode(const optional<int32_t> & mode)175 static tuple<bool, int32_t> ValidMode(const optional<int32_t> &mode)
176 {
177 int modeValue = 0;
178 if (mode.has_value()) {
179 modeValue = mode.value();
180 if (modeValue) {
181 return { false, modeValue };
182 }
183 }
184 return { true, modeValue };
185 }
186
DoCopyFile(FileInfo & src,FileInfo & dest,const optional<int32_t> & mode)187 FsResult<void> CopyFileCore::DoCopyFile(FileInfo &src, FileInfo &dest,
188 const optional<int32_t> &mode)
189 {
190 auto [succMode, modeValue] = ValidMode(mode);
191 if (!succMode) {
192 HILOGE("Failed to convert mode to int32");
193 return FsResult<void>::Error(EINVAL);
194 }
195
196 if (src.isPath && dest.isPath) {
197 auto err = IsAllPath(src, dest);
198 if (err) {
199 return FsResult<void>::Error(err);
200 }
201 } else {
202 auto err = OpenFile(src, dest);
203 if (err) {
204 return FsResult<void>::Error(err);
205 }
206 }
207 return FsResult<void>::Success();
208 }
209
210 } // namespace OHOS::FileManagement::ModuleFileIO