From 8b4ca868d1f566d1b19ecbbb2766d0031a6cc4a9 Mon Sep 17 00:00:00 2001
From: Derek Buitenhuis <derek.buitenhuis@gmail.com>
Date: Mon, 29 Apr 2024 20:26:17 +0100
Subject: [PATCH] filehandle: Add missing avio_read error check

Rather than return an error when reading fails part way though,
avio_read returns those bytes and fails on the *next* read. This
can cause weird stuff to happen, like calculating wrong file
signatures due to partial reads, when used over a network.

To figure out if there was a partial read due to an error, we
need to check avio_feof, which for some reason not only checks
for EOF but also for all read/write errors, by its own docs.
This only tells us *if* there was an error, or EOF, though, so
we must also check the contents of avio->error after that.

Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
---
 src/core/filehandle.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/core/filehandle.cpp b/src/core/filehandle.cpp
index 7c7dcd5507..a909f6357e 100644
--- a/src/core/filehandle.cpp
+++ b/src/core/filehandle.cpp
@@ -73,9 +73,16 @@ int64_t FileHandle::Tell() {
 
 size_t FileHandle::Read(char *buffer, size_t size) {
     int count = avio_read(avio, (unsigned char *)buffer, size);
-    if (count < 0)
+    if (count < 0) {
         throw FFMS_Exception(error_source, FFMS_ERROR_FILE_READ,
             "Failed to read from '" + filename + "': " + AVErrorToString(count));
+    } else if (avio_feof(avio)) {
+        // "Similar to feof() but also returns nonzero on read errors" -- FFmpeg docs
+        if (avio->error != 0 && avio->error != AVERROR_EOF) {
+            throw FFMS_Exception(error_source, FFMS_ERROR_FILE_READ,
+                "Failed to read from '" + filename + "': " + AVErrorToString(avio->error));
+        }
+    }
     return (size_t)count;
 }