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