• 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 "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