2#ifndef LIBSBX_FILESYSTEM_NATIVE_FILESYSTEM_HPP_
3#define LIBSBX_FILESYSTEM_NATIVE_FILESYSTEM_HPP_
7#include <unordered_map>
12#include <libsbx/utility/lockable.hpp>
14#include <libsbx/filesystem/filesystem_base.hpp>
15#include <libsbx/filesystem/native_file.hpp>
17namespace sbx::filesystem {
19template<utility::lockable Lockable>
24 using lockable_type = Lockable;
27 : _alias_path{alias_path},
28 _base_path{base_path} { }
34 auto initialize() ->
bool override {
35 auto lock = std::scoped_lock{_mutex};
37 if (_is_initialized) {
41 if (!std::filesystem::exists(_base_path) || !std::filesystem::is_directory(_base_path)) {
45 _build_file_list(_alias_path, _base_path);
46 _is_initialized =
true;
51 auto shutdown() ->
void override {
52 auto lock = std::scoped_lock{_mutex};
57 _is_initialized =
false;
60 [[nodiscard]]
auto is_initialized()
const ->
bool override {
61 auto lock = std::scoped_lock{_mutex};
63 return _is_initialized;
66 [[nodiscard]]
auto base_path()
const ->
const std::string&
override {
67 auto lock = std::scoped_lock{_mutex};
72 [[nodiscard]]
auto virtual_path()
const ->
const std::string&
override {
73 auto lock = std::scoped_lock{_mutex};
78 [[nodiscard]]
auto is_read_only()
const ->
bool override {
79 auto lock = std::scoped_lock{_mutex};
81 if (!_is_initialized) {
85 auto permissions = std::filesystem::status(_base_path).permissions();
86 return (permissions & std::filesystem::perms::owner_write) == std::filesystem::perms::none;
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};
92 const auto request_write = file_base::mode_has_flag(mode, file_base::mode::write);
95 auto permissions = std::filesystem::status(_base_path).permissions();
97 if ((permissions & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
102 auto entry = _files.find(path);
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;
109 if (entry == _files.end()) {
113 auto& file_entry = entry->second;
114 auto file = std::make_shared<basic_native_file<lockable_type>>(file_entry.info);
116 if (!file || !file->open(mode)) {
120 file_entry.opened_handles.push_back(file);
125 auto close_file(
const file_ptr& file) ->
void override {
126 auto lock = std::scoped_lock{_mutex};
132 for (
auto& [path, entry] : _files) {
133 entry.cleanup_opened_handles(file);
137 auto remove_file(
const std::string& path) ->
bool override {
138 auto lock = std::scoped_lock{_mutex};
140 if (is_read_only()) {
144 const auto entry = _files.find(path);
146 if (entry == _files.end()) {
150 const auto native_path = entry->second.info.native_path();
154 return std::filesystem::remove(native_path);
157 [[nodiscard]]
auto exists(
const std::string& path)
const ->
bool override {
158 auto lock = std::scoped_lock{_mutex};
160 const auto entry = _files.find(path);
162 return entry != _files.end() && std::filesystem::exists(entry->second.info.native_path());
165 [[nodiscard]]
auto files()
const -> files_list
override {
166 auto lock = std::scoped_lock{_mutex};
168 auto list = files_list{};
169 list.reserve(_files.size());
171 for (
const auto& [path, entry] : _files) {
172 list.push_back(entry.info);
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);
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};
185 if (is_read_only()) {
189 const auto entry = _files.find(source);
191 if (entry == _files.end()) {
195 const auto& source_info = entry->second.info;
196 const auto destination_info =
file_info{_alias_path, _base_path, destination};
198 auto options = overwrite ? std::filesystem::copy_options::overwrite_existing : std::filesystem::copy_options::skip_existing;
200 auto ec = std::error_code{};
202 if (!std::filesystem::copy_file(source_info.native_path(), destination_info.native_path(), options, ec)) {
206 _files.insert_or_assign(destination, file_entry{destination_info});
211 auto rename_file(
const std::string& source,
const std::string& destination) ->
bool override {
212 auto lock = std::scoped_lock{_mutex};
214 if (is_read_only()) {
218 const auto entry = _files.find(source);
220 if (entry == _files.end()) {
224 const auto& source_info = entry->second.info;
225 const auto destination_info =
file_info{_alias_path, _base_path, destination};
227 auto ec = std::error_code{};
228 std::filesystem::rename(source_info.native_path(), destination_info.native_path(), ec);
235 _files.insert_or_assign(destination, file_entry{destination_info});
245 std::vector<std::weak_ptr<basic_native_file<Lockable>>> opened_handles;
247 explicit file_entry(
const file_info& info)
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());
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());
266 _files.emplace(info.virtual_path(), file_entry{info});
270 mutable lockable_type _mutex;
272 std::string _alias_path;
273 std::string _base_path;
274 bool _is_initialized{
false};
276 std::unordered_map<std::string, file_entry> _files;
Definition: native_filesystem.hpp:20
Definition: file_info.hpp:10
Definition: filesystem_base.hpp:14