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