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