[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH 20/15] libdpkg: compression: check for output errors closing files



A gzclose call has the same potential for errors as a write,
since the compressor needs to flush its buffers before closing
its output file.  The same applies to BZ2_bzclose, but
unfortunately libbz2's gzio-style API does not expose the error
code.  Luckily, the only possible errors are I/O errors, which
can be detected through errno.

Similarly, explicitly closing a file descriptor can reveal errors
writing out buffered data.

Closing input handles, on the other hand, would be a waste of
time: all it would accomplish is to free some resources held by a
process that is about to exit anyway.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 lib/dpkg/compression-backend.c |   56 +++++++++++++++++++++++++++++++++++-----
 1 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/lib/dpkg/compression-backend.c b/lib/dpkg/compression-backend.c
index 112b817..7a1e664 100644
--- a/lib/dpkg/compression-backend.c
+++ b/lib/dpkg/compression-backend.c
@@ -119,17 +119,22 @@ default_memlimit()
 			ohshite(_("%s: internal " format " error: %s"), \
 			        desc, "write"); \
 	} \
+	\
+	if (close(fd_out)) \
+		ohshite(_("%s: internal " format " error: %s"), \
+			desc, "close"); \
 	exit(0); \
 } while(0)
 
-#define COMPRESS(format, zFile, zdopen, zwrite, zclose, zerror, ERR_ERRNO, \
+#define COMPRESS(format, zFile, zdopen, zwrite, zclose, \
+		zcloseerror, zerror, ERR_ERRNO, \
 		fd_in, fd_out, compression, desc) do \
 { \
 	/* If compression == '\0', use library default. */ \
 	char combuf[] = {'w', compression, '\0'}; \
-	int actualread, actualwrite; \
 	char buffer[DPKG_BUFFER_SIZE]; \
 	zFile zfile; \
+	int actualread, actualwrite, err = 0; \
 	\
 	zfile = zdopen(fd_out, combuf); \
 	while ((actualread = read(fd_in, buffer, sizeof(buffer)))) { \
@@ -139,7 +144,6 @@ default_memlimit()
 		\
 		actualwrite = zwrite(zfile, buffer, actualread); \
 		if (actualwrite != actualread) { \
-			int err = 0; \
 			const char *errmsg = zerror(zfile, &err); \
 			\
 			if (err == ERR_ERRNO) \
@@ -148,7 +152,15 @@ default_memlimit()
 			        desc, "write", errmsg); \
 		} \
 	} \
-	zclose(zfile); \
+	\
+	err = zclose(zfile); \
+	if (err) { \
+		const char *errmsg = err == ERR_ERRNO ? \
+			strerror(errno) : zcloseerror(err); \
+		\
+		ohshit(_("%s: internal " format " error: %s: %s"), \
+			desc, "close", errmsg); \
+	} \
 	exit(0); \
 } while(0)
 
@@ -174,7 +186,8 @@ compress_gzip(int fd_in, int fd_out, char compression, const char *desc)
 	if (compression == '\0')
 		compression = default_gz_compression;
 
-	COMPRESS("gzip", gzFile, gzdopen, gzwrite, gzclose, gzerror, Z_ERRNO,
+	COMPRESS("gzip", gzFile, gzdopen, gzwrite, gzclose,
+		zError, gzerror, Z_ERRNO,
 		fd_in, fd_out, compression, desc);
 }
 #else /* !WITH_ZLIB */
@@ -195,6 +208,35 @@ compress_gzip(int fd_in, int fd_out, char compression, const char *desc)
 #endif
 
 #ifdef WITH_BZ2
+/*
+ * BZ2_bzclose does not pass on the error code to the caller.
+ * To work around this, we could
+ *  - set errno to 0 and compare the value afterwards, hoping
+ *    that libbz2 did not recover from any errors itself
+ *  or
+ *  - modify a copy of BZ2_bzclose to expose the error
+ * This function follows the former course, so it can continue
+ * to work even if the layout of BZFILE objects should change.
+ */
+static int
+bzclose(BZFILE *b)
+{
+	errno = 0;
+	bzclose(b);
+	if (errno)
+		return BZ_IO_ERROR;
+
+	return 0;
+}
+
+static const char *bzcloseerror(int err) DPKG_ATTR_CONST;
+static const char *
+bzcloseerror(int err)
+{
+	/* bzclose only returns BZ_OK and BZ_IO_ERROR. */
+	return _("Unexpected error (bug)");
+}
+
 void
 decompress_bzip2(int fd_in, int fd_out, const char *desc)
 {
@@ -209,8 +251,8 @@ compress_bzip2(int fd_in, int fd_out, char compression, const char *desc)
 	if (compression == '\0')
 		compression = default_bz2_compression;
 
-	COMPRESS("bzip2", BZFILE *, BZ2_bzdopen, BZ2_bzwrite, BZ2_bzclose,
-		BZ2_bzerror, BZ_IO_ERROR,
+	COMPRESS("bzip2", BZFILE *, BZ2_bzdopen, BZ2_bzwrite, bzclose,
+		bzcloseerror, BZ2_bzerror, BZ_IO_ERROR,
 		fd_in, fd_out, compression, desc);
 }
 #else /* !WITH_BZ2 */
-- 
1.6.5.rc1.199.g596ec


Reply to: