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_dir_core.h"
17
18 #include <dirent.h>
19 #include <filesystem>
20 #include <memory>
21 #include <tuple>
22 #include <unistd.h>
23 #include <vector>
24
25 #include "file_utils.h"
26 #include "filemgmt_libhilog.h"
27
28 namespace OHOS {
29 namespace FileManagement {
30 namespace ModuleFileIO {
31 using namespace std;
32
33 static int RecurCopyDir(
34 const string &srcPath, const string &destPath, const int mode, vector<struct ConflictFiles> &errfiles);
35
EndWithSlash(const string & src)36 static bool EndWithSlash(const string &src)
37 {
38 return src.back() == '/';
39 }
40
AllowToCopy(const string & src,const string & dest)41 static bool AllowToCopy(const string &src, const string &dest)
42 {
43 if (src == dest) {
44 HILOGE("Failed to copy file, the same path");
45 return false;
46 }
47 if (EndWithSlash(src) ? dest.find(src) == 0 : dest.find(src + "/") == 0) {
48 HILOGE("Failed to copy file, dest is under src");
49 return false;
50 }
51 if (filesystem::path(src).parent_path() == dest) {
52 HILOGE("Failed to copy file, src's parent path is dest");
53 return false;
54 }
55 return true;
56 }
57
ParseAndCheckOperand(const string & src,const string & dest,const optional<int32_t> & mode)58 static tuple<bool, int32_t> ParseAndCheckOperand(const string &src, const string &dest, const optional<int32_t> &mode)
59 {
60 error_code errCode;
61 if (!filesystem::is_directory(filesystem::status(src, errCode))) {
62 HILOGE("Invalid src, errCode = %{public}d", errCode.value());
63 return { false, 0 };
64 }
65 if (!filesystem::is_directory(filesystem::status(dest, errCode))) {
66 HILOGE("Invalid dest, errCode = %{public}d", errCode.value());
67 return { false, 0 };
68 }
69 if (!AllowToCopy(src, dest)) {
70 return { false, 0 };
71 }
72 int32_t modeValue = 0;
73 if (mode.has_value()) {
74 modeValue = mode.value();
75 if (modeValue < COPYMODE_MIN || modeValue > COPYMODE_MAX) {
76 HILOGE("Invalid mode");
77 return { false, 0 };
78 }
79 }
80 return { true, modeValue };
81 }
82
MakeDir(const string & path)83 static int MakeDir(const string &path)
84 {
85 filesystem::path destDir(path);
86 error_code errCode;
87 if (!filesystem::create_directory(destDir, errCode)) {
88 HILOGE("Failed to create directory, error code: %{public}d", errCode.value());
89 return errCode.value();
90 }
91 return ERRNO_NOERR;
92 }
93
94 struct NameList {
95 struct dirent **namelist = { nullptr };
96 int direntNum = 0;
97 };
98
RemoveFile(const string & destPath)99 static int RemoveFile(const string &destPath)
100 {
101 filesystem::path destFile(destPath);
102 error_code errCode;
103 if (!filesystem::remove(destFile, errCode)) {
104 HILOGE("Failed to remove file with path, error code: %{public}d", errCode.value());
105 return errCode.value();
106 }
107 return ERRNO_NOERR;
108 }
109
Deleter(struct NameList * arg)110 static void Deleter(struct NameList *arg)
111 {
112 for (int i = 0; i < arg->direntNum; i++) {
113 free((arg->namelist)[i]);
114 (arg->namelist)[i] = nullptr;
115 }
116 free(arg->namelist);
117 arg->namelist = nullptr;
118 delete arg;
119 arg = nullptr;
120 }
121
CopyFile(const string & src,const string & dest,const int mode)122 static int CopyFile(const string &src, const string &dest, const int mode)
123 {
124 filesystem::path dstPath(dest);
125 error_code errCode;
126 if (filesystem::exists(dstPath, errCode)) {
127 int ret = (mode == DIRMODE_FILE_COPY_THROW_ERR) ? EEXIST : RemoveFile(dest);
128 if (ret) {
129 HILOGE("Failed to copy file due to existing destPath with throw err");
130 return ret;
131 }
132 }
133 if (errCode.value() != ERRNO_NOERR) {
134 HILOGE("Fs exists fail, errcode is %{public}d", errCode.value());
135 return errCode.value();
136 }
137 filesystem::path srcPath(src);
138 if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
139 HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
140 return errCode.value();
141 }
142 return ERRNO_NOERR;
143 }
144
CopySubDir(const string & srcPath,const string & destPath,const int mode,vector<struct ConflictFiles> & errfiles)145 static int CopySubDir(
146 const string &srcPath, const string &destPath, const int mode, vector<struct ConflictFiles> &errfiles)
147 {
148 error_code errCode;
149 if (!filesystem::exists(destPath, errCode) && errCode.value() == ERRNO_NOERR) {
150 int res = MakeDir(destPath);
151 if (res != ERRNO_NOERR) {
152 HILOGE("Failed to mkdir");
153 return res;
154 }
155 } else if (errCode.value() != ERRNO_NOERR) {
156 HILOGE("Fs exists fail, errcode is %{public}d", errCode.value());
157 return errCode.value();
158 }
159 return RecurCopyDir(srcPath, destPath, mode, errfiles);
160 }
161
FilterFunc(const struct dirent * filename)162 static int FilterFunc(const struct dirent *filename)
163 {
164 if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
165 return DISMATCH;
166 }
167 return MATCH;
168 }
169
RecurCopyDir(const string & srcPath,const string & destPath,const int mode,vector<struct ConflictFiles> & errfiles)170 static int RecurCopyDir(
171 const string &srcPath, const string &destPath, const int mode, vector<struct ConflictFiles> &errfiles)
172 {
173 unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (nothrow) struct NameList, Deleter };
174 if (pNameList == nullptr) {
175 HILOGE("Failed to request heap memory.");
176 return ENOMEM;
177 }
178 int num = scandir(srcPath.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
179 if (num < 0) {
180 HILOGE("Scandir fail errno is %{public}d", errno);
181 return errno;
182 }
183 pNameList->direntNum = num;
184
185 for (int i = 0; i < num; i++) {
186 if ((pNameList->namelist[i])->d_type == DT_DIR) {
187 string srcTemp = srcPath + '/' + string((pNameList->namelist[i])->d_name);
188 string destTemp = destPath + '/' + string((pNameList->namelist[i])->d_name);
189 int res = CopySubDir(srcTemp, destTemp, mode, errfiles);
190 if (res == ERRNO_NOERR) {
191 continue;
192 }
193 return res;
194 } else {
195 string src = srcPath + '/' + string((pNameList->namelist[i])->d_name);
196 string dest = destPath + '/' + string((pNameList->namelist[i])->d_name);
197 int res = CopyFile(src, dest, mode);
198 if (res == EEXIST) {
199 errfiles.emplace_back(src, dest);
200 continue;
201 } else if (res == ERRNO_NOERR) {
202 continue;
203 } else {
204 HILOGE("Failed to copy file for error %{public}d", res);
205 return res;
206 }
207 }
208 }
209 return ERRNO_NOERR;
210 }
211
CopyDirFunc(const string & src,const string & dest,const int mode,vector<struct ConflictFiles> & errfiles)212 static int CopyDirFunc(const string &src, const string &dest, const int mode, vector<struct ConflictFiles> &errfiles)
213 {
214 size_t found = string(src).rfind('/');
215 if (found == string::npos) {
216 return EINVAL;
217 }
218 string dirName = string(src).substr(found);
219 string destStr = dest + dirName;
220 error_code errCode;
221 if (!filesystem::exists(destStr, errCode) && errCode.value() == ERRNO_NOERR) {
222 int res = MakeDir(destStr);
223 if (res != ERRNO_NOERR) {
224 HILOGE("Failed to mkdir");
225 return res;
226 }
227 } else if (errCode.value() != ERRNO_NOERR) {
228 HILOGE("Fs exists fail, errcode is %{public}d", errCode.value());
229 return errCode.value();
230 }
231 int res = RecurCopyDir(src, destStr, mode, errfiles);
232 if (!errfiles.empty() && res == ERRNO_NOERR) {
233 return EEXIST;
234 }
235 return res;
236 }
237
DoCopyDir(const string & src,const string & dest,const optional<int32_t> & mode)238 struct CopyDirResult CopyDirCore::DoCopyDir(const string &src, const string &dest, const optional<int32_t> &mode)
239 {
240 auto [succ, modeValue] = ParseAndCheckOperand(src, dest, mode);
241 if (!succ) {
242 return { FsResult<void>::Error(EINVAL), nullopt };
243 }
244
245 vector<struct ConflictFiles> errfiles = {};
246 int ret = CopyDirFunc(src, dest, modeValue, errfiles);
247 if (ret == EEXIST && modeValue == DIRMODE_FILE_COPY_THROW_ERR) {
248 return { FsResult<void>::Error(EEXIST), make_optional<vector<struct ConflictFiles>>(move(errfiles)) };
249 } else if (ret) {
250 return { FsResult<void>::Error(ret), nullopt };
251 }
252 return { FsResult<void>::Success(), nullopt };
253 }
254
255 } // namespace ModuleFileIO
256 } // namespace FileManagement
257 } // namespace OHOS