sandbox
Loading...
Searching...
No Matches
native_filesystem.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_FILESYSTEM_NATIVE_FILESYSTEM_HPP_
3#define LIBSBX_FILESYSTEM_NATIVE_FILESYSTEM_HPP_
4
5#include <string>
6#include <vector>
7#include <unordered_map>
8#include <mutex>
9#include <filesystem>
10#include <algorithm>
11
12#include <libsbx/utility/lockable.hpp>
13
14#include <libsbx/filesystem/filesystem_base.hpp>
15#include <libsbx/filesystem/native_file.hpp>
16
17namespace sbx::filesystem {
18
19template<utility::lockable Lockable>
21
22public:
23
24 using lockable_type = Lockable;
25
26 basic_native_filesystem(const std::string& alias_path, const std::string& base_path)
27 : _alias_path{alias_path},
28 _base_path{base_path} { }
29
30 ~basic_native_filesystem() override {
31 shutdown();
32 }
33
34 auto initialize() -> bool override {
35 auto lock = std::scoped_lock{_mutex};
36
37 if (_is_initialized) {
38 return true;
39 }
40
41 if (!std::filesystem::exists(_base_path) || !std::filesystem::is_directory(_base_path)) {
42 return false;
43 }
44
45 _build_file_list(_alias_path, _base_path);
46 _is_initialized = true;
47
48 return true;
49 }
50
51 auto shutdown() -> void override {
52 auto lock = std::scoped_lock{_mutex};
53
54 _base_path.clear();
55 _alias_path.clear();
56 _files.clear();
57 _is_initialized = false;
58 }
59
60 [[nodiscard]] auto is_initialized() const -> bool override {
61 auto lock = std::scoped_lock{_mutex};
62
63 return _is_initialized;
64 }
65
66 [[nodiscard]] auto base_path() const -> const std::string& override {
67 auto lock = std::scoped_lock{_mutex};
68
69 return _base_path;
70 }
71
72 [[nodiscard]] auto virtual_path() const -> const std::string& override {
73 auto lock = std::scoped_lock{_mutex};
74
75 return _alias_path;
76 }
77
78 [[nodiscard]] auto is_read_only() const -> bool override {
79 auto lock = std::scoped_lock{_mutex};
80
81 if (!_is_initialized) {
82 return true;
83 }
84
85 auto permissions = std::filesystem::status(_base_path).permissions();
86 return (permissions & std::filesystem::perms::owner_write) == std::filesystem::perms::none;
87 }
88
89 auto open_file(const std::string& path, file_base::mode mode) -> std::shared_ptr<file_base> override {
90 auto lock = std::scoped_lock{_mutex};
91
92 const auto request_write = file_base::mode_has_flag(mode, file_base::mode::write);
93
94 if (request_write) {
95 auto permissions = std::filesystem::status(_base_path).permissions();
96
97 if ((permissions & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
98 return nullptr;
99 }
100 }
101
102 auto entry = _files.find(path);
103
104 if (entry == _files.end() && request_write) {
105 auto info = file_info{_alias_path, _base_path, path};
106 entry = _files.emplace(path, file_entry{info}).first;
107 }
108
109 if (entry == _files.end()) {
110 return nullptr;
111 }
112
113 auto& file_entry = entry->second;
114 auto file = std::make_shared<basic_native_file<lockable_type>>(file_entry.info);
115
116 if (!file || !file->open(mode)) {
117 return nullptr;
118 }
119
120 file_entry.opened_handles.push_back(file);
121
122 return file;
123 }
124
125 auto close_file(const file_ptr& file) -> void override {
126 auto lock = std::scoped_lock{_mutex};
127
128 if (file) {
129 file->close();
130 }
131
132 for (auto& [path, entry] : _files) {
133 entry.cleanup_opened_handles(file);
134 }
135 }
136
137 auto remove_file(const std::string& path) -> bool override {
138 auto lock = std::scoped_lock{_mutex};
139
140 if (is_read_only()) {
141 return false;
142 }
143
144 const auto entry = _files.find(path);
145
146 if (entry == _files.end()) {
147 return false;
148 }
149
150 const auto native_path = entry->second.info.native_path();
151
152 _files.erase(entry);
153
154 return std::filesystem::remove(native_path);
155 }
156
157 [[nodiscard]] auto exists(const std::string& path) const -> bool override {
158 auto lock = std::scoped_lock{_mutex};
159
160 const auto entry = _files.find(path);
161
162 return entry != _files.end() && std::filesystem::exists(entry->second.info.native_path());
163 }
164
165 [[nodiscard]] auto files() const -> files_list override {
166 auto lock = std::scoped_lock{_mutex};
167
168 auto list = files_list{};
169 list.reserve(_files.size());
170
171 for (const auto& [path, entry] : _files) {
172 list.push_back(entry.info);
173 }
174
175 return list;
176 }
177
178 [[nodiscard]] auto create_file(const std::string& path) -> file_ptr override {
179 return open_file(path, file_base::mode::write | file_base::mode::truncate);
180 }
181
182 auto copy_file(const std::string& source, const std::string& destination, const bool overwrite = false) -> bool override {
183 auto lock = std::scoped_lock{_mutex};
184
185 if (is_read_only()) {
186 return false;
187 }
188
189 const auto entry = _files.find(source);
190
191 if (entry == _files.end()) {
192 return false;
193 }
194
195 const auto& source_info = entry->second.info;
196 const auto destination_info = file_info{_alias_path, _base_path, destination};
197
198 auto options = overwrite ? std::filesystem::copy_options::overwrite_existing : std::filesystem::copy_options::skip_existing;
199
200 auto ec = std::error_code{};
201
202 if (!std::filesystem::copy_file(source_info.native_path(), destination_info.native_path(), options, ec)) {
203 return false;
204 }
205
206 _files.insert_or_assign(destination, file_entry{destination_info});
207
208 return true;
209 }
210
211 auto rename_file(const std::string& source, const std::string& destination) -> bool override {
212 auto lock = std::scoped_lock{_mutex};
213
214 if (is_read_only()) {
215 return false;
216 }
217
218 const auto entry = _files.find(source);
219
220 if (entry == _files.end()) {
221 return false;
222 }
223
224 const auto& source_info = entry->second.info;
225 const auto destination_info = file_info{_alias_path, _base_path, destination};
226
227 auto ec = std::error_code{};
228 std::filesystem::rename(source_info.native_path(), destination_info.native_path(), ec);
229
230 if (ec) {
231 return false;
232 }
233
234 _files.erase(entry);
235 _files.insert_or_assign(destination, file_entry{destination_info});
236
237 return true;
238 }
239
240private:
241
242 struct file_entry {
243
244 file_info info;
245 std::vector<std::weak_ptr<basic_native_file<Lockable>>> opened_handles;
246
247 explicit file_entry(const file_info& info)
248 : info{info} { }
249
250 auto cleanup_opened_handles(const std::shared_ptr<file_base>& file_to_exclude = nullptr) -> void {
251 opened_handles.erase(std::remove_if(opened_handles.begin(), opened_handles.end(), [&](const auto& weak) {
252 return weak.expired() || weak.lock() == file_to_exclude;
253 }), opened_handles.end());
254 }
255
256 }; // struct file_entry
257
258 auto _build_file_list(const std::string& alias, const std::string& current_path) -> void {
259 for (const auto& entry : std::filesystem::directory_iterator(current_path)) {
260 if (entry.is_directory()) {
261 _build_file_list(alias, entry.path().string());
262 continue;
263 }
264
265 auto info = file_info{alias, _base_path, entry.path().string()};
266 _files.emplace(info.virtual_path(), file_entry{info});
267 }
268 }
269
270 mutable lockable_type _mutex;
271
272 std::string _alias_path;
273 std::string _base_path;
274 bool _is_initialized{false};
275
276 std::unordered_map<std::string, file_entry> _files;
277
278}; // class basic_native_filesystem
279
282
284
285} // namespace sbx::filesystem
286
287#endif // LIBSBX_FILESYSTEM_NATIVE_FILESYSTEM_HPP_
Definition: alias.hpp:13
Definition: native_filesystem.hpp:20
Definition: file_info.hpp:10
Definition: filesystem_base.hpp:14