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 33namespace OHOS { 34namespace CJSystemapi { 35using namespace std; 36using namespace OHOS::FileManagement::LibN; 37 38#ifdef __MUSL__ 39static bool CheckDir(const string &path) 40{ 41 if (!filesystem::is_directory(filesystem::status(path))) { 42 return false; 43 } 44 return true; 45} 46#else 47static 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 61static 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} 79static 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 131static 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 146int 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}