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