Compare commits
4 Commits
13fffacd98
...
fce7698b44
Author | SHA1 | Date | |
---|---|---|---|
fce7698b44 | |||
f43314cb53 | |||
26182be695 | |||
be54ad3110 |
@ -382,6 +382,13 @@ export namespace coral {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the `apply` procedure if the optional is not empty, otherwise having no side-effects.
|
||||
*/
|
||||
void and_then(closure<void(element &)> const & apply) {
|
||||
if (this->has_value()) apply(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the optional contains a value, otherwise `false`.
|
||||
*/
|
||||
|
@ -145,41 +145,46 @@ export namespace coral {
|
||||
*/
|
||||
struct fs {
|
||||
/**
|
||||
* Descriptor of the various rules that the file-system enforces over access to its files.
|
||||
* Errors that may occur while trying to walk a file tree.
|
||||
*
|
||||
* [walk_error::end_of_walk] reports that there are no more paths left in the file tree that to traverse.
|
||||
*
|
||||
* [walk_error::io_unavailable] indicates that an implementation-defined I/O error has occured during traversal
|
||||
* of the file tree and failed to recover the next file tree path as a result.
|
||||
*/
|
||||
struct access_rules {
|
||||
bool can_read;
|
||||
|
||||
bool can_write;
|
||||
enum class walk_error {
|
||||
end_of_walk,
|
||||
io_unavailable,
|
||||
};
|
||||
|
||||
enum class [[nodiscard]] walk_result {
|
||||
ok,
|
||||
not_implemented,
|
||||
access_denied,
|
||||
not_found,
|
||||
io_error,
|
||||
};
|
||||
/**
|
||||
* Supplier of file paths from a file tree walking context created by calling [walk_files].
|
||||
*
|
||||
* Each subsequent invocation will produce either a valid path in the file tree from `target_path` or a
|
||||
* [walk_error], with [walk_error::end_of_walk] signalling that there are no more paths left in the file tree to
|
||||
* traverse. See [walk_error] for more details on potential errors.
|
||||
*/
|
||||
using walker = closure<expected<path, walk_error>()>;
|
||||
|
||||
virtual ~fs() {};
|
||||
|
||||
virtual walk_result walk_files(path const & target_path, closure<bool(path const &)> const & apply) {
|
||||
return walk_result::not_implemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the file-system for its global [access_rules], returning them.
|
||||
*/
|
||||
virtual access_rules query_access() = 0;
|
||||
|
||||
/**
|
||||
* Attempts to read the file in `file_path`, calling `then` if it was successfully opened for reading.
|
||||
* Attempts to read the file in `target_path`, calling `then` if it was successfully opened for reading and
|
||||
* passing the [file_reader] context along.
|
||||
*/
|
||||
virtual void read_file(path const & target_path, closure<void(file_reader &)> const & then) {}
|
||||
|
||||
/**
|
||||
* Attempts to write the file in the file system located at `file_path`, calling `then` if it was successfully
|
||||
* opened for writing.
|
||||
* Attempts to walk the file tree from `target_path`, calling `then` if it was successfully opened for walking
|
||||
* and passing the [walker] context along.
|
||||
*
|
||||
* See [walker] for more information on how to use it to traverse the file tree.
|
||||
*/
|
||||
virtual void walk_files(path const & target_path, closure<void(walker const &)> const & then) {}
|
||||
|
||||
/**
|
||||
* Attempts to write the file in the file system located at `target_path`, calling `then` if it was successfully
|
||||
* opened for writing and passing the [file_writer] context along.
|
||||
*/
|
||||
virtual void write_file(path const & target_path, closure<void(file_writer &)> const & then) {}
|
||||
};
|
||||
|
106
source/oar.cpp
106
source/oar.cpp
@ -220,69 +220,42 @@ export namespace oar {
|
||||
this->archive_path = archive_path;
|
||||
}
|
||||
|
||||
walk_result walk_files(path const & target_path, closure<bool(path const &)> const & apply) override {
|
||||
bool not_found {false};
|
||||
bool has_io_error {false};
|
||||
|
||||
/**
|
||||
* See [fs::walk_files].
|
||||
*/
|
||||
void walk_files(path const & target_path, closure<void(walker const &)> const & then) override {
|
||||
this->backing_fs->read_file(this->archive_path, [&](file_reader & archive_reader) {
|
||||
entry archive_entry{&archive_reader};
|
||||
|
||||
if (archive_entry.find(entry_kind::directory, target_path) != entry::find_result::ok) {
|
||||
not_found = true;
|
||||
if (archive_entry.find(entry_kind::directory, target_path) != entry::find_result::ok) return;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
then([&]() -> expected<path, walk_error> {
|
||||
constexpr usize path_size {sizeof(path)};
|
||||
u8 path_buffer[path_size] {0};
|
||||
expected const data_read {archive_entry.read(path_buffer)};
|
||||
|
||||
if (data_read.is_error()) {
|
||||
has_io_error = true;
|
||||
// Read verify integrity.
|
||||
{
|
||||
expected const data_read {archive_entry.read(path_buffer)};
|
||||
|
||||
return;
|
||||
}
|
||||
if (data_read.is_error()) return walk_error::io_unavailable;
|
||||
|
||||
if (usize const data_read_value {*data_read.ok()}; data_read_value != path_size) {
|
||||
if (data_read_value != 0) has_io_error = true;
|
||||
|
||||
return;
|
||||
switch (*data_read.ok()) {
|
||||
case path_size: break;
|
||||
case 0: return walk_error::end_of_walk;
|
||||
default: return walk_error::io_unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify existence of zero terminator in path.
|
||||
if (!coral::find_last(path_buffer, 0).has_value()) {
|
||||
has_io_error = true;
|
||||
if (!coral::find_last(path_buffer, 0).has_value()) return walk_error::io_unavailable;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (archive_entry.read(path_buffer).map<bool>(coral::equality_predicate(path_size)).ok_or(false))
|
||||
|
||||
if (!apply(*reinterpret_cast<path const *>(path_buffer))) return;
|
||||
}
|
||||
return {*reinterpret_cast<path const *>(path_buffer)};
|
||||
});
|
||||
});
|
||||
|
||||
if (not_found) return walk_result::not_found;
|
||||
|
||||
if (has_io_error) return walk_result::io_error;
|
||||
|
||||
return walk_result::ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the archive for the [fs::access_rules] and returns them.
|
||||
*/
|
||||
access_rules query_access() override {
|
||||
return {
|
||||
.can_read = true,
|
||||
.can_write = false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to open a readable context for reading from the archive file identified by `file_path`, doing
|
||||
* nothing if the requested file could not be found.
|
||||
* See [fs::read_file].
|
||||
*/
|
||||
void read_file(path const & file_path, closure<void(file_reader &)> const & then) override {
|
||||
if ((this->backing_fs == nullptr) || (this->archive_path.byte_size() == 0)) return;
|
||||
@ -317,17 +290,43 @@ export namespace oar {
|
||||
|
||||
// Walk input dir to create blocks for all files needed.
|
||||
{
|
||||
bool has_memory {true};
|
||||
bool has_io_error {false};
|
||||
bool is_out_of_memory {false};
|
||||
|
||||
if (input_fs.walk_files(input_path, [&](path const & entry_path) -> bool {
|
||||
has_memory = archive_blocks.push({.layout = {.path = entry_path}}) == coral::push_result::ok;
|
||||
input_fs.walk_files(input_path, [&](fs::walker const & walk) {
|
||||
coral::expected walked_path {walk()};
|
||||
|
||||
return !has_memory;
|
||||
while (walked_path.is_ok()) {
|
||||
is_out_of_memory = archive_blocks.push({.layout = {
|
||||
.path = *walked_path.ok()
|
||||
}}) != coral::push_result::ok;
|
||||
|
||||
file_count += 1;
|
||||
}) != fs::walk_result::ok) return bundle_result::io_error;
|
||||
if (is_out_of_memory) return;
|
||||
|
||||
if (!has_memory) return bundle_result::out_of_memory;
|
||||
file_count += 1;
|
||||
walked_path = walk();
|
||||
}
|
||||
|
||||
walked_path.error().and_then([&](fs::walk_error walk_error) {
|
||||
switch (walk_error) {
|
||||
case fs::walk_error::io_unavailable: {
|
||||
has_io_error = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case fs::walk_error::end_of_walk: {
|
||||
has_io_error = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (has_io_error) return bundle_result::io_error;
|
||||
|
||||
if (is_out_of_memory) return bundle_result::out_of_memory;
|
||||
|
||||
if (file_count > coral::u32_max) return bundle_result::too_many_files;
|
||||
}
|
||||
@ -341,6 +340,7 @@ export namespace oar {
|
||||
|
||||
coral::copy(archive_header.layout.signature, signature_magic);
|
||||
|
||||
// This was safety-checked during the initial file tree walk step.
|
||||
archive_header.layout.entry_count = static_cast<coral::u32>(file_count);
|
||||
|
||||
if (!archive_writer.write(archive_header.bytes).map<bool>(header::is_sizeof).ok_or(false)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user