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, &copyfile_req, src.c_str(), dest.c_str(), MODE_FORCE_MOVE, nullptr);
109     uv_fs_req_cleanup(&copyfile_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 }