From c4dc6e1fb6f9a6b18be3b4bf7c400c0b3ef980b3 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 25 Jun 2025 14:48:15 +0200
Subject: [PATCH v5 4/6] pg_dump compression API: close_func

close_func is defined as returning true on success and false on error
leaving errorhandling to the caller.

 * zstd: Ensure to close the FD even if closing down the (de)compressor
   fails, and clean up state allocation on fclose failures.  Make sure
   to capture errors set by fclose.
 * lz4: Ensure to close the FD even if closing down the (de)compressor
   fails and don't call pg_fatal but instead log the failures using
   pg_log_error. Make sure to capture errors set by fclose.
 * none: Make sure to catch errors set by fclose.
---
 src/bin/pg_dump/compress_io.c   |  1 +
 src/bin/pg_dump/compress_lz4.c  | 36 ++++++++++++++++++++++++---------
 src/bin/pg_dump/compress_none.c |  5 +++++
 src/bin/pg_dump/compress_zstd.c | 17 ++++++++++++----
 4 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index cf5358de741..9cadc6f2a3f 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -290,6 +290,7 @@ EndCompressFileHandle(CompressFileHandle *CFH)
 {
 	bool		ret = false;
 
+	errno = 0;
 	if (CFH->private_data)
 		ret = CFH->close_func(CFH);
 
diff --git a/src/bin/pg_dump/compress_lz4.c b/src/bin/pg_dump/compress_lz4.c
index 2e1a3c33386..a34c1119f22 100644
--- a/src/bin/pg_dump/compress_lz4.c
+++ b/src/bin/pg_dump/compress_lz4.c
@@ -661,6 +661,7 @@ LZ4Stream_close(CompressFileHandle *CFH)
 	FILE	   *fp;
 	LZ4State   *state = (LZ4State *) CFH->private_data;
 	size_t		status;
+	int			ret;
 
 	fp = state->fp;
 	if (state->inited)
@@ -669,25 +670,31 @@ LZ4Stream_close(CompressFileHandle *CFH)
 		{
 			status = LZ4F_compressEnd(state->ctx, state->buffer, state->buflen, NULL);
 			if (LZ4F_isError(status))
-				pg_fatal("could not end compression: %s",
-						 LZ4F_getErrorName(status));
-			else if (fwrite(state->buffer, 1, status, state->fp) != status)
 			{
-				errno = (errno) ? errno : ENOSPC;
-				WRITE_ERROR_EXIT;
+				pg_log_error("could not end compression: %s",
+							 LZ4F_getErrorName(status));
+			}
+			else
+			{
+				errno = 0;
+				if (fwrite(state->buffer, 1, status, state->fp) != status)
+				{
+					errno = (errno) ? errno : ENOSPC;
+					pg_log_error("could not write to output file: %m");
+				}
 			}
 
 			status = LZ4F_freeCompressionContext(state->ctx);
 			if (LZ4F_isError(status))
-				pg_fatal("could not end compression: %s",
-						 LZ4F_getErrorName(status));
+				pg_log_error("could not end compression: %s",
+							 LZ4F_getErrorName(status));
 		}
 		else
 		{
 			status = LZ4F_freeDecompressionContext(state->dtx);
 			if (LZ4F_isError(status))
-				pg_fatal("could not end decompression: %s",
-						 LZ4F_getErrorName(status));
+				pg_log_error("could not end decompression: %s",
+							 LZ4F_getErrorName(status));
 			pg_free(state->overflowbuf);
 		}
 
@@ -695,8 +702,17 @@ LZ4Stream_close(CompressFileHandle *CFH)
 	}
 
 	pg_free(state);
+	CFH->private_data = NULL;
+
+	errno = 0;
+	ret = fclose(fp);
+	if (ret != 0)
+	{
+		pg_log_error("could not close file: %m");
+		return false;
+	}
 
-	return fclose(fp) == 0;
+	return true;
 }
 
 static bool
diff --git a/src/bin/pg_dump/compress_none.c b/src/bin/pg_dump/compress_none.c
index 7c456136096..452151fb9a2 100644
--- a/src/bin/pg_dump/compress_none.c
+++ b/src/bin/pg_dump/compress_none.c
@@ -146,7 +146,12 @@ close_none(CompressFileHandle *CFH)
 	CFH->private_data = NULL;
 
 	if (fp)
+	{
+		errno = 0;
 		ret = fclose(fp);
+		if (ret != 0)
+			pg_log_error("could not close file: %m");
+	}
 
 	return ret == 0;
 }
diff --git a/src/bin/pg_dump/compress_zstd.c b/src/bin/pg_dump/compress_zstd.c
index 1a8fcd4d508..c0ee5654cb2 100644
--- a/src/bin/pg_dump/compress_zstd.c
+++ b/src/bin/pg_dump/compress_zstd.c
@@ -415,6 +415,7 @@ static bool
 Zstd_close(CompressFileHandle *CFH)
 {
 	ZstdCompressorState *zstdcs = (ZstdCompressorState *) CFH->private_data;
+	bool		success = true;
 
 	if (zstdcs->cstream)
 	{
@@ -431,14 +432,17 @@ Zstd_close(CompressFileHandle *CFH)
 			if (ZSTD_isError(res))
 			{
 				zstdcs->zstderror = ZSTD_getErrorName(res);
-				return false;
+				success = false;
+				break;
 			}
 
+			errno = 0;
 			cnt = fwrite(output->dst, 1, output->pos, zstdcs->fp);
 			if (cnt != output->pos)
 			{
 				zstdcs->zstderror = strerror(errno);
-				return false;
+				success = false;
+				break;
 			}
 
 			if (res == 0)
@@ -455,11 +459,16 @@ Zstd_close(CompressFileHandle *CFH)
 		pg_free(unconstify(void *, zstdcs->input.src));
 	}
 
+	errno = 0;
 	if (fclose(zstdcs->fp) != 0)
-		return false;
+	{
+		zstdcs->zstderror = strerror(errno);
+		success = false;
+	}
 
 	pg_free(zstdcs);
-	return true;
+	CFH->private_data = NULL;
+	return success;
 }
 
 static bool
-- 
2.39.3 (Apple Git-146)

