diff options
author | Max Kellermann <max@musicpd.org> | 2020-09-07 21:15:53 +0200 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2020-09-07 21:15:53 +0200 |
commit | e8380cf2aa9ff7517d5ea3ada33a40df09d8542c (patch) | |
tree | 05e90c5cbd73e2b5bb0f98b330a7d931eb50cd2f /src/archive | |
parent | 291be84704e9369f2cfc895c02bcceeace7b95d3 (diff) | |
parent | b2ae5298a765f83dd142564adfdcaf5b2d34ff52 (diff) |
Merge branch 'v0.21.x' into master
Diffstat (limited to 'src/archive')
-rw-r--r-- | src/archive/plugins/Iso9660ArchivePlugin.cxx | 108 |
1 files changed, 88 insertions, 20 deletions
diff --git a/src/archive/plugins/Iso9660ArchivePlugin.cxx b/src/archive/plugins/Iso9660ArchivePlugin.cxx index 78b17d5b1..1976390a7 100644 --- a/src/archive/plugins/Iso9660ArchivePlugin.cxx +++ b/src/archive/plugins/Iso9660ArchivePlugin.cxx @@ -29,9 +29,12 @@ #include "fs/Path.hxx" #include "util/RuntimeError.hxx" #include "util/StringCompare.hxx" +#include "util/WritableBuffer.hxx" #include <cdio/iso9660.h> +#include <array> + #include <stdlib.h> #include <string.h> @@ -150,6 +153,47 @@ class Iso9660InputStream final : public InputStream { const lsn_t lsn; + /** + * libiso9660 can only read whole sectors at a time, and this + * buffer is used to store one whole sector and allow Read() + * to handle partial sector reads. + */ + class BlockBuffer { + size_t position = 0, fill = 0; + + std::array<uint8_t, ISO_BLOCKSIZE> data; + + public: + ConstBuffer<uint8_t> Read() const noexcept { + assert(fill <= data.size()); + assert(position <= fill); + + return {&data[position], &data[fill]}; + } + + void Consume(size_t nbytes) noexcept { + assert(nbytes <= Read().size); + + position += nbytes; + } + + WritableBuffer<uint8_t> Write() noexcept { + assert(Read().empty()); + + return {data.data(), data.size()}; + } + + void Append(size_t nbytes) noexcept { + assert(Read().empty()); + assert(nbytes <= data.size()); + + fill = nbytes; + position = 0; + } + }; + + BlockBuffer buffer; + public: Iso9660InputStream(std::shared_ptr<Iso9660> _iso, const char *_uri, @@ -170,6 +214,9 @@ public: void *ptr, size_t size) override; void Seek(std::unique_lock<Mutex> &, offset_type new_offset) override { + if (new_offset > size) + throw std::runtime_error("Invalid seek offset"); + offset = new_offset; } }; @@ -195,35 +242,56 @@ size_t Iso9660InputStream::Read(std::unique_lock<Mutex> &, void *ptr, size_t read_size) { - const ScopeUnlock unlock(mutex); + const offset_type remaining = size - offset; + if (remaining == 0) + return 0; - int readed = 0; - int no_blocks, cur_block; - size_t left_bytes = size - offset; + if (offset_type(read_size) > remaining) + read_size = remaining; - if (left_bytes < read_size) { - no_blocks = CEILING(left_bytes, ISO_BLOCKSIZE); - } else { - no_blocks = read_size / ISO_BLOCKSIZE; - } + auto r = buffer.Read(); - if (no_blocks == 0) - return 0; + if (r.empty()) { + /* the buffer is empty - read more data from the ISO file */ + + assert(offset % ISO_BLOCKSIZE == 0); - cur_block = offset / ISO_BLOCKSIZE; + const ScopeUnlock unlock(mutex); - readed = iso->SeekRead(ptr, lsn + cur_block, no_blocks); + const lsn_t read_lsn = lsn + offset / ISO_BLOCKSIZE; - if (readed != no_blocks * ISO_BLOCKSIZE) - throw FormatRuntimeError("error reading ISO file at lsn %lu", - (unsigned long)cur_block); + if (read_size >= ISO_BLOCKSIZE) { + /* big read - read right into the caller's buffer */ - if (left_bytes < read_size) { - readed = left_bytes; + auto nbytes = iso->SeekRead(ptr, read_lsn, + read_size / ISO_BLOCKSIZE); + if (nbytes <= 0) + throw std::runtime_error("Failed to read ISO9660 file"); + + offset += nbytes; + return nbytes; + } + + /* fill the buffer */ + + auto w = buffer.Write(); + auto nbytes = iso->SeekRead(w.data, read_lsn, + w.size / ISO_BLOCKSIZE); + if (nbytes <= 0) + throw std::runtime_error("Failed to read ISO9660 file"); + + buffer.Append(nbytes); + + r = buffer.Read(); } - offset += readed; - return readed; + assert(!r.empty()); + + size_t nbytes = std::min(read_size, r.size); + memcpy(ptr, r.data, nbytes); + buffer.Consume(nbytes); + offset += nbytes; + return nbytes; } bool |