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 "std_filesystem.h"
17
18 #if defined(__has_include)
19 #if __has_include(<filesystem>)
20 #include <filesystem>
21 #endif
22 #endif // defined(__has_include)
23
24 #if !defined(HAS_FILESYSTEM)
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #endif
28
29 #include <cstdint>
30 #include <cstdio>
31
32 #include <base/containers/string.h>
33 #include <base/containers/string_view.h>
34 #include <base/containers/type_traits.h>
35 #include <base/containers/unique_ptr.h>
36 #include <base/containers/vector.h>
37 #include <base/namespace.h>
38 #include <core/io/intf_directory.h>
39 #include <core/io/intf_file.h>
40 #include <core/log.h>
41 #include <core/namespace.h>
42
43 #include "io/path_tools.h"
44 #include "std_directory.h"
45 #include "std_file.h"
46
47 CORE_BEGIN_NAMESPACE()
48 using BASE_NS::make_unique;
49 using BASE_NS::string;
50 using BASE_NS::string_view;
51 using BASE_NS::vector;
52
53 namespace {
54 #if defined(HAS_FILESYSTEM)
U8Path(string_view str)55 std::filesystem::path U8Path(string_view str)
56 {
57 return std::filesystem::u8path(str.begin().ptr(), str.end().ptr());
58 }
59 #endif
60 } // namespace
61
ValidatePath(const string_view pathIn) const62 string StdFilesystem::ValidatePath(const string_view pathIn) const
63 {
64 auto path = NormalizePath(pathIn);
65 if (!path.empty()) {
66 if (!basePath_.empty()) {
67 // If basePath_ is set we are in a sandbox. so all paths are relative to basePath_ (after normalization)
68 path = basePath_ + path;
69 }
70 // path must be absolute.
71 if (path[0] != '/') {
72 CORE_LOG_V("Corrupted path in StdFilesystem::ValidatePath. not absolute");
73 return "";
74 }
75 #ifdef _WIN32
76 // path must have drive letter, otherwise it is NOT absolute. ie. must conform to "/C:/" style
77 if ((path.length() < 4) || (path[2] != ':') || (path[3] != '/')) { // 4: size limit; 2 3: index of ':' '/'
78 CORE_LOG_V("Corrupted path in StdFilesystem::ValidatePath. missing drive letter, or incorrect root");
79 return "";
80 }
81 // remove the '/' slash, which is not used in windows.
82 return string(path.substr(1));
83 #endif
84 }
85 return path;
86 }
87
OpenFile(const string_view pathIn)88 IFile::Ptr StdFilesystem::OpenFile(const string_view pathIn)
89 {
90 auto path = ValidatePath(pathIn);
91 if (!path.empty()) {
92 return StdFile::Open(path, IFile::Mode::READ_ONLY);
93 }
94 return IFile::Ptr();
95 }
96
CreateFile(const string_view pathIn)97 IFile::Ptr StdFilesystem::CreateFile(const string_view pathIn)
98 {
99 auto path = ValidatePath(pathIn);
100 if (!path.empty()) {
101 return StdFile::Create(path, IFile::Mode::READ_WRITE);
102 }
103
104 return IFile::Ptr();
105 }
106
DeleteFile(const string_view pathIn)107 bool StdFilesystem::DeleteFile(const string_view pathIn)
108 {
109 auto path = ValidatePath(pathIn);
110 if (path.empty()) {
111 return false;
112 }
113 #if defined(HAS_FILESYSTEM)
114 std::error_code ec;
115 return std::filesystem::remove(U8Path(path), ec) && !ec;
116 #else
117 return std::remove(path.c_str()) == 0;
118 #endif
119 }
120
OpenDirectory(const string_view pathIn)121 IDirectory::Ptr StdFilesystem::OpenDirectory(const string_view pathIn)
122 {
123 auto path = ValidatePath(pathIn);
124 if (!path.empty()) {
125 return IDirectory::Ptr { StdDirectory::Open(path).release() };
126 }
127
128 return IDirectory::Ptr();
129 }
130
CreateDirectory(const string_view pathIn)131 IDirectory::Ptr StdFilesystem::CreateDirectory(const string_view pathIn)
132 {
133 auto path = ValidatePath(pathIn);
134 if (!path.empty()) {
135 return IDirectory::Ptr { StdDirectory::Create(path).release() };
136 }
137
138 return IDirectory::Ptr();
139 }
140
DeleteDirectory(const string_view pathIn)141 bool StdFilesystem::DeleteDirectory(const string_view pathIn)
142 {
143 auto path = ValidatePath(pathIn);
144 if (path.empty()) {
145 return false;
146 }
147 #if defined(HAS_FILESYSTEM)
148 std::error_code ec;
149 return std::filesystem::remove(U8Path(path), ec) && !ec;
150 #else
151 return rmdir(string(path).c_str()) == 0;
152 #endif
153 }
154
Rename(const string_view fromPath,const string_view toPath)155 bool StdFilesystem::Rename(const string_view fromPath, const string_view toPath)
156 {
157 auto pathFrom = ValidatePath(fromPath);
158 auto pathTo = ValidatePath(toPath);
159 if (pathFrom.empty() || pathTo.empty()) {
160 return false;
161 }
162
163 #if defined(HAS_FILESYSTEM)
164 std::error_code ec;
165 std::filesystem::rename(U8Path(pathFrom), U8Path(pathTo), ec);
166 return !ec;
167 #else
168 return std::rename(pathFrom.c_str(), pathTo.c_str()) == 0;
169 #endif
170 }
171
GetUriPaths(const string_view) const172 vector<string> StdFilesystem::GetUriPaths(const string_view) const
173 {
174 return {};
175 }
176
StdFilesystem(string_view basePath)177 StdFilesystem::StdFilesystem(string_view basePath) : basePath_(basePath)
178 {
179 // remove the extraneous slash
180 if (basePath_.back() == '/') {
181 basePath_.resize(basePath_.size() - 1);
182 }
183 }
184
185 CORE_END_NAMESPACE()
186
187 // the rest is here, due to shlwapi leaking windows CreateFile macro, and breaking build.
188 #if !defined(HAS_FILESYSTEM)
189 #include <climits>
190 #define CORE_MAX_PATH PATH_MAX
191 #endif
192
CORE_BEGIN_NAMESPACE()193 CORE_BEGIN_NAMESPACE()
194 IDirectory::Entry StdFilesystem::GetEntry(const string_view uriIn)
195 {
196 auto uri = ValidatePath(uriIn);
197 if (!uri.empty()) {
198 #if defined(HAS_FILESYSTEM)
199 std::error_code ec;
200 auto canonicalPath = std::filesystem::canonical(U8Path(uri), ec);
201 if (ec) {
202 return {};
203 }
204 auto status = std::filesystem::status(canonicalPath, ec);
205 if (ec) {
206 return {};
207 }
208 auto time = std::filesystem::last_write_time(canonicalPath, ec);
209 if (ec) {
210 return {};
211 }
212
213 auto asString = canonicalPath.u8string();
214 if (std::filesystem::is_directory(status)) {
215 return { IDirectory::Entry::DIRECTORY, string { asString.data(), asString.size() },
216 static_cast<uint64_t>(time.time_since_epoch().count()) };
217 }
218 if (std::filesystem::is_regular_file(status)) {
219 return { IDirectory::Entry::FILE, string { asString.data(), asString.size() },
220 static_cast<uint64_t>(time.time_since_epoch().count()) };
221 }
222 #else
223 auto path = string(uri);
224 char canonicalPath[CORE_MAX_PATH] = { 0 };
225
226 if (realpath(path.c_str(), canonicalPath) == nullptr) {
227 return {};
228 }
229 struct stat ds {};
230 if (stat(canonicalPath, &ds) != 0) {
231 return {};
232 }
233
234 if ((ds.st_mode & S_IFDIR)) {
235 return { IDirectory::Entry::DIRECTORY, canonicalPath, static_cast<uint64_t>(ds.st_mtime) };
236 }
237 if ((ds.st_mode & S_IFREG)) {
238 return { IDirectory::Entry::FILE, canonicalPath, static_cast<uint64_t>(ds.st_mtime) };
239 }
240 #endif
241 }
242 return {};
243 }
244
245 CORE_END_NAMESPACE()
246