• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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