From c13fe63f105c7d0083020d305b947cd8c66bd4dc Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 21 Sep 2020 14:24:50 +0200
Subject: [PATCH] archive/iso9660: fix odd seeking bug (assertion failure)

Skip the beginning of a sector if the last seek was odd, and clear the
buffer on seek.
---
 src/archive/plugins/Iso9660ArchivePlugin.cxx | 26 +++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/archive/plugins/Iso9660ArchivePlugin.cxx b/src/archive/plugins/Iso9660ArchivePlugin.cxx
index 540da0e3f..0217faddc 100644
--- a/src/archive/plugins/Iso9660ArchivePlugin.cxx
+++ b/src/archive/plugins/Iso9660ArchivePlugin.cxx
@@ -184,10 +184,21 @@ class Iso9660InputStream final : public InputStream {
 			fill = nbytes;
 			position = 0;
 		}
+
+		void Clear() noexcept {
+			position = fill = 0;
+		}
 	};
 
 	BlockBuffer buffer;
 
+	/**
+	 * Skip this number of bytes of the first sector after filling
+	 * the buffer next time.  This is used for seeking into the
+	 * middle of a sector.
+	 */
+	size_t skip = 0;
+
 public:
 	Iso9660InputStream(std::shared_ptr<Iso9660> _iso,
 			   const char *_uri,
@@ -211,7 +222,9 @@ public:
 		if (new_offset > size)
 			throw std::runtime_error("Invalid seek offset");
 
-		offset = new_offset;
+		skip = new_offset % ISO_BLOCKSIZE;
+		offset = new_offset - skip;
+		buffer.Clear();
 	}
 };
 
@@ -277,9 +290,20 @@ Iso9660InputStream::Read(std::unique_lock<Mutex> &,
 		buffer.Append(nbytes);
 
 		r = buffer.Read();
+
+		if (skip > 0) {
+			if (skip >= r.size)
+				throw std::runtime_error("Premature end of ISO9660 track");
+
+			buffer.Consume(skip);
+			skip = 0;
+
+			r = buffer.Read();
+		}
 	}
 
 	assert(!r.empty());
+	assert(skip == 0);
 
 	size_t nbytes = std::min(read_size, r.size);
 	memcpy(ptr, r.data, nbytes);