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 "movedir_core.h"
17
18 #include <deque>
19 #include <dirent.h>
20 #include <filesystem>
21 #include <memory>
22 #include <string_view>
23 #include <tuple>
24 #include <unistd.h>
25
26 #include "file_utils.h"
27 #include "filemgmt_libhilog.h"
28
29 namespace OHOS {
30 namespace FileManagement {
31 namespace ModuleFileIO {
32 using namespace std;
33
34 static int RecurMoveDir(const string &srcPath, const string &destPath, const int mode,
35 deque<struct ErrFiles> &errfiles);
36
JudgeExistAndEmpty(const string & path)37 static tuple<bool, bool> JudgeExistAndEmpty(const string &path)
38 {
39 std::error_code errCode;
40 filesystem::path pathName(path);
41 if (filesystem::exists(pathName, errCode)) {
42 if (filesystem::is_empty(pathName, errCode)) {
43 return { true, true };
44 }
45 return { true, false };
46 }
47 return { false, false };
48 }
49
RmDirectory(const string & path)50 static int RmDirectory(const string &path)
51 {
52 filesystem::path pathName(path);
53 std::error_code errCode;
54 if (filesystem::exists(pathName, errCode)) {
55 std::error_code errCode;
56 (void)filesystem::remove_all(pathName, errCode);
57 if (errCode.value() != 0) {
58 HILOGE("Failed to remove directory, error code: %{public}d", errCode.value());
59 return errCode.value();
60 }
61 } else if (errCode.value() != ERRNO_NOERR) {
62 HILOGE("Fs exists fail, errcode is %{public}d", errCode.value());
63 return errCode.value();
64 }
65 return ERRNO_NOERR;
66 }
67
RemovePath(const string & pathStr)68 static int RemovePath(const string& pathStr)
69 {
70 filesystem::path pathTarget(pathStr);
71 std::error_code errCode;
72 if (!filesystem::remove(pathTarget, errCode)) {
73 HILOGE("Failed to remove file or directory, error code: %{public}d", errCode.value());
74 return errCode.value();
75 }
76 return ERRNO_NOERR;
77 }
78
CopyAndDeleteFile(const string & src,const string & dest)79 static int CopyAndDeleteFile(const string &src, const string &dest)
80 {
81 filesystem::path dstPath(dest);
82 std::error_code errCode;
83 if (filesystem::exists(dstPath, errCode)) {
84 int removeRes = RemovePath(dest);
85 if (removeRes != 0) {
86 HILOGE("Failed to remove dest file");
87 return removeRes;
88 }
89 }
90 filesystem::path srcPath(src);
91 if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
92 HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
93 return errCode.value();
94 }
95 return RemovePath(src);
96 }
97
RenameFile(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)98 static int RenameFile(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
99 {
100 filesystem::path dstPath(dest);
101 std::error_code errCode;
102 if (filesystem::exists(dstPath, errCode)) {
103 if (filesystem::is_directory(dstPath, errCode)) {
104 errfiles.emplace_front(src, dest);
105 return ERRNO_NOERR;
106 }
107 if (mode == DIRMODE_FILE_THROW_ERR) {
108 errfiles.emplace_back(src, dest);
109 return ERRNO_NOERR;
110 }
111 }
112 if (errCode.value() != ERRNO_NOERR) {
113 HILOGE("Fs exists or is_directory fail, errcode is %{public}d", errCode.value());
114 }
115 filesystem::path srcPath(src);
116 filesystem::rename(srcPath, dstPath, errCode);
117 if (errCode.value() == EXDEV) {
118 HILOGD("Failed to rename file due to EXDEV");
119 return CopyAndDeleteFile(src, dest);
120 }
121 return errCode.value();
122 }
123
FilterFunc(const struct dirent * filename)124 static int32_t FilterFunc(const struct dirent *filename)
125 {
126 if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
127 return FILE_DISMATCH;
128 }
129 return FILE_MATCH;
130 }
131
RenameDir(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)132 static int RenameDir(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
133 {
134 filesystem::path destPath(dest);
135 std::error_code errCode;
136 if (filesystem::exists(destPath, errCode)) {
137 return RecurMoveDir(src, dest, mode, errfiles);
138 } else if (errCode.value() != ERRNO_NOERR) {
139 HILOGE("Fs exists fail, errcode is %{public}d", errCode.value());
140 return errCode.value();
141 }
142 filesystem::path srcPath(src);
143 filesystem::rename(srcPath, destPath, errCode);
144 if (errCode.value() == EXDEV) {
145 HILOGD("Failed to rename file due to EXDEV");
146 if (!filesystem::create_directory(destPath, errCode)) {
147 HILOGE("Failed to create directory, error code: %{public}d", errCode.value());
148 return errCode.value();
149 }
150 return RecurMoveDir(src, dest, mode, errfiles);
151 }
152 if (errCode.value() != 0) {
153 HILOGE("Failed to rename file, error code: %{public}d", errCode.value());
154 return errCode.value();
155 }
156 return ERRNO_NOERR;
157 }
158
159 struct NameListArg {
160 struct dirent** namelist;
161 int num;
162 };
163
Deleter(struct NameListArg * arg)164 static void Deleter(struct NameListArg *arg)
165 {
166 for (int i = 0; i < arg->num; i++) {
167 free((arg->namelist)[i]);
168 (arg->namelist)[i] = nullptr;
169 }
170 free(arg->namelist);
171 arg->namelist = nullptr;
172 delete arg;
173 arg = nullptr;
174 }
175
RecurMoveDir(const string & srcPath,const string & destPath,const int mode,deque<struct ErrFiles> & errfiles)176 static int RecurMoveDir(const string &srcPath, const string &destPath, const int mode,
177 deque<struct ErrFiles> &errfiles)
178 {
179 filesystem::path dpath(destPath);
180 std::error_code errCode;
181 if (!filesystem::is_directory(dpath, errCode)) {
182 errfiles.emplace_front(srcPath, destPath);
183 return ERRNO_NOERR;
184 }
185
186 unique_ptr<struct NameListArg, decltype(Deleter)*> ptr = {new struct NameListArg, Deleter};
187 if (!ptr) {
188 HILOGE("Failed to request heap memory.");
189 return ENOMEM;
190 }
191 int num = scandir(srcPath.c_str(), &(ptr->namelist), FilterFunc, alphasort);
192 ptr->num = num;
193
194 for (int i = 0; i < num; i++) {
195 if ((ptr->namelist[i])->d_type == DT_DIR) {
196 string srcTemp = srcPath + '/' + string((ptr->namelist[i])->d_name);
197 string destTemp = destPath + '/' + string((ptr->namelist[i])->d_name);
198 size_t size = errfiles.size();
199 int res = RenameDir(srcTemp, destTemp, mode, errfiles);
200 if (res != ERRNO_NOERR) {
201 return res;
202 }
203 if (size != errfiles.size()) {
204 continue;
205 }
206 res = RemovePath(srcTemp);
207 if (res) {
208 return res;
209 }
210 } else {
211 string src = srcPath + '/' + string((ptr->namelist[i])->d_name);
212 string dest = destPath + '/' + string((ptr->namelist[i])->d_name);
213 int res = RenameFile(src, dest, mode, errfiles);
214 if (res != ERRNO_NOERR) {
215 HILOGE("Failed to rename file for error %{public}d", res);
216 return res;
217 }
218 }
219 }
220 return ERRNO_NOERR;
221 }
222
MoveDirFunc(const string & src,const string & dest,const int mode,deque<struct ErrFiles> & errfiles)223 static int MoveDirFunc(const string &src, const string &dest, const int mode, deque<struct ErrFiles> &errfiles)
224 {
225 size_t found = string(src).rfind('/');
226 if (found == std::string::npos) {
227 return EINVAL;
228 }
229 if (access(src.c_str(), W_OK) != 0) {
230 HILOGE("Failed to move src directory due to doesn't exist or hasn't write permission");
231 return errno;
232 }
233 string dirName = string(src).substr(found);
234 string destStr = dest + dirName;
235 auto [destStrExist, destStrEmpty] = JudgeExistAndEmpty(destStr);
236 if (destStrExist && !destStrEmpty) {
237 if (mode == DIRMODE_DIRECTORY_REPLACE) {
238 int removeRes = RmDirectory(destStr);
239 if (removeRes) {
240 HILOGE("Failed to remove dest directory in DIRMODE_DIRECTORY_REPLACE");
241 return removeRes;
242 }
243 }
244 if (mode == DIRMODE_DIRECTORY_THROW_ERR) {
245 HILOGE("Failed to move directory in DIRMODE_DIRECTORY_THROW_ERR");
246 return ENOTEMPTY;
247 }
248 }
249 int res = RenameDir(src, destStr, mode, errfiles);
250 if (res == ERRNO_NOERR) {
251 if (!errfiles.empty()) {
252 HILOGE("Failed to movedir with some conflicted files");
253 return EEXIST;
254 }
255 int removeRes = RmDirectory(src);
256 if (removeRes) {
257 HILOGE("Failed to remove src directory");
258 return removeRes;
259 }
260 }
261 return res;
262 }
263
ValidMoveDirArg(const string & src,const string & dest,optional<int32_t> mode)264 static tuple<bool, int> ValidMoveDirArg(
265 const string &src, const string &dest, optional<int32_t> mode)
266 {
267 std::error_code errCode;
268 if (!filesystem::is_directory(filesystem::status(src.c_str(), errCode))) {
269 HILOGE("Invalid src, errCode = %{public}d", errCode.value());
270 return { false, 0 };
271 }
272 if (!filesystem::is_directory(filesystem::status(dest.c_str(), errCode))) {
273 HILOGE("Invalid dest,errCode = %{public}d", errCode.value());
274 return { false, 0 };
275 }
276 int modeType = 0;
277 if (mode.has_value()) {
278 modeType = mode.value();
279 if (modeType < DIRMODE_MIN || modeType > DIRMODE_MAX) {
280 HILOGE("Invalid mode");
281 return { false, 0 };
282 }
283 }
284 return { true, modeType };
285 }
286
DoMoveDir(const string & src,const string & dest,optional<int32_t> modeType)287 MoveDirResult MoveDirCore::DoMoveDir(
288 const string &src, const string &dest, optional<int32_t> modeType)
289 {
290 auto [succ, mode] = ValidMoveDirArg(src, dest, modeType);
291 if (!succ) {
292 return { FsResult<void>::Error(EINVAL), nullopt };
293 }
294 deque<struct ErrFiles> errfiles = {};
295 int ret = MoveDirFunc(src, dest, mode, errfiles);
296 if (ret == EEXIST) {
297 return { FsResult<void>::Error(EEXIST), optional<deque<struct ErrFiles>> { errfiles } };
298 } else if (ret) {
299 return { FsResult<void>::Error(ret), nullopt };
300 }
301 return { FsResult<void>::Success(), nullopt };
302 }
303
304 } // ModuleFileIO
305 } // FileManagement
306 } // OHOS