15bebb993Sopenharmony_ci/*
25bebb993Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
35bebb993Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
45bebb993Sopenharmony_ci * you may not use this file except in compliance with the License.
55bebb993Sopenharmony_ci * You may obtain a copy of the License at
65bebb993Sopenharmony_ci *
75bebb993Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
85bebb993Sopenharmony_ci *
95bebb993Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
105bebb993Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
115bebb993Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
125bebb993Sopenharmony_ci * See the License for the specific language governing permissions and
135bebb993Sopenharmony_ci * limitations under the License.
145bebb993Sopenharmony_ci */
155bebb993Sopenharmony_ci
165bebb993Sopenharmony_ci#include "move_file.h"
175bebb993Sopenharmony_ci#include "macro.h"
185bebb993Sopenharmony_ci#include "stat_impl.h"
195bebb993Sopenharmony_ci
205bebb993Sopenharmony_ci#include <cstring>
215bebb993Sopenharmony_ci#include <fcntl.h>
225bebb993Sopenharmony_ci
235bebb993Sopenharmony_ci#ifdef __MUSL__
245bebb993Sopenharmony_ci#include <filesystem>
255bebb993Sopenharmony_ci#else
265bebb993Sopenharmony_ci#include <sys/stat.h>
275bebb993Sopenharmony_ci#endif
285bebb993Sopenharmony_ci
295bebb993Sopenharmony_ci#include "uv.h"
305bebb993Sopenharmony_ci#include <tuple>
315bebb993Sopenharmony_ci#include <unistd.h>
325bebb993Sopenharmony_ci
335bebb993Sopenharmony_cinamespace OHOS {
345bebb993Sopenharmony_cinamespace CJSystemapi {
355bebb993Sopenharmony_ciusing namespace std;
365bebb993Sopenharmony_ciusing namespace OHOS::FileManagement::LibN;
375bebb993Sopenharmony_ci
385bebb993Sopenharmony_ci#ifdef __MUSL__
395bebb993Sopenharmony_cistatic bool CheckDir(const string &path)
405bebb993Sopenharmony_ci{
415bebb993Sopenharmony_ci    if (!filesystem::is_directory(filesystem::status(path))) {
425bebb993Sopenharmony_ci        return false;
435bebb993Sopenharmony_ci    }
445bebb993Sopenharmony_ci    return true;
455bebb993Sopenharmony_ci}
465bebb993Sopenharmony_ci#else
475bebb993Sopenharmony_cistatic bool CheckDir(const string &path)
485bebb993Sopenharmony_ci{
495bebb993Sopenharmony_ci    struct stat fileInformation;
505bebb993Sopenharmony_ci    if (stat(path.c_str(), &fileInformation) == 0) {
515bebb993Sopenharmony_ci        if (fileInformation.st_mode & S_IFDIR) {
525bebb993Sopenharmony_ci            return true;
535bebb993Sopenharmony_ci        }
545bebb993Sopenharmony_ci    } else {
555bebb993Sopenharmony_ci        LOGE("Failed to stat file");
565bebb993Sopenharmony_ci    }
575bebb993Sopenharmony_ci    return false;
585bebb993Sopenharmony_ci}
595bebb993Sopenharmony_ci#endif
605bebb993Sopenharmony_ci
615bebb993Sopenharmony_cistatic int ChangeTime(const string &path, uv_fs_t *statReq)
625bebb993Sopenharmony_ci{
635bebb993Sopenharmony_ci    std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> utime_req = {
645bebb993Sopenharmony_ci        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
655bebb993Sopenharmony_ci    if (!utime_req) {
665bebb993Sopenharmony_ci        LOGE("Failed to request heap memory.");
675bebb993Sopenharmony_ci        return ENOMEM;
685bebb993Sopenharmony_ci    }
695bebb993Sopenharmony_ci    double atime = static_cast<double>(statReq->statbuf.st_atim.tv_sec) +
705bebb993Sopenharmony_ci        static_cast<double>(statReq->statbuf.st_atim.tv_nsec) / NS;
715bebb993Sopenharmony_ci    double mtime = static_cast<double>(statReq->statbuf.st_mtim.tv_sec) +
725bebb993Sopenharmony_ci        static_cast<double>(statReq->statbuf.st_mtim.tv_nsec) / NS;
735bebb993Sopenharmony_ci    int ret = uv_fs_utime(nullptr, utime_req.get(), path.c_str(), atime, mtime, nullptr);
745bebb993Sopenharmony_ci    if (ret < 0) {
755bebb993Sopenharmony_ci        LOGE("Failed to utime dstPath");
765bebb993Sopenharmony_ci    }
775bebb993Sopenharmony_ci    return ret;
785bebb993Sopenharmony_ci}
795bebb993Sopenharmony_cistatic int CopyAndDeleteFile(const string &src, const string &dest)
805bebb993Sopenharmony_ci{
815bebb993Sopenharmony_ci    std::unique_ptr<uv_fs_t, decltype(CommonFunc::FsReqCleanup)*> stat_req = {
825bebb993Sopenharmony_ci        new (std::nothrow) uv_fs_t, CommonFunc::FsReqCleanup };
835bebb993Sopenharmony_ci    if (!stat_req) {
845bebb993Sopenharmony_ci        LOGE("Failed to request heap memory.");
855bebb993Sopenharmony_ci        return ENOMEM;
865bebb993Sopenharmony_ci    }
875bebb993Sopenharmony_ci    int ret = uv_fs_stat(nullptr, stat_req.get(), src.c_str(), nullptr);
885bebb993Sopenharmony_ci    if (ret < 0) {
895bebb993Sopenharmony_ci        LOGE("Failed to stat srcPath");
905bebb993Sopenharmony_ci        return ret;
915bebb993Sopenharmony_ci    }
925bebb993Sopenharmony_ci#if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
935bebb993Sopenharmony_ci    filesystem::path dstPath(dest);
945bebb993Sopenharmony_ci    std::error_code errCode;
955bebb993Sopenharmony_ci    if (filesystem::exists(dstPath)) {
965bebb993Sopenharmony_ci        if (!filesystem::remove(dstPath, errCode)) {
975bebb993Sopenharmony_ci            LOGE("Failed to remove dest file, error code: %{public}d", errCode.value());
985bebb993Sopenharmony_ci            return errCode.value();
995bebb993Sopenharmony_ci        }
1005bebb993Sopenharmony_ci    }
1015bebb993Sopenharmony_ci    filesystem::path srcPath(src);
1025bebb993Sopenharmony_ci    if (!filesystem::copy_file(srcPath, dstPath, filesystem::copy_options::overwrite_existing, errCode)) {
1035bebb993Sopenharmony_ci        LOGE("Failed to copy file, error code: %{public}d", errCode.value());
1045bebb993Sopenharmony_ci        return errCode.value();
1055bebb993Sopenharmony_ci    }
1065bebb993Sopenharmony_ci#else
1075bebb993Sopenharmony_ci    uv_fs_t copyfile_req;
1085bebb993Sopenharmony_ci    ret = uv_fs_copyfile(nullptr, &copyfile_req, src.c_str(), dest.c_str(), MODE_FORCE_MOVE, nullptr);
1095bebb993Sopenharmony_ci    uv_fs_req_cleanup(&copyfile_req);
1105bebb993Sopenharmony_ci    if (ret < 0) {
1115bebb993Sopenharmony_ci        LOGE("Failed to move file using copyfile interface.");
1125bebb993Sopenharmony_ci        return ret;
1135bebb993Sopenharmony_ci    }
1145bebb993Sopenharmony_ci#endif
1155bebb993Sopenharmony_ci    uv_fs_t unlinkReq;
1165bebb993Sopenharmony_ci    ret = uv_fs_unlink(nullptr, &unlinkReq, src.c_str(), nullptr);
1175bebb993Sopenharmony_ci    if (ret < 0) {
1185bebb993Sopenharmony_ci        LOGE("Failed to unlink src file");
1195bebb993Sopenharmony_ci        int result = uv_fs_unlink(nullptr, &unlinkReq, dest.c_str(), nullptr);
1205bebb993Sopenharmony_ci        if (result < 0) {
1215bebb993Sopenharmony_ci            LOGE("Failed to unlink dest file");
1225bebb993Sopenharmony_ci            return result;
1235bebb993Sopenharmony_ci        }
1245bebb993Sopenharmony_ci        uv_fs_req_cleanup(&unlinkReq);
1255bebb993Sopenharmony_ci        return ret;
1265bebb993Sopenharmony_ci    }
1275bebb993Sopenharmony_ci    uv_fs_req_cleanup(&unlinkReq);
1285bebb993Sopenharmony_ci    return ChangeTime(dest, stat_req.get());
1295bebb993Sopenharmony_ci}
1305bebb993Sopenharmony_ci
1315bebb993Sopenharmony_cistatic int RenameFile(const string &src, const string &dest)
1325bebb993Sopenharmony_ci{
1335bebb993Sopenharmony_ci    int ret = 0;
1345bebb993Sopenharmony_ci    uv_fs_t renameReq;
1355bebb993Sopenharmony_ci    ret = uv_fs_rename(nullptr, &renameReq, src.c_str(), dest.c_str(), nullptr);
1365bebb993Sopenharmony_ci    if (ret < 0 && (string_view(uv_err_name(ret)) == "EXDEV")) {
1375bebb993Sopenharmony_ci        return CopyAndDeleteFile(src, dest);
1385bebb993Sopenharmony_ci    }
1395bebb993Sopenharmony_ci    if (ret < 0) {
1405bebb993Sopenharmony_ci        LOGE("Failed to move file using rename syscall.");
1415bebb993Sopenharmony_ci        return ret;
1425bebb993Sopenharmony_ci    }
1435bebb993Sopenharmony_ci    return OHOS::FileManagement::LibN::ERRNO_NOERR;
1445bebb993Sopenharmony_ci}
1455bebb993Sopenharmony_ci
1465bebb993Sopenharmony_ciint MoveFileImpl::MoveFile(const std::string& src, const std::string& dest, int mode)
1475bebb993Sopenharmony_ci{
1485bebb993Sopenharmony_ci    LOGI("FS_TEST:: MoveFileImpl::MoveFile start");
1495bebb993Sopenharmony_ci    if (CheckDir(src)) {
1505bebb993Sopenharmony_ci        LOGE("Invalid src");
1515bebb993Sopenharmony_ci        return EINVAL;
1525bebb993Sopenharmony_ci    }
1535bebb993Sopenharmony_ci    if (CheckDir(dest)) {
1545bebb993Sopenharmony_ci        LOGE("Invalid dest");
1555bebb993Sopenharmony_ci        return EINVAL;
1565bebb993Sopenharmony_ci    }
1575bebb993Sopenharmony_ci    uv_fs_t accessReq;
1585bebb993Sopenharmony_ci    int ret = uv_fs_access(nullptr, &accessReq, src.c_str(), W_OK, nullptr);
1595bebb993Sopenharmony_ci    if (ret < 0) {
1605bebb993Sopenharmony_ci        LOGE("Failed to move src file due to doesn't exist or hasn't write permission");
1615bebb993Sopenharmony_ci        uv_fs_req_cleanup(&accessReq);
1625bebb993Sopenharmony_ci        return ret;
1635bebb993Sopenharmony_ci    }
1645bebb993Sopenharmony_ci    if (mode == MODE_THROW_ERR) {
1655bebb993Sopenharmony_ci        ret = uv_fs_access(nullptr, &accessReq, dest.c_str(), 0, nullptr);
1665bebb993Sopenharmony_ci        uv_fs_req_cleanup(&accessReq);
1675bebb993Sopenharmony_ci        if (ret == 0) {
1685bebb993Sopenharmony_ci            LOGE("Failed to move file due to existing destPath with MODE_THROW_ERR.");
1695bebb993Sopenharmony_ci            return EEXIST;
1705bebb993Sopenharmony_ci        }
1715bebb993Sopenharmony_ci        if (ret < 0 && (string_view(uv_err_name(ret)) != "ENOENT")) {
1725bebb993Sopenharmony_ci            LOGE("Failed to access destPath with MODE_THROW_ERR.");
1735bebb993Sopenharmony_ci            return ret;
1745bebb993Sopenharmony_ci        }
1755bebb993Sopenharmony_ci    } else {
1765bebb993Sopenharmony_ci        uv_fs_req_cleanup(&accessReq);
1775bebb993Sopenharmony_ci    }
1785bebb993Sopenharmony_ci    return RenameFile(src, dest);
1795bebb993Sopenharmony_ci}
1805bebb993Sopenharmony_ci
1815bebb993Sopenharmony_ci}
1825bebb993Sopenharmony_ci}