• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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