[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: