1 /*
2 * Copyright (C) 2023 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 <io/std_directory.h>
19
20 #include <core/log.h>
21 #include <core/namespace.h>
22
23 #if defined(_WIN32)
24 #include <shlwapi.h>
25 #pragma comment(lib, "Shlwapi.lib")
26 #define CORE_MAX_PATH MAX_PATH
27 #else
28 #include <cerrno>
29 #include <dirent.h>
30
31 #ifndef _DIRENT_HAVE_D_TYPE
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #endif
36 #include <climits>
37 #define CORE_MAX_PATH PATH_MAX
38 #endif
39
40 CORE_BEGIN_NAMESPACE()
41 using BASE_NS::string;
42 using BASE_NS::string_view;
43
44 namespace {
OpenFileAccessMode(IFile::Mode mode)45 const char* OpenFileAccessMode(IFile::Mode mode)
46 {
47 switch (mode) {
48 case IFile::Mode::INVALID:
49 CORE_ASSERT_MSG(false, "Invalid file access mode.");
50 return "";
51 case IFile::Mode::READ_ONLY:
52 return "rb";
53 case IFile::Mode::READ_WRITE:
54 return "rb+";
55 default:
56 return "";
57 }
58 }
59
CreateFileAccessMode(IFile::Mode mode)60 const char* CreateFileAccessMode(IFile::Mode mode)
61 {
62 switch (mode) {
63 case IFile::Mode::INVALID:
64 case IFile::Mode::READ_ONLY:
65 CORE_ASSERT_MSG(false, "Invalid create file access mode.");
66 return "";
67 case IFile::Mode::READ_WRITE:
68 return "wb+";
69 default:
70 return "";
71 }
72 }
73 } // namespace
74
~StdFile()75 StdFile::~StdFile()
76 {
77 Close();
78 }
79
GetMode() const80 IFile::Mode StdFile::GetMode() const
81 {
82 return mode_;
83 }
84
IsValidPath(const string_view path)85 bool StdFile::IsValidPath(const string_view path)
86 {
87 // Path's should always be valid here.
88 return true;
89 }
90
Open(const string_view path,Mode mode)91 bool StdFile::Open(const string_view path, Mode mode)
92 {
93 char canonicalPath[CORE_MAX_PATH] = { 0 };
94
95 #if defined(_WIN32)
96 if (!PathCanonicalize(canonicalPath, string(path).c_str())) {
97 return false;
98 }
99 #else
100 if (realpath(string(path).c_str(), canonicalPath) == NULL) {
101 return false;
102 }
103 #endif
104 if (!IsValidPath(canonicalPath)) {
105 return false;
106 }
107 file_ = fopen(canonicalPath, OpenFileAccessMode(mode));
108 if (file_) {
109 mode_ = mode;
110 return true;
111 }
112
113 return false;
114 }
115
Create(const string_view path,Mode mode)116 bool StdFile::Create(const string_view path, Mode mode)
117 {
118 if (path.empty()) {
119 return false;
120 }
121
122 // NOTE: As realpath requires that the path exists, we check that the
123 // parent dir is valid instead of the full path of the file being created.
124 const string parentDir = StdDirectory::GetDirName(path);
125 char canonicalParentPath[CORE_MAX_PATH] = { 0 };
126
127 #if defined(_WIN32)
128 if (!PathCanonicalize(canonicalParentPath, parentDir.c_str())) {
129 return false;
130 }
131 #else
132 if (realpath(parentDir.c_str(), canonicalParentPath) == NULL) {
133 return false;
134 }
135 #endif
136
137 const string filename = StdDirectory::GetBaseName(path);
138 const string fullPath = string_view(canonicalParentPath) + '/' + filename;
139 if (!IsValidPath(fullPath.c_str())) {
140 return false;
141 }
142
143 // Create the file.
144 file_ = fopen(fullPath.c_str(), CreateFileAccessMode(mode));
145 if (file_) {
146 mode_ = mode;
147 return true;
148 }
149
150 return false;
151 }
152
Close()153 void StdFile::Close()
154 {
155 if (file_) {
156 if (fclose(file_) != 0) {
157 CORE_LOG_E("Failed to close file.");
158 }
159
160 mode_ = Mode::INVALID;
161 file_ = nullptr;
162 }
163 }
164
Read(void * buffer,uint64_t count)165 uint64_t StdFile::Read(void* buffer, uint64_t count)
166 {
167 return fread(buffer, 1, static_cast<size_t>(count), file_);
168 }
169
Write(const void * buffer,uint64_t count)170 uint64_t StdFile::Write(const void* buffer, uint64_t count)
171 {
172 return fwrite(buffer, 1, static_cast<size_t>(count), file_);
173 }
174
GetLength() const175 uint64_t StdFile::GetLength() const
176 {
177 const long offset = ftell(file_);
178 if (offset == EOF) {
179 CORE_ASSERT_MSG(false, "ftell failed, unable to evaluate file length.");
180 return {};
181 }
182
183 if (fseek(file_, 0, SEEK_END) != 0) {
184 // Library implementations are allowed to not meaningfully support SEEK_END.
185 CORE_ASSERT_MSG(false, "SEEK_END is not supported, unable to evaluate file length.");
186 }
187
188 uint64_t length = 0;
189 if (auto const endPosition = ftell(file_); endPosition != EOF) {
190 length = static_cast<uint64_t>(endPosition);
191 }
192 fseek(file_, offset, SEEK_SET);
193
194 return length;
195 }
196
Seek(uint64_t offset)197 bool StdFile::Seek(uint64_t offset)
198 {
199 return fseek(file_, static_cast<long>(offset), SEEK_SET) == 0;
200 }
201
GetPosition() const202 uint64_t StdFile::GetPosition() const
203 {
204 return static_cast<uint64_t>(ftell(file_));
205 }
206
207 CORE_END_NAMESPACE()
208