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 "io/std_file.h"
17
18 #include <cstdint>
19
20 #ifdef __has_include
21 #if __has_include(<filesystem>)
22 #include <filesystem>
23 #endif
24 #endif
25
26 #ifndef HAS_FILESYSTEM
27 #include <cerrno>
28 #include <dirent.h>
29
30 #ifndef _DIRENT_HAVE_D_TYPE
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #endif
35 #include <climits>
36 #define CORE_MAX_PATH PATH_MAX
37 #endif
38
39 #include <base/containers/string.h>
40 #include <base/containers/string_view.h>
41 #include <core/io/intf_file.h>
42 #include <core/log.h>
43 #include <core/namespace.h>
44
45 #include "io/std_directory.h"
46
47 CORE_BEGIN_NAMESPACE()
48 using BASE_NS::string;
49 using BASE_NS::string_view;
50
51 namespace {
OpenFileAccessMode(IFile::Mode mode)52 std::ios_base::openmode OpenFileAccessMode(IFile::Mode mode)
53 {
54 switch (mode) {
55 case IFile::Mode::INVALID:
56 CORE_ASSERT_MSG(false, "Invalid file access mode.");
57 return {};
58 case IFile::Mode::READ_ONLY:
59 return std::ios_base::binary | std::ios_base::in;
60 case IFile::Mode::READ_WRITE:
61 return std::ios_base::binary | std::ios_base::out | std::ios_base::in;
62 default:
63 return {};
64 }
65 }
66
CreateFileAccessMode(IFile::Mode mode)67 std::ios_base::openmode CreateFileAccessMode(IFile::Mode mode)
68 {
69 switch (mode) {
70 case IFile::Mode::INVALID:
71 case IFile::Mode::READ_ONLY:
72 CORE_ASSERT_MSG(false, "Invalid create file access mode.");
73 return {};
74 case IFile::Mode::READ_WRITE:
75 return std::ios_base::binary | std::ios_base::out | std::ios_base::in | std::ios_base::trunc;
76 default:
77 return {};
78 }
79 }
80
81 #if defined(HAS_FILESYSTEM)
U8Path(string_view str)82 std::filesystem::path U8Path(string_view str)
83 {
84 return std::filesystem::u8path(str.begin().ptr(), str.end().ptr());
85 }
86 #endif
87 } // namespace
88
StdFile(Mode mode,std::fstream && stream)89 StdFile::StdFile(Mode mode, std::fstream&& stream) : mode_(mode), file_(BASE_NS::move(stream)) {}
90
~StdFile()91 StdFile::~StdFile()
92 {
93 Close();
94 }
95
GetMode() const96 IFile::Mode StdFile::GetMode() const
97 {
98 return mode_;
99 }
100
IsValidPath(const string_view)101 bool StdFile::IsValidPath(const string_view /* path */)
102 {
103 // Path's should always be valid here.
104 return true;
105 }
106
Open(const string_view path,Mode mode)107 IFile::Ptr StdFile::Open(const string_view path, Mode mode)
108 {
109 #if defined(HAS_FILESYSTEM)
110 std::error_code ec;
111 auto canonicalPath = std::filesystem::canonical(U8Path(path), ec);
112 if (ec) {
113 return {};
114 }
115
116 if (std::filesystem::is_directory(canonicalPath)) {
117 return {};
118 }
119
120 if (auto stream = std::fstream(canonicalPath, OpenFileAccessMode(mode)); stream) {
121 return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
122 }
123 #else
124 char canonicalPath[CORE_MAX_PATH] = { 0 };
125 if (realpath(string(path).c_str(), canonicalPath) == nullptr) {
126 return {};
127 }
128
129 if (auto stream = std::fstream(canonicalPath, OpenFileAccessMode(mode)); stream) {
130 return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
131 }
132
133 #endif
134 return {};
135 }
136
Create(const string_view path,Mode mode)137 IFile::Ptr StdFile::Create(const string_view path, Mode mode)
138 {
139 if (path.empty()) {
140 return {};
141 }
142
143 #if defined(HAS_FILESYSTEM)
144 std::error_code ec;
145 auto canonicalPath = std::filesystem::weakly_canonical(U8Path(path), ec);
146 if (ec) {
147 return {};
148 }
149 // Create the file.
150 if (auto stream = std::fstream(canonicalPath, CreateFileAccessMode(mode)); stream) {
151 return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
152 }
153 #else
154 // NOTE: As realpath requires that the path exists, we check that the
155 // parent dir is valid instead of the full path of the file being created.
156 const string parentDir = StdDirectory::GetDirName(path);
157 char canonicalParentPath[CORE_MAX_PATH] = { 0 };
158 if (realpath(parentDir.c_str(), canonicalParentPath) == nullptr) {
159 return {};
160 }
161 const string filename = StdDirectory::GetBaseName(path);
162 const string fullPath = string_view(canonicalParentPath) + '/' + filename;
163
164 // Create the file.
165 if (auto stream = std::fstream(fullPath.data(), CreateFileAccessMode(mode)); stream) {
166 return IFile::Ptr { BASE_NS::make_unique<StdFile>(mode, BASE_NS::move(stream)).release() };
167 }
168 #endif
169 return {};
170 }
171
Close()172 void StdFile::Close()
173 {
174 if (file_.is_open()) {
175 file_ = {};
176 mode_ = Mode::INVALID;
177 }
178 }
179
Read(void * buffer,uint64_t count)180 uint64_t StdFile::Read(void* buffer, uint64_t count)
181 {
182 file_.read(static_cast<char*>(buffer), static_cast<std::streamsize>(count));
183 if (file_.eof() && file_.fail()) {
184 file_.clear();
185 }
186 return static_cast<uint64_t>(file_.gcount());
187 }
188
Write(const void * buffer,uint64_t count)189 uint64_t StdFile::Write(const void* buffer, uint64_t count)
190 {
191 const auto pos = file_.tellp();
192 file_.write(static_cast<const char*>(buffer), static_cast<std::streamsize>(count));
193 return static_cast<uint64_t>(file_.tellp() - pos);
194 }
195
GetLength() const196 uint64_t StdFile::GetLength() const
197 {
198 const auto offset = file_.tellg();
199 if (offset == decltype(file_)::pos_type(-1)) {
200 return {};
201 }
202
203 const auto end = file_.seekg(0, std::ios_base::end).tellg();
204 if (!file_.good()) {
205 return {};
206 }
207 const auto beg = file_.seekg(0, std::ios_base::beg).tellg();
208 file_.seekg(offset);
209 return static_cast<uint64_t>(end - beg);
210 }
211
Seek(uint64_t offset)212 bool StdFile::Seek(uint64_t offset)
213 {
214 file_.seekg(static_cast<decltype(file_)::off_type>(offset), std::ios_base::beg);
215 return file_.good();
216 }
217
GetPosition() const218 uint64_t StdFile::GetPosition() const
219 {
220 return static_cast<uint64_t>(file_.tellg());
221 }
222 CORE_END_NAMESPACE()
223