18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#include "io/std_file.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <cstdint>
198bf80f4bSopenharmony_ci
208bf80f4bSopenharmony_ci#ifdef __has_include
218bf80f4bSopenharmony_ci#if __has_include(<filesystem>)
228bf80f4bSopenharmony_ci#include <filesystem>
238bf80f4bSopenharmony_ci#endif
248bf80f4bSopenharmony_ci#endif
258bf80f4bSopenharmony_ci
268bf80f4bSopenharmony_ci#ifndef HAS_FILESYSTEM
278bf80f4bSopenharmony_ci#include <cerrno>
288bf80f4bSopenharmony_ci#include <dirent.h>
298bf80f4bSopenharmony_ci
308bf80f4bSopenharmony_ci#ifndef _DIRENT_HAVE_D_TYPE
318bf80f4bSopenharmony_ci#include <sys/stat.h>
328bf80f4bSopenharmony_ci#include <sys/types.h>
338bf80f4bSopenharmony_ci
348bf80f4bSopenharmony_ci#endif
358bf80f4bSopenharmony_ci#include <climits>
368bf80f4bSopenharmony_ci#define CORE_MAX_PATH PATH_MAX
378bf80f4bSopenharmony_ci#endif
388bf80f4bSopenharmony_ci
398bf80f4bSopenharmony_ci#include <base/containers/string.h>
408bf80f4bSopenharmony_ci#include <base/containers/string_view.h>
418bf80f4bSopenharmony_ci#include <core/io/intf_file.h>
428bf80f4bSopenharmony_ci#include <core/log.h>
438bf80f4bSopenharmony_ci#include <core/namespace.h>
448bf80f4bSopenharmony_ci
458bf80f4bSopenharmony_ci#include "io/std_directory.h"
468bf80f4bSopenharmony_ci
478bf80f4bSopenharmony_ciCORE_BEGIN_NAMESPACE()
488bf80f4bSopenharmony_ciusing BASE_NS::string;
498bf80f4bSopenharmony_ciusing BASE_NS::string_view;
508bf80f4bSopenharmony_ci
518bf80f4bSopenharmony_cinamespace {
528bf80f4bSopenharmony_cistd::ios_base::openmode OpenFileAccessMode(IFile::Mode mode)
538bf80f4bSopenharmony_ci{
548bf80f4bSopenharmony_ci    switch (mode) {
558bf80f4bSopenharmony_ci        case IFile::Mode::INVALID:
568bf80f4bSopenharmony_ci            CORE_ASSERT_MSG(false, "Invalid file access mode.");
578bf80f4bSopenharmony_ci            return {};
588bf80f4bSopenharmony_ci        case IFile::Mode::READ_ONLY:
598bf80f4bSopenharmony_ci            return std::ios_base::binary | std::ios_base::in;
608bf80f4bSopenharmony_ci        case IFile::Mode::READ_WRITE:
618bf80f4bSopenharmony_ci            return std::ios_base::binary | std::ios_base::out | std::ios_base::in;
628bf80f4bSopenharmony_ci        default:
638bf80f4bSopenharmony_ci            return {};
648bf80f4bSopenharmony_ci    }
658bf80f4bSopenharmony_ci}
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_cistd::ios_base::openmode CreateFileAccessMode(IFile::Mode mode)
688bf80f4bSopenharmony_ci{
698bf80f4bSopenharmony_ci    switch (mode) {
708bf80f4bSopenharmony_ci        case IFile::Mode::INVALID:
718bf80f4bSopenharmony_ci        case IFile::Mode::READ_ONLY:
728bf80f4bSopenharmony_ci            CORE_ASSERT_MSG(false, "Invalid create file access mode.");
738bf80f4bSopenharmony_ci            return {};
748bf80f4bSopenharmony_ci        case IFile::Mode::READ_WRITE:
758bf80f4bSopenharmony_ci            return std::ios_base::binary | std::ios_base::out | std::ios_base::in | std::ios_base::trunc;
768bf80f4bSopenharmony_ci        default:
778bf80f4bSopenharmony_ci            return {};
788bf80f4bSopenharmony_ci    }
798bf80f4bSopenharmony_ci}
808bf80f4bSopenharmony_ci
818bf80f4bSopenharmony_ci#if defined(HAS_FILESYSTEM)
828bf80f4bSopenharmony_cistd::filesystem::path U8Path(string_view str)
838bf80f4bSopenharmony_ci{
848bf80f4bSopenharmony_ci    return std::filesystem::u8path(str.begin().ptr(), str.end().ptr());
858bf80f4bSopenharmony_ci}
868bf80f4bSopenharmony_ci#endif
878bf80f4bSopenharmony_ci} // namespace
888bf80f4bSopenharmony_ci
898bf80f4bSopenharmony_ciStdFile::StdFile(Mode mode, std::fstream&& stream) : mode_(mode), file_(BASE_NS::move(stream)) {}
908bf80f4bSopenharmony_ci
918bf80f4bSopenharmony_ciStdFile::~StdFile()
928bf80f4bSopenharmony_ci{
938bf80f4bSopenharmony_ci    Close();
948bf80f4bSopenharmony_ci}
958bf80f4bSopenharmony_ci
968bf80f4bSopenharmony_ciIFile::Mode StdFile::GetMode() const
978bf80f4bSopenharmony_ci{
988bf80f4bSopenharmony_ci    return mode_;
998bf80f4bSopenharmony_ci}
1008bf80f4bSopenharmony_ci
1018bf80f4bSopenharmony_cibool StdFile::IsValidPath(const string_view /* path */)
1028bf80f4bSopenharmony_ci{
1038bf80f4bSopenharmony_ci    // Path's should always be valid here.
1048bf80f4bSopenharmony_ci    return true;
1058bf80f4bSopenharmony_ci}
1068bf80f4bSopenharmony_ci
1078bf80f4bSopenharmony_ciIFile::Ptr StdFile::Open(const string_view path, Mode mode)
1088bf80f4bSopenharmony_ci{
1098bf80f4bSopenharmony_ci#if defined(HAS_FILESYSTEM)
1108bf80f4bSopenharmony_ci    std::error_code ec;
1118bf80f4bSopenharmony_ci    auto canonicalPath = std::filesystem::canonical(U8Path(path), ec);
1128bf80f4bSopenharmony_ci    if (ec) {
1138bf80f4bSopenharmony_ci        return {};
1148bf80f4bSopenharmony_ci    }
1158bf80f4bSopenharmony_ci
1168bf80f4bSopenharmony_ci    if (std::filesystem::is_directory(canonicalPath)) {
1178bf80f4bSopenharmony_ci        return {};
1188bf80f4bSopenharmony_ci    }
1198bf80f4bSopenharmony_ci
1208bf80f4bSopenharmony_ci    if (auto stream = std::fstream(canonicalPath, OpenFileAccessMode(mode)); stream) {
1218bf80f4bSopenharmony_ci        return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
1228bf80f4bSopenharmony_ci    }
1238bf80f4bSopenharmony_ci#else
1248bf80f4bSopenharmony_ci    char canonicalPath[CORE_MAX_PATH] = { 0 };
1258bf80f4bSopenharmony_ci    if (realpath(string(path).c_str(), canonicalPath) == nullptr) {
1268bf80f4bSopenharmony_ci        return {};
1278bf80f4bSopenharmony_ci    }
1288bf80f4bSopenharmony_ci
1298bf80f4bSopenharmony_ci    if (auto stream = std::fstream(canonicalPath, OpenFileAccessMode(mode)); stream) {
1308bf80f4bSopenharmony_ci        return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
1318bf80f4bSopenharmony_ci    }
1328bf80f4bSopenharmony_ci
1338bf80f4bSopenharmony_ci#endif
1348bf80f4bSopenharmony_ci    return {};
1358bf80f4bSopenharmony_ci}
1368bf80f4bSopenharmony_ci
1378bf80f4bSopenharmony_ciIFile::Ptr StdFile::Create(const string_view path, Mode mode)
1388bf80f4bSopenharmony_ci{
1398bf80f4bSopenharmony_ci    if (path.empty()) {
1408bf80f4bSopenharmony_ci        return {};
1418bf80f4bSopenharmony_ci    }
1428bf80f4bSopenharmony_ci
1438bf80f4bSopenharmony_ci#if defined(HAS_FILESYSTEM)
1448bf80f4bSopenharmony_ci    std::error_code ec;
1458bf80f4bSopenharmony_ci    auto canonicalPath = std::filesystem::weakly_canonical(U8Path(path), ec);
1468bf80f4bSopenharmony_ci    if (ec) {
1478bf80f4bSopenharmony_ci        return {};
1488bf80f4bSopenharmony_ci    }
1498bf80f4bSopenharmony_ci    // Create the file.
1508bf80f4bSopenharmony_ci    if (auto stream = std::fstream(canonicalPath, CreateFileAccessMode(mode)); stream) {
1518bf80f4bSopenharmony_ci        return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
1528bf80f4bSopenharmony_ci    }
1538bf80f4bSopenharmony_ci#else
1548bf80f4bSopenharmony_ci    // NOTE: As realpath requires that the path exists, we check that the
1558bf80f4bSopenharmony_ci    // parent dir is valid instead of the full path of the file being created.
1568bf80f4bSopenharmony_ci    const string parentDir = StdDirectory::GetDirName(path);
1578bf80f4bSopenharmony_ci    char canonicalParentPath[CORE_MAX_PATH] = { 0 };
1588bf80f4bSopenharmony_ci    if (realpath(parentDir.c_str(), canonicalParentPath) == nullptr) {
1598bf80f4bSopenharmony_ci        return {};
1608bf80f4bSopenharmony_ci    }
1618bf80f4bSopenharmony_ci    const string filename = StdDirectory::GetBaseName(path);
1628bf80f4bSopenharmony_ci    const string fullPath = string_view(canonicalParentPath) + '/' + filename;
1638bf80f4bSopenharmony_ci
1648bf80f4bSopenharmony_ci    // Create the file.
1658bf80f4bSopenharmony_ci    if (auto stream = std::fstream(fullPath.data(), CreateFileAccessMode(mode)); stream) {
1668bf80f4bSopenharmony_ci        return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
1678bf80f4bSopenharmony_ci    }
1688bf80f4bSopenharmony_ci#endif
1698bf80f4bSopenharmony_ci    return {};
1708bf80f4bSopenharmony_ci}
1718bf80f4bSopenharmony_ci
1728bf80f4bSopenharmony_civoid StdFile::Close()
1738bf80f4bSopenharmony_ci{
1748bf80f4bSopenharmony_ci    if (file_.is_open()) {
1758bf80f4bSopenharmony_ci        file_ = {};
1768bf80f4bSopenharmony_ci        mode_ = Mode::INVALID;
1778bf80f4bSopenharmony_ci    }
1788bf80f4bSopenharmony_ci}
1798bf80f4bSopenharmony_ci
1808bf80f4bSopenharmony_ciuint64_t StdFile::Read(void* buffer, uint64_t count)
1818bf80f4bSopenharmony_ci{
1828bf80f4bSopenharmony_ci    file_.read(static_cast<char*>(buffer), static_cast<std::streamsize>(count));
1838bf80f4bSopenharmony_ci    if (file_.eof() && file_.fail()) {
1848bf80f4bSopenharmony_ci        file_.clear();
1858bf80f4bSopenharmony_ci    }
1868bf80f4bSopenharmony_ci    return static_cast<uint64_t>(file_.gcount());
1878bf80f4bSopenharmony_ci}
1888bf80f4bSopenharmony_ci
1898bf80f4bSopenharmony_ciuint64_t StdFile::Write(const void* buffer, uint64_t count)
1908bf80f4bSopenharmony_ci{
1918bf80f4bSopenharmony_ci    const auto pos = file_.tellp();
1928bf80f4bSopenharmony_ci    file_.write(static_cast<const char*>(buffer), static_cast<std::streamsize>(count));
1938bf80f4bSopenharmony_ci    return static_cast<uint64_t>(file_.tellp() - pos);
1948bf80f4bSopenharmony_ci}
1958bf80f4bSopenharmony_ci
1968bf80f4bSopenharmony_ciuint64_t StdFile::GetLength() const
1978bf80f4bSopenharmony_ci{
1988bf80f4bSopenharmony_ci    const auto offset = file_.tellg();
1998bf80f4bSopenharmony_ci    if (offset == decltype(file_)::pos_type(-1)) {
2008bf80f4bSopenharmony_ci        return {};
2018bf80f4bSopenharmony_ci    }
2028bf80f4bSopenharmony_ci
2038bf80f4bSopenharmony_ci    const auto end = file_.seekg(0, std::ios_base::end).tellg();
2048bf80f4bSopenharmony_ci    if (!file_.good()) {
2058bf80f4bSopenharmony_ci        return {};
2068bf80f4bSopenharmony_ci    }
2078bf80f4bSopenharmony_ci    const auto beg = file_.seekg(0, std::ios_base::beg).tellg();
2088bf80f4bSopenharmony_ci    file_.seekg(offset);
2098bf80f4bSopenharmony_ci    return static_cast<uint64_t>(end - beg);
2108bf80f4bSopenharmony_ci}
2118bf80f4bSopenharmony_ci
2128bf80f4bSopenharmony_cibool StdFile::Seek(uint64_t offset)
2138bf80f4bSopenharmony_ci{
2148bf80f4bSopenharmony_ci    file_.seekg(static_cast<decltype(file_)::off_type>(offset), std::ios_base::beg);
2158bf80f4bSopenharmony_ci    return file_.good();
2168bf80f4bSopenharmony_ci}
2178bf80f4bSopenharmony_ci
2188bf80f4bSopenharmony_ciuint64_t StdFile::GetPosition() const
2198bf80f4bSopenharmony_ci{
2208bf80f4bSopenharmony_ci    return static_cast<uint64_t>(file_.tellg());
2218bf80f4bSopenharmony_ci}
2228bf80f4bSopenharmony_ciCORE_END_NAMESPACE()
223