archive_r_ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +56 -0
- data/README.md +103 -0
- data/ext/archive_r/archive_r_ext.cc +910 -0
- data/ext/archive_r/extconf.rb +90 -0
- data/ext/archive_r/vendor/archive_r/LICENSE.txt +56 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/data_stream.h +41 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry.h +161 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_fault.h +34 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_metadata.h +56 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/multi_volume_stream_base.h +46 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy.h +109 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy_utils.h +37 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/traverser.h +122 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.cc +330 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.h +98 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.cc +162 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.h +54 -0
- data/ext/archive_r/vendor/archive_r/src/archive_type.cc +552 -0
- data/ext/archive_r/vendor/archive_r/src/archive_type.h +76 -0
- data/ext/archive_r/vendor/archive_r/src/data_stream.cc +35 -0
- data/ext/archive_r/vendor/archive_r/src/entry.cc +253 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault.cc +26 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.cc +54 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.h +32 -0
- data/ext/archive_r/vendor/archive_r/src/entry_impl.h +58 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.cc +81 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.h +41 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_stream_base.cc +199 -0
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy.cc +151 -0
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy_utils.cc +304 -0
- data/ext/archive_r/vendor/archive_r/src/simple_profiler.h +120 -0
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.cc +263 -0
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.h +46 -0
- data/ext/archive_r/vendor/archive_r/src/traverser.cc +314 -0
- data/lib/archive_r.rb +80 -0
- metadata +112 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/data_stream.h"
|
|
5
|
+
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
namespace archive_r {
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
RootStreamFactory &factory_storage() {
|
|
14
|
+
static RootStreamFactory factory;
|
|
15
|
+
return factory;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
std::mutex &factory_mutex() {
|
|
19
|
+
static std::mutex mutex;
|
|
20
|
+
return mutex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
void set_root_stream_factory(RootStreamFactory factory) {
|
|
26
|
+
std::lock_guard<std::mutex> lock(factory_mutex());
|
|
27
|
+
factory_storage() = std::move(factory);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
RootStreamFactory get_root_stream_factory() {
|
|
31
|
+
std::lock_guard<std::mutex> lock(factory_mutex());
|
|
32
|
+
return factory_storage();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/entry.h"
|
|
5
|
+
#include "archive_r/path_hierarchy_utils.h"
|
|
6
|
+
#include "archive_stack_orchestrator.h"
|
|
7
|
+
#include "entry_fault_error.h"
|
|
8
|
+
#include "entry_impl.h"
|
|
9
|
+
#include "system_file_stream.h"
|
|
10
|
+
#include <archive.h>
|
|
11
|
+
#include <archive_entry.h>
|
|
12
|
+
#include <filesystem>
|
|
13
|
+
#include <sstream>
|
|
14
|
+
#include <sys/stat.h>
|
|
15
|
+
#include <system_error>
|
|
16
|
+
#include <unordered_set>
|
|
17
|
+
#include <utility>
|
|
18
|
+
|
|
19
|
+
namespace archive_r {
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Entry::Impl Implementation
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
// Unified constructor - receives metadata directly
|
|
26
|
+
// Copy constructor - cached orchestrator is NOT copied (will be recreated on first read)
|
|
27
|
+
Entry::Impl::Impl(const Impl &other)
|
|
28
|
+
: _path_hierarchy(other._path_hierarchy)
|
|
29
|
+
, _size(other._size)
|
|
30
|
+
, _filetype(other._filetype)
|
|
31
|
+
, _metadata(other._metadata)
|
|
32
|
+
, _descend_enabled(other._descend_enabled)
|
|
33
|
+
, _orchestrator(nullptr)
|
|
34
|
+
, _shares_traverser_orchestrator(false)
|
|
35
|
+
, _archive_options(other._archive_options) {
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Copy assignment operator
|
|
39
|
+
Entry::Impl &Entry::Impl::operator=(const Impl &other) {
|
|
40
|
+
if (this != &other) {
|
|
41
|
+
_path_hierarchy = other._path_hierarchy;
|
|
42
|
+
_size = other._size;
|
|
43
|
+
_filetype = other._filetype;
|
|
44
|
+
_metadata = other._metadata;
|
|
45
|
+
_descend_enabled = other._descend_enabled;
|
|
46
|
+
_orchestrator.reset();
|
|
47
|
+
_shares_traverser_orchestrator = false;
|
|
48
|
+
_archive_options = other._archive_options;
|
|
49
|
+
}
|
|
50
|
+
return *this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Entry::Impl::Impl(const PathHierarchy &hierarchy, std::shared_ptr<ArchiveStackOrchestrator> data_source_orchestrator, bool default_descent)
|
|
54
|
+
: _path_hierarchy(hierarchy)
|
|
55
|
+
, _size(0)
|
|
56
|
+
, _filetype(0)
|
|
57
|
+
, _descend_enabled(default_descent)
|
|
58
|
+
, _orchestrator(std::move(data_source_orchestrator))
|
|
59
|
+
, _shares_traverser_orchestrator(static_cast<bool>(_orchestrator)) {
|
|
60
|
+
if (!_orchestrator) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const auto &keys = _orchestrator->metadata_keys();
|
|
65
|
+
|
|
66
|
+
const StreamArchive *archive = _orchestrator->current_archive();
|
|
67
|
+
const bool use_archive_metadata = archive && _path_hierarchy.size() > 1;
|
|
68
|
+
|
|
69
|
+
if (use_archive_metadata) {
|
|
70
|
+
_size = archive->current_entry_size();
|
|
71
|
+
_filetype = archive->current_entry_filetype();
|
|
72
|
+
if (!keys.empty()) {
|
|
73
|
+
_metadata = archive->current_entry_metadata(keys);
|
|
74
|
+
}
|
|
75
|
+
} else if (!hierarchy.empty()) {
|
|
76
|
+
FilesystemMetadataInfo info = collect_root_path_metadata(hierarchy, keys);
|
|
77
|
+
_size = info.size;
|
|
78
|
+
_filetype = info.filetype;
|
|
79
|
+
_metadata = std::move(info.metadata);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (use_archive_metadata) {
|
|
83
|
+
_archive_options = _orchestrator->options();
|
|
84
|
+
} else {
|
|
85
|
+
_archive_options.reset();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (_filetype == 0 && archive && _orchestrator->depth() == _path_hierarchy.size()) {
|
|
89
|
+
_filetype = AE_IFREG;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
std::string Entry::Impl::name() const {
|
|
94
|
+
if (_path_hierarchy.empty()) {
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
const PathEntry &tail = _path_hierarchy.back();
|
|
98
|
+
std::string display_name;
|
|
99
|
+
if (entry_name_from_component(tail, display_name) && !display_name.empty()) {
|
|
100
|
+
return display_name;
|
|
101
|
+
}
|
|
102
|
+
if (tail.is_single()) {
|
|
103
|
+
return tail.single_value();
|
|
104
|
+
}
|
|
105
|
+
return path_entry_display(tail);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const PathHierarchy &Entry::Impl::path_hierarchy() const { return _path_hierarchy; }
|
|
109
|
+
|
|
110
|
+
bool Entry::Impl::is_directory() const { return _filetype == AE_IFDIR; }
|
|
111
|
+
|
|
112
|
+
bool Entry::Impl::is_file() const { return _filetype == AE_IFREG; }
|
|
113
|
+
|
|
114
|
+
uint64_t Entry::Impl::size() const { return _size; }
|
|
115
|
+
|
|
116
|
+
size_t Entry::Impl::depth() const {
|
|
117
|
+
// Depth is based on path hierarchy length
|
|
118
|
+
return _path_hierarchy.size() > 0 ? _path_hierarchy.size() - 1 : 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void Entry::Impl::set_descent(bool enabled) {
|
|
122
|
+
if (!_shares_traverser_orchestrator) {
|
|
123
|
+
emit_fault("set_descent requires traverser-managed orchestrator");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
_descend_enabled = enabled;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
bool Entry::Impl::descent_enabled() const { return _descend_enabled; }
|
|
130
|
+
|
|
131
|
+
const EntryMetadataMap &Entry::Impl::metadata() const { return _metadata; }
|
|
132
|
+
|
|
133
|
+
const EntryMetadataValue *Entry::Impl::metadata_value(const std::string &key) const {
|
|
134
|
+
const auto it = _metadata.find(key);
|
|
135
|
+
if (it == _metadata.end()) {
|
|
136
|
+
return nullptr;
|
|
137
|
+
}
|
|
138
|
+
return &it->second;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void Entry::Impl::set_multi_volume_group(const std::string &base_name, const MultiVolumeGroupOptions &options) {
|
|
142
|
+
if (!_shares_traverser_orchestrator) {
|
|
143
|
+
emit_fault("set_multi_volume_group requires traverser-managed orchestrator");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Notify traverser via ArchiveStackOrchestrator
|
|
148
|
+
_descend_enabled = false;
|
|
149
|
+
_orchestrator->mark_entry_as_multi_volume(_path_hierarchy, base_name, options.ordering);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void Entry::Impl::emit_fault(const std::string &message, int errno_value) const {
|
|
153
|
+
EntryFault fault;
|
|
154
|
+
fault.message = message;
|
|
155
|
+
fault.errno_value = errno_value;
|
|
156
|
+
fault.hierarchy = _path_hierarchy;
|
|
157
|
+
dispatch_registered_fault(fault);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
std::shared_ptr<ArchiveStackOrchestrator> Entry::Impl::ensure_orchestrator() {
|
|
161
|
+
if (_orchestrator) {
|
|
162
|
+
if (_shares_traverser_orchestrator && _orchestrator->depth() == 0) {
|
|
163
|
+
// Traverser-managed orchestrator never synchronized to this entry (depth 0 filesystem read).
|
|
164
|
+
_orchestrator.reset();
|
|
165
|
+
_shares_traverser_orchestrator = false;
|
|
166
|
+
} else {
|
|
167
|
+
return _orchestrator;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ArchiveOption opts = _archive_options.value_or(ArchiveOption{});
|
|
172
|
+
_orchestrator = std::make_shared<ArchiveStackOrchestrator>(opts);
|
|
173
|
+
if (!_orchestrator->synchronize_to_hierarchy(_path_hierarchy)) {
|
|
174
|
+
_orchestrator.reset();
|
|
175
|
+
return nullptr;
|
|
176
|
+
}
|
|
177
|
+
_shares_traverser_orchestrator = false;
|
|
178
|
+
return _orchestrator;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
ssize_t Entry::Impl::read(void *buffer, size_t length) {
|
|
182
|
+
if (!ensure_orchestrator()) {
|
|
183
|
+
emit_fault("Failed to initialize ArchiveStackOrchestrator");
|
|
184
|
+
return -1;
|
|
185
|
+
}
|
|
186
|
+
const ssize_t bytes_read = _orchestrator->read_head(buffer, length);
|
|
187
|
+
if (bytes_read < 0) {
|
|
188
|
+
emit_fault("Failed to read entry content");
|
|
189
|
+
return -1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_descend_enabled = false; // Require explicit re-enable before descending again
|
|
193
|
+
|
|
194
|
+
return bytes_read;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Entry Public API Implementation
|
|
199
|
+
// ============================================================================
|
|
200
|
+
|
|
201
|
+
Entry::Entry(const PathHierarchy &hierarchy, std::shared_ptr<ArchiveStackOrchestrator> data_source_orchestrator, bool default_descent)
|
|
202
|
+
: _impl(std::make_unique<Impl>(hierarchy, std::move(data_source_orchestrator), default_descent)) {}
|
|
203
|
+
|
|
204
|
+
Entry::Entry(Impl *impl)
|
|
205
|
+
: _impl(impl) {}
|
|
206
|
+
|
|
207
|
+
Entry::~Entry() = default;
|
|
208
|
+
|
|
209
|
+
std::unique_ptr<Entry> Entry::create(PathHierarchy hierarchy, std::shared_ptr<ArchiveStackOrchestrator> data_source_orchestrator, bool default_descent) {
|
|
210
|
+
return std::unique_ptr<Entry>(new Entry(hierarchy, std::move(data_source_orchestrator), default_descent));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Copy operations - creates a new Impl copy
|
|
214
|
+
Entry::Entry(const Entry &other)
|
|
215
|
+
: _impl(other._impl ? std::make_unique<Impl>(*other._impl) : nullptr) {}
|
|
216
|
+
|
|
217
|
+
Entry &Entry::operator=(const Entry &other) {
|
|
218
|
+
if (this != &other) {
|
|
219
|
+
_impl = other._impl ? std::make_unique<Impl>(*other._impl) : nullptr;
|
|
220
|
+
}
|
|
221
|
+
return *this;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Entry::Entry(Entry &&) noexcept = default;
|
|
225
|
+
Entry &Entry::operator=(Entry &&) noexcept = default;
|
|
226
|
+
|
|
227
|
+
std::string Entry::name() const { return _impl->name(); }
|
|
228
|
+
|
|
229
|
+
std::string Entry::path() const { return hierarchy_display(_impl->path_hierarchy()); }
|
|
230
|
+
|
|
231
|
+
const PathHierarchy &Entry::path_hierarchy() const { return _impl->path_hierarchy(); }
|
|
232
|
+
|
|
233
|
+
bool Entry::is_directory() const { return _impl->is_directory(); }
|
|
234
|
+
|
|
235
|
+
bool Entry::is_file() const { return _impl->is_file(); }
|
|
236
|
+
|
|
237
|
+
uint64_t Entry::size() const { return _impl->size(); }
|
|
238
|
+
|
|
239
|
+
size_t Entry::depth() const { return _impl->depth(); }
|
|
240
|
+
|
|
241
|
+
ssize_t Entry::read(void *buffer, size_t length) { return _impl->read(buffer, length); }
|
|
242
|
+
|
|
243
|
+
void Entry::set_descent(bool enabled) { _impl->set_descent(enabled); }
|
|
244
|
+
|
|
245
|
+
bool Entry::descent_enabled() const { return _impl->descent_enabled(); }
|
|
246
|
+
|
|
247
|
+
void Entry::set_multi_volume_group(const std::string &base_name, const MultiVolumeGroupOptions &options) { _impl->set_multi_volume_group(base_name, options); }
|
|
248
|
+
|
|
249
|
+
const EntryMetadataMap &Entry::metadata() const { return _impl->metadata(); }
|
|
250
|
+
|
|
251
|
+
const EntryMetadataValue *Entry::find_metadata(const std::string &key) const { return _impl->metadata_value(key); }
|
|
252
|
+
|
|
253
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/entry_fault.h"
|
|
5
|
+
|
|
6
|
+
#include <atomic>
|
|
7
|
+
#include <memory>
|
|
8
|
+
|
|
9
|
+
namespace archive_r {
|
|
10
|
+
namespace {
|
|
11
|
+
std::shared_ptr<const FaultCallback> g_fault_callback = std::make_shared<const FaultCallback>();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
void register_fault_callback(FaultCallback callback) {
|
|
15
|
+
auto new_callback = std::make_shared<const FaultCallback>(std::move(callback));
|
|
16
|
+
std::atomic_store_explicit(&g_fault_callback, std::move(new_callback), std::memory_order_release);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
void dispatch_registered_fault(const EntryFault &fault) {
|
|
20
|
+
auto callback = std::atomic_load_explicit(&g_fault_callback, std::memory_order_acquire);
|
|
21
|
+
if (callback && *callback) {
|
|
22
|
+
(*callback)(fault);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "entry_fault_error.h"
|
|
5
|
+
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <utility>
|
|
9
|
+
|
|
10
|
+
namespace archive_r {
|
|
11
|
+
|
|
12
|
+
EntryFaultError::EntryFaultError(EntryFault fault)
|
|
13
|
+
: std::runtime_error(fault.message)
|
|
14
|
+
, _fault(std::move(fault)) {}
|
|
15
|
+
|
|
16
|
+
EntryFaultError::EntryFaultError(EntryFault fault, const std::string &internal_message)
|
|
17
|
+
: std::runtime_error(internal_message.empty() ? fault.message : internal_message)
|
|
18
|
+
, _fault(std::move(fault)) {}
|
|
19
|
+
|
|
20
|
+
EntryFaultError make_entry_fault_error(const std::string &message, PathHierarchy hierarchy, int errno_value) {
|
|
21
|
+
EntryFault fault;
|
|
22
|
+
fault.hierarchy = std::move(hierarchy);
|
|
23
|
+
fault.message = message;
|
|
24
|
+
fault.errno_value = errno_value;
|
|
25
|
+
return EntryFaultError(std::move(fault));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
std::string format_errno_error(const std::string &prefix, int err) {
|
|
29
|
+
if (err == 0) {
|
|
30
|
+
return prefix;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
std::string message = prefix;
|
|
34
|
+
message.append(": ");
|
|
35
|
+
message.append(std::strerror(err));
|
|
36
|
+
message.append(" (posix errno=");
|
|
37
|
+
message.append(std::to_string(err));
|
|
38
|
+
message.push_back(')');
|
|
39
|
+
return message;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
std::string format_path_errno_error(const std::string &action, const std::string &path, int err) {
|
|
43
|
+
std::string prefix = action;
|
|
44
|
+
if (!path.empty()) {
|
|
45
|
+
prefix.append(" '");
|
|
46
|
+
prefix.append(path);
|
|
47
|
+
prefix.push_back('\'');
|
|
48
|
+
}
|
|
49
|
+
return format_errno_error(prefix, err);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
std::string prefer_error_detail(const std::string &detail, const std::string &fallback) { return detail.empty() ? fallback : detail; }
|
|
53
|
+
|
|
54
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include "archive_r/entry_fault.h"
|
|
7
|
+
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
#include <string>
|
|
10
|
+
|
|
11
|
+
namespace archive_r {
|
|
12
|
+
|
|
13
|
+
class EntryFaultError : public std::runtime_error {
|
|
14
|
+
public:
|
|
15
|
+
explicit EntryFaultError(EntryFault fault);
|
|
16
|
+
EntryFaultError(EntryFault fault, const std::string &internal_message);
|
|
17
|
+
|
|
18
|
+
const EntryFault &fault() const noexcept { return _fault; }
|
|
19
|
+
const PathHierarchy &hierarchy() const noexcept { return _fault.hierarchy; }
|
|
20
|
+
int errno_value() const noexcept { return _fault.errno_value; }
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
EntryFault _fault;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
EntryFaultError make_entry_fault_error(const std::string &message, PathHierarchy hierarchy = {}, int errno_value = 0);
|
|
27
|
+
|
|
28
|
+
std::string format_errno_error(const std::string &prefix, int err);
|
|
29
|
+
std::string format_path_errno_error(const std::string &action, const std::string &path, int err);
|
|
30
|
+
std::string prefer_error_detail(const std::string &detail, const std::string &fallback);
|
|
31
|
+
|
|
32
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include "archive_r/entry.h"
|
|
7
|
+
#include "archive_stack_orchestrator.h"
|
|
8
|
+
#include <filesystem>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <optional>
|
|
11
|
+
#include <sys/types.h> // for mode_t
|
|
12
|
+
#include <unordered_set>
|
|
13
|
+
|
|
14
|
+
namespace archive_r {
|
|
15
|
+
|
|
16
|
+
// Entry implementation class - internal to Traverser
|
|
17
|
+
class Entry::Impl {
|
|
18
|
+
public:
|
|
19
|
+
// Unified constructor - receives metadata directly
|
|
20
|
+
// No libarchive dependency
|
|
21
|
+
// Copy constructor and assignment (orchestrator is not copied)
|
|
22
|
+
Impl(const Impl &other);
|
|
23
|
+
Impl &operator=(const Impl &other);
|
|
24
|
+
|
|
25
|
+
Impl(const PathHierarchy &hierarchy, std::shared_ptr<ArchiveStackOrchestrator> data_source_orchestrator, bool default_descent);
|
|
26
|
+
|
|
27
|
+
std::string name() const;
|
|
28
|
+
const PathHierarchy &path_hierarchy() const;
|
|
29
|
+
bool is_directory() const;
|
|
30
|
+
bool is_file() const;
|
|
31
|
+
uint64_t size() const;
|
|
32
|
+
size_t depth() const;
|
|
33
|
+
void set_descent(bool enabled);
|
|
34
|
+
bool descent_enabled() const;
|
|
35
|
+
void set_multi_volume_group(const std::string &base_name, const MultiVolumeGroupOptions &options);
|
|
36
|
+
ssize_t read(void *buffer, size_t length);
|
|
37
|
+
const EntryMetadataMap &metadata() const;
|
|
38
|
+
const EntryMetadataValue *metadata_value(const std::string &key) const;
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
PathHierarchy _path_hierarchy;
|
|
42
|
+
|
|
43
|
+
// Metadata only (no libarchive types)
|
|
44
|
+
uint64_t _size;
|
|
45
|
+
mode_t _filetype;
|
|
46
|
+
EntryMetadataMap _metadata;
|
|
47
|
+
bool _descend_enabled = true; // Flag to control automatic descent
|
|
48
|
+
|
|
49
|
+
std::shared_ptr<ArchiveStackOrchestrator> _orchestrator; ///< Active orchestrator (shared with traverser or detached copy)
|
|
50
|
+
bool _shares_traverser_orchestrator = false;
|
|
51
|
+
|
|
52
|
+
mutable std::optional<ArchiveOption> _archive_options;
|
|
53
|
+
|
|
54
|
+
void emit_fault(const std::string &message, int errno_value = 0) const;
|
|
55
|
+
std::shared_ptr<ArchiveStackOrchestrator> ensure_orchestrator();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "multi_volume_manager.h"
|
|
5
|
+
|
|
6
|
+
#include "archive_r/path_hierarchy_utils.h"
|
|
7
|
+
|
|
8
|
+
#include <algorithm>
|
|
9
|
+
#include <utility>
|
|
10
|
+
|
|
11
|
+
namespace archive_r {
|
|
12
|
+
|
|
13
|
+
MultiVolumeManager::MultiVolumeGroup &MultiVolumeManager::get_or_create_multi_volume_group(const PathHierarchy &parent_hierarchy,
|
|
14
|
+
const std::string &base_name,
|
|
15
|
+
PathEntry::Parts::Ordering ordering) {
|
|
16
|
+
GroupList &groups = _multi_volume_groups[parent_hierarchy];
|
|
17
|
+
auto it = std::find_if(groups.begin(), groups.end(), [&](const MultiVolumeGroup &group) { return group.base_name == base_name; });
|
|
18
|
+
|
|
19
|
+
if (it == groups.end()) {
|
|
20
|
+
MultiVolumeGroup group;
|
|
21
|
+
group.base_name = base_name;
|
|
22
|
+
group.parent_hierarchy = parent_hierarchy;
|
|
23
|
+
group.ordering = ordering;
|
|
24
|
+
groups.push_back(std::move(group));
|
|
25
|
+
return groups.back();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
MultiVolumeGroup &target = *it;
|
|
29
|
+
if (ordering == PathEntry::Parts::Ordering::Given) {
|
|
30
|
+
target.ordering = PathEntry::Parts::Ordering::Given;
|
|
31
|
+
}
|
|
32
|
+
return target;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
bool MultiVolumeManager::pop_group_for_parent(const PathHierarchy &parent_hierarchy, MultiVolumeGroup &out_group) {
|
|
36
|
+
ParentGroupMap::iterator it = _multi_volume_groups.find(parent_hierarchy);
|
|
37
|
+
if (it == _multi_volume_groups.end() || it->second.empty()) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
out_group = it->second.front();
|
|
42
|
+
it->second.erase(it->second.begin());
|
|
43
|
+
if (it->second.empty()) {
|
|
44
|
+
_multi_volume_groups.erase(it);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void MultiVolumeManager::mark_entry_as_multi_volume(const PathHierarchy &entry_path, const std::string &base_name,
|
|
51
|
+
PathEntry::Parts::Ordering ordering) {
|
|
52
|
+
if (entry_path.empty() || pathhierarchy_is_multivolume(entry_path)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
MultiVolumeGroup &target = get_or_create_multi_volume_group(parent_hierarchy(entry_path), base_name, ordering);
|
|
57
|
+
auto &parts = target.parts;
|
|
58
|
+
const bool exists = std::any_of(parts.begin(), parts.end(), [&](const PathHierarchy &existing) {
|
|
59
|
+
return hierarchies_equal(existing, entry_path);
|
|
60
|
+
});
|
|
61
|
+
if (!exists) {
|
|
62
|
+
parts.push_back(entry_path);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
bool MultiVolumeManager::pop_multi_volume_group(const PathHierarchy ¤t_hierarchy, PathHierarchy &multi_volume_hierarchy) {
|
|
67
|
+
MultiVolumeGroup group;
|
|
68
|
+
if (!pop_group_for_parent(current_hierarchy, group)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (group.ordering != PathEntry::Parts::Ordering::Given) {
|
|
73
|
+
sort_hierarchies(group.parts);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
multi_volume_hierarchy = merge_multi_volume_sources(group.parts);
|
|
77
|
+
multi_volume_hierarchy.back().multi_volume_parts_mut().ordering = group.ordering;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include "archive_r/path_hierarchy.h"
|
|
7
|
+
#include "archive_type.h"
|
|
8
|
+
|
|
9
|
+
#include <map>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
namespace archive_r {
|
|
14
|
+
|
|
15
|
+
class MultiVolumeManager {
|
|
16
|
+
public:
|
|
17
|
+
struct MultiVolumeGroup {
|
|
18
|
+
std::vector<PathHierarchy> parts;
|
|
19
|
+
std::string base_name;
|
|
20
|
+
PathHierarchy parent_hierarchy;
|
|
21
|
+
PathEntry::Parts::Ordering ordering = PathEntry::Parts::Ordering::Natural;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
using GroupList = std::vector<MultiVolumeGroup>;
|
|
25
|
+
using ParentGroupMap = std::map<PathHierarchy, GroupList, PathHierarchyLess>;
|
|
26
|
+
|
|
27
|
+
void mark_entry_as_multi_volume(const PathHierarchy &entry_path, const std::string &base_name,
|
|
28
|
+
PathEntry::Parts::Ordering ordering = PathEntry::Parts::Ordering::Natural);
|
|
29
|
+
|
|
30
|
+
bool pop_multi_volume_group(const PathHierarchy ¤t_hierarchy, PathHierarchy &multi_volume_hierarchy);
|
|
31
|
+
|
|
32
|
+
private:
|
|
33
|
+
MultiVolumeGroup &get_or_create_multi_volume_group(const PathHierarchy &parent_hierarchy, const std::string &base_name,
|
|
34
|
+
PathEntry::Parts::Ordering ordering);
|
|
35
|
+
|
|
36
|
+
bool pop_group_for_parent(const PathHierarchy &parent_hierarchy, MultiVolumeGroup &out_group);
|
|
37
|
+
|
|
38
|
+
ParentGroupMap _multi_volume_groups;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
} // namespace archive_r
|