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 "move_core.h"
17
18 #ifdef __MUSL__
19 #include <filesystem>
20 #else
21 #include <sys/stat.h>
22 #endif
23
24 #include <tuple>
25 #include <unistd.h>
26
27 #include "filemgmt_libhilog.h"
28 #include "uv.h"
29
30 namespace OHOS {
31 namespace FileManagement {
32 namespace ModuleFileIO {
33 using namespace std;
34
35 #ifdef __MUSL__
CheckDir(const string & path)36 static bool CheckDir(const string &path)
37 {
38 error_code errCode;
39 if (!filesystem::is_directory(filesystem::status(path, errCode))) {
40 return false;
41 }
42 return true;
43 }
44 #else
CheckDir(const string & path)45 static bool CheckDir(const string &path)
46 {
47 struct stat fileInformation;
48 if (stat(path.c_str(), &fileInformation) == 0) {
49 if (fileInformation.st_mode & S_IFDIR) {
50 return true;
51 }
52 } else {
53 HILOGE("Failed to stat file");
54 }
55 return false;
56 }
57 #endif
58
ValidMoveArg(const string & src,const string & dest,const optional<int> & mode)59 static tuple<bool, string, string, int> ValidMoveArg(const string &src, const string &dest, const optional<int> &mode)
60 {
61 if (CheckDir(src)) {
62 HILOGE("Invalid src");
63 return { false, "", "", 0 };
64 }
65 if (CheckDir(dest)) {
66 HILOGE("Invalid dest");
67 return { false, "", "", 0 };
68 }
69 int modeType = 0;
70 if (mode.has_value()) {
71 modeType = mode.value();
72 if ((modeType != MODE_FORCE_MOVE && modeType != MODE_THROW_ERR)) {
73 HILOGE("Invalid mode");
74 return { false, "", "", 0 };
75 }
76 }
77 return { true, move(src), move(dest), modeType };
78 }
79
CopyAndDeleteFile(const string & src,const string & dest)80 static int CopyAndDeleteFile(const string &src, const string &dest)
81 {
82 unique_ptr<uv_fs_t, decltype(FsUtils::FsReqCleanup)*> statReq = {
83 new (nothrow) uv_fs_t, FsUtils::FsReqCleanup };
84 if (!statReq) {
85 HILOGE("Failed to request heap memory.");
86 return ENOMEM;
87 }
88 int ret = uv_fs_stat(nullptr, statReq.get(), src.c_str(), nullptr);
89 if (ret < 0) {
90 HILOGE("Failed to stat srcPath");
91 return ret;
92 }
93 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
94 filesystem::path dstPath(dest);
95 error_code errCode;
96 if (filesystem::exists(dstPath, errCode)) {
97 if (!filesystem::remove(dstPath, errCode)) {
98 HILOGE("Failed to remove dest file, error code: %{public}d", errCode.value());
99 return errCode.value();
100 }
101 }
102 filesystem::path srcPath(src);
103 if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
104 HILOGE("Failed to copy file, error code: %{public}d", errCode.value());
105 return errCode.value();
106 }
107 #else
108 uv_fs_t copyfileReq;
109 ret = uv_fs_copyfile(nullptr, ©fileReq, src.c_str(), dest.c_str(), MODE_FORCE_MOVE, nullptr);
110 uv_fs_req_cleanup(©fileReq);
111 if (ret < 0) {
112 HILOGE("Failed to move file using copyfile interface.");
113 return ret;
114 }
115 #endif
116 uv_fs_t unlinkReq;
117 ret = uv_fs_unlink(nullptr, &unlinkReq, src.c_str(), nullptr);
118 if (ret < 0) {
119 HILOGE("Failed to unlink src file");
120 int result = uv_fs_unlink(nullptr, &unlinkReq, dest.c_str(), nullptr);
121 if (result < 0) {
122 HILOGE("Failed to unlink dest file");
123 return result;
124 }
125 uv_fs_req_cleanup(&unlinkReq);
126 return ret;
127 }
128
129 uv_fs_req_cleanup(&unlinkReq);
130 return ERRNO_NOERR;
131 }
132
RenameFile(const string & src,const string & dest)133 static int RenameFile(const string &src, const string &dest)
134 {
135 int ret = 0;
136 uv_fs_t renameReq;
137 ret = uv_fs_rename(nullptr, &renameReq, src.c_str(), dest.c_str(), nullptr);
138 if (ret < 0 && (string_view(uv_err_name(ret)) == "EXDEV")) {
139 return CopyAndDeleteFile(src, dest);
140 }
141 if (ret < 0) {
142 HILOGE("Failed to move file using rename syscall.");
143 return ret;
144 }
145 return ERRNO_NOERR;
146 }
147
MoveFile(const string & src,const string & dest,int mode)148 static int MoveFile(const string &src, const string &dest, int mode)
149 {
150 uv_fs_t accessReq;
151 int ret = uv_fs_access(nullptr, &accessReq, src.c_str(), W_OK, nullptr);
152 if (ret < 0) {
153 HILOGE("Failed to move src file due to doesn't exist or hasn't write permission");
154 uv_fs_req_cleanup(&accessReq);
155 return ret;
156 }
157 if (mode == MODE_THROW_ERR) {
158 ret = uv_fs_access(nullptr, &accessReq, dest.c_str(), 0, nullptr);
159 uv_fs_req_cleanup(&accessReq);
160 if (ret == 0) {
161 HILOGE("Failed to move file due to existing destPath with MODE_THROW_ERR.");
162 return EEXIST;
163 }
164 if (ret < 0 && (string_view(uv_err_name(ret)) != "ENOENT")) {
165 HILOGE("Failed to access destPath with MODE_THROW_ERR.");
166 return ret;
167 }
168 } else {
169 uv_fs_req_cleanup(&accessReq);
170 }
171 return RenameFile(src, dest);
172 }
173
DoMove(const string & src,const string & dest,const optional<int> & mode)174 FsResult<void> MoveCore::DoMove(const string &src, const string &dest, const optional<int> &mode)
175 {
176 auto [succ, srcPath, destPath, modeType] = ValidMoveArg(src, dest, mode);
177 if (!succ) {
178 return FsResult<void>::Error(EINVAL);
179 }
180 int ret = MoveFile(move(srcPath), move(destPath), modeType);
181 if (ret) {
182 return FsResult<void>::Error(ret);
183 }
184 return FsResult<void>::Success();
185 }
186
187 } // namespace ModuleFileIO
188 } // namespace FileManagement
189 } // namespace OHOS