* Gordon's patch (bug #874605)
* - Error-handling improvements - mainly for multi-archive
* scenarios (bug #883782)
+ * - Fixed occasional failure on decompress with --ignore-trailing-garbage=1
+ * with multiple bad blocks in the archive (bug #886625)
+ * - Fixed refusal to write to stdout on -dc from stdin (bug #886628)
* David James - provided patch to fix deadlock due to unsynchronized broadcast (bug #876686)
* Gordon - provided patch for improving I/O error messages (bug #874605)
*
*
* Jeff Gilchrist, Ottawa, Canada.
* pbzip2@compression.ca
- * pbzip2 version 1.1.6 of Oct 30, 2011
+ * pbzip2 version 1.1.7 of Dec 11, 2011
*
*/
#include "pbzip2.h"
static size_t NumBufferedBlocksMax = 0;
static int NextBlockToWrite;
static int LastGoodBlock; // set only to terminate write prematurely (ignoring garbage)
+static int MinErrorBlock; // lowest so far block number which has errors (on decompress; could be trailing garbage)
static size_t OutBufferPosToWrite; // = 0; // position in output buffer
static int Verbosity = 0;
static int QuietMode = 1;
inline int syncGetTerminateFlag();
inline void syncSetTerminateFlag(int newValue);
inline void syncSetFinishedFlag(int newValue);
-inline void syncSetLastGoodBlock(int newValue);
+inline void syncSetLastGoodBlock(int newValue, int errBlock);
inline int syncGetLastGoodBlock();
void cleanupUnfinishedWork();
void cleanupAndQuit(int exitCode);
int detectCPUs(void);
inline bool isIgnoredTrailingGarbage();
-int waitForPreviousBlock(int blockNum);
+int waitForPreviousBlock(int blockNumToWait, int errBlockNumber);
inline int getLastGoodBlockBeforeErr(int errBlockNumber, int outSequenceNumber);
inline int issueDecompressError(int bzret, const outBuff * fileData,
int outSequenceNumber, const bz_stream & strm, const char * errmsg,
bool isTrailingGarbageErr);
int decompressErrCheck(int bzret, const outBuff * fileData,
int outSequenceNumber, const bz_stream & strm);
+inline bool hasTrailingGarbage(int bzret, const outBuff * fileData,
+ const bz_stream & strm);
int producerDecompressCheckInterrupt(int hInfile, outBuff *& fileData, int lastBlock);
using pbzip2::ErrorContext;
safe_mutex_unlock(&TerminateFlagMutex);
}
-inline void syncSetLastGoodBlock(int newValue)
+/**
+ * Set last block which is maybe good (not guaranteed) and MinErrorBlock
+ * (lowest block with errors encountered so far).
+ * Only moving downwards has effect (attempts to raise the block numbers are ignored).
+ * -1 means infinity.
+ *
+ *
+ * @param newValue last block which is maybe good. -1 means +infinity.
+ * @param errBlock block number which has errors
+ */
+inline void syncSetLastGoodBlock(int newValue, int errBlock)
{
+ bool changed = false;
+
safe_mutex_lock(OutMutex);
#ifdef PBZIP_DEBUG
unsigned long long thid = (unsigned long long) pthread_self();
- fprintf(stderr, "(%"PRIu64") syncSetLastGoodBlock: %d -> %d\n", thid, LastGoodBlock, newValue );
+ fprintf(stderr, "(%"PRIu64") syncSetLastGoodBlock: %d -> %d; MinErrorBlock: %d -> %d\n",
+ thid, LastGoodBlock, newValue, MinErrorBlock, errBlock);
#endif
if ( (LastGoodBlock == -1) || (newValue < LastGoodBlock) )
{
LastGoodBlock = newValue;
+ changed = true;
+ }
+
+ if ( (MinErrorBlock == -1) || (errBlock < MinErrorBlock) )
+ {
+ MinErrorBlock = errBlock;
+ changed = true;
+ }
+ if ( changed )
+ {
safe_cond_signal(&ErrStateChangeCond);
safe_cond_signal(&OutBufferHeadNotEmpty);
return ret;
}
+inline int syncGetMinErrorBlock()
+{
+ int ret;
+ safe_mutex_lock(OutMutex);
+ ret = MinErrorBlock;
+ safe_mutex_unlock(OutMutex);
+
+ return ret;
+}
+
inline bool isIgnoredTrailingGarbage()
{
return (IgnoreTrailingGarbageFlag != 0);
/**
*
* @return -1 - terminate flag set (error)
- * 0 - prev block is OK
+ * 0 - prev block is OK (i.e. we're on the first error here)
* 2 - lower block number already in error state
*/
-int waitForPreviousBlock(int blockNum)
+int waitForPreviousBlock(int blockNumToWait, int errBlockNumber)
{
#ifdef PBZIP_DEBUG
unsigned long long thid = (unsigned long long) pthread_self();
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock before check: LastGoodBlock=%d; blockNum=%d; NextBlockToWrite=%d\n",
+ safe_mutex_lock(OutMutex);
+ fprintf( stderr, "(%"PRIu64") waitForPreviousBlock enter: LastGoodBlock=%d"
+ "; blockNumToWait=%d; NextBlockToWrite=%d; MinErrorBlock=%d; errBlockNumber=%d\n",
thid,
- LastGoodBlock, blockNum, NextBlockToWrite );
+ LastGoodBlock, blockNumToWait, NextBlockToWrite,
+ MinErrorBlock, errBlockNumber );
+ safe_mutex_unlock(OutMutex);
#endif
for (;;)
if (syncGetTerminateFlag() != 0)
{
#ifdef PBZIP_DEBUG
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock terminated [%d]: blockNum=%d\n",
- thid, -1, blockNum );
+ fprintf(stderr, "(%"PRIu64") waitForPreviousBlock terminated [%d]: blockNumToWait=%d\n",
+ thid, -1, blockNumToWait );
#endif
return -1;
}
safe_mutex_lock(OutMutex);
#ifdef PBZIP_DEBUG
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock before check: LastGoodBlock=%d; blockNum=%d; NextBlockToWrite=%d\n",
- thid, LastGoodBlock, blockNum, NextBlockToWrite );
+ fprintf( stderr, "(%"PRIu64") waitForPreviousBlock before check: LastGoodBlock=%d; blockNumToWait=%d; NextBlockToWrite=%d; MinErrorBlock=%d\n",
+ thid, LastGoodBlock, blockNumToWait, NextBlockToWrite, MinErrorBlock );
#endif
- if (blockNum <= NextBlockToWrite)
+ // This check should (min error block) be before next one (next block to write)
+ if ( (MinErrorBlock != -1) && (MinErrorBlock < errBlockNumber) )
{
#ifdef PBZIP_DEBUG
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock exit [%d]: LastGoodBlock=%d; blockNum=%d; NextBlockToWrite=%d\n",
- thid, 0, LastGoodBlock, blockNum, NextBlockToWrite );
+ fprintf( stderr, "(%"PRIu64") waitForPreviousBlock exit [%d]: LastGoodBlock=%d; blockNumToWait=%d; NextBlockToWrite=%d; MinErrorBlock=%d\n",
+ thid, 2, LastGoodBlock, blockNumToWait, NextBlockToWrite, MinErrorBlock );
#endif
safe_mutex_unlock(OutMutex);
- return 0;
+ return 2;
}
-
- if ( (LastGoodBlock != -1) && (LastGoodBlock < blockNum) )
+
+ if (errBlockNumber <= NextBlockToWrite)
{
#ifdef PBZIP_DEBUG
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock exit [%d]: LastGoodBlock=%d; blockNum=%d; NextBlockToWrite=%d\n",
- thid, 2, LastGoodBlock, blockNum, NextBlockToWrite );
+ fprintf( stderr, "(%"PRIu64") waitForPreviousBlock exit [%d]: LastGoodBlock=%d; blockNumToWait=%d; NextBlockToWrite=%d; MinErrorBlock=%d\n",
+ thid, 0, LastGoodBlock, blockNumToWait, NextBlockToWrite, MinErrorBlock );
#endif
safe_mutex_unlock(OutMutex);
- return 2;
+ return 0;
}
#ifdef PBZIP_DEBUG
- fprintf(stderr, "(%"PRIu64") waitForPreviousBlock to sleep: LastGoodBlock=%d; blockNum=%d; NextBlockToWrite=%d\n",
- thid, LastGoodBlock, blockNum, NextBlockToWrite );
+ fprintf( stderr, "(%"PRIu64") waitForPreviousBlock to sleep: LastGoodBlock=%d; blockNumToWait=%d; NextBlockToWrite=%d; MinErrorBlock=%d\n",
+ thid, LastGoodBlock, blockNumToWait, NextBlockToWrite, MinErrorBlock );
#endif
safe_cond_timed_wait(&ErrStateChangeCond, OutMutex, 1, "waitForPreviousBlock");
/**
*
- * @param errBlockNumber
- * @param outSequenceNumber
- * @return Last input block not after the given which resulted in good out blocks.
+ * @param errBlockNumber block from input file
+ * @param outSequenceNumber sequence in the output tail for the given block
+ * @return Last input block not after the given which possibly (not guaranteed)
+ * resulted in good out blocks.
* -1 if such don't exist.
*/
inline int getLastGoodBlockBeforeErr(int errBlockNumber, int outSequenceNumber)
int outSequenceNumber, const bz_stream & strm, const char * errmsg,
int exitCode)
{
+ #ifdef PBZIP_DEBUG
+ unsigned long long thid = (unsigned long long) pthread_self();
+ fprintf(stderr, "(%"PRIu64") enter issueDecompressError: msg=%s; ret=%d; block=%d; seq=%d; isLastInSeq=%d; avail_in=%d\n",
+ thid,
+ errmsg, bzret, fileData->blockNumber,
+ outSequenceNumber, (int)fileData->isLastInSequence, strm.avail_in);
+ #endif
+
handle_error(EF_EXIT, exitCode,
"pbzip2: %s: ret=%d; block=%d; seq=%d; isLastInSeq=%d; avail_in=%d\n",
errmsg, bzret, fileData->blockNumber,
*
*
* @param bzret
- * @param fileData
+ * @param fileData block from input file
* @param outSequenceNumber
* @param strm
* @param errmsg
int lastGoodBlock = getLastGoodBlockBeforeErr(fileData->blockNumber, outSequenceNumber);
#ifdef PBZIP_DEBUG
- fprintf(stderr, "enter decompressErrCheckSingle: msg=%s; ret=%d; block=%d; seq=%d; isLastInSeq=%d; avail_in=%d; lastGoodBlock=%d\n",
- errmsg, bzret, fileData->blockNumber,
- outSequenceNumber, (int)fileData->isLastInSequence, strm.avail_in, lastGoodBlock);
+ unsigned long long thid = (unsigned long long) pthread_self();
+ fprintf(stderr, "(%"PRIu64") enter decompressErrCheckSingle: msg=%s; ret=%d; block=%d; seq=%d; isLastInSeq=%d; avail_in=%d; lastGoodBlock=%d\n",
+ thid,
+ errmsg, bzret, fileData->blockNumber,
+ outSequenceNumber, (int)fileData->isLastInSequence, strm.avail_in, lastGoodBlock);
#endif
if ( (lastGoodBlock == -1) || !isIgnoredTrailingGarbage() )
else
{
// Cut off larger block numbers
- syncSetLastGoodBlock(lastGoodBlock);
+ syncSetLastGoodBlock(lastGoodBlock, fileData->blockNumber);
// wait until the state of previous block is known
- int prevState = waitForPreviousBlock(lastGoodBlock);
+ int prevState = waitForPreviousBlock(lastGoodBlock, fileData->blockNumber);
if (prevState == 0)
{
}
/**
+ * Check if trailing garbage has been identified during the last decompression
+ * operation.
+ *
+ * @param bzret last bzip2 return code
+ * @param fileData should be initialized before calling this
+ * @param strm bzip2 library bz_stream
+ * @return true if trailing garbage has been detected. false otherwise
+ */
+inline bool hasTrailingGarbage(int bzret, const outBuff * fileData, const bz_stream & strm)
+{
+ return (bzret == BZ_STREAM_END) &&
+ ( (strm.avail_in != 0) || !fileData->isLastInSequence );
+}
+
+/**
*
* @param bzret
* @param fileData
int decompressErrCheck(int bzret, const outBuff * fileData,
int outSequenceNumber, const bz_stream & strm)
{
- if ( (bzret == BZ_STREAM_END) &&
- ((strm.avail_in != 0) || !fileData->isLastInSequence) )
+ if ( hasTrailingGarbage( bzret, fileData, strm ) )
{
// Potential trailing garbage
return decompressErrCheckSingle(bzret, fileData, outSequenceNumber, strm,
isInterrupted = true;
#ifdef PBZIP_DEBUG
- fprintf (stderr, "(%"PRIu64") producer_decompress: interrupt1 - TerminateFlag set.\n", thid);
+ fprintf (stderr, "(%"PRIu64") consumer_decompress: interrupt1 - TerminateFlag set.\n", thid);
#endif
}
- if ( (syncGetLastGoodBlock() != -1) &&
- ( (lastElement == NULL) || (lastElement->blockNumber > syncGetLastGoodBlock())
- || lastElement->isLastInSequence ) )
+ int minErrBlock = syncGetMinErrorBlock();
+ if ( (minErrBlock != -1) &&
+ ( (lastElement == NULL)
+ || (lastElement->blockNumber >= minErrBlock)
+ || lastElement->isLastInSequence ) )
{
isInterrupted = true;
outBuff * addret = NULL;
unsigned int len = outSize - strm.avail_out;
bool isLast = (bzret == BZ_STREAM_END);
-
- if ( isLast && ( (strm.avail_in != 0) || !fileData->isLastInSequence ) )
+
+ if ( hasTrailingGarbage( bzret, fileData, strm ) )
{
- // trailng garbage detected
- syncSetLastGoodBlock(fileData->blockNumber);
+ // trailing garbage detected
+ syncSetLastGoodBlock(fileData->blockNumber, fileData->blockNumber);
}
if (outSequenceNumber>0)
*/
void banner()
{
- fprintf(stderr, "Parallel BZIP2 v1.1.6 - by: Jeff Gilchrist [http://compression.ca]\n");
- fprintf(stderr, "[Oct. 30, 2011] (uses libbzip2 by Julian Seward)\n");
+ fprintf(stderr, "Parallel BZIP2 v1.1.7 - by: Jeff Gilchrist [http://compression.ca]\n");
+ fprintf(stderr, "[Dec. 11, 2011] (uses libbzip2 by Julian Seward)\n");
fprintf(stderr, "Major contributions: Yavor Nikolov <nikolov.javor+pbzip2@gmail.com>\n");
return;
FileList[FileListCount] = stdinFile;
FileListCount++;
}
- else if (OutputStdOut == 1)
- {
- #ifndef WIN32
- if (isatty(fileno(stdout)))
- #else
- if (_isatty(_fileno(stdout)))
- #endif
- {
- fprintf(stderr,"pbzip2: *ERROR: Won't write compressed data to terminal. Aborting!\n");
- fprintf(stderr,"pbzip2: For help type: %s -h\n", argv[0]);
- return 1;
- }
- // expecting data from stdin
- FileList[FileListCount] = stdinFile;
- FileListCount++;
- }
- else if ((decompress == 1) && (argc == 2))
+ else if (decompress == 1)
{
#ifndef WIN32
if (isatty(fileno(stdin)))
if (_isatty(_fileno(stdin)))
#endif
{
- fprintf(stderr,"pbzip2: *ERROR: Won't read compressed data from terminal. Aborting!\n");
- fprintf(stderr,"pbzip2: For help type: %s -h\n", argv[0]);
- return 1;
+ fprintf(stderr,"pbzip2: *ERROR: Won't read compressed data from terminal. Aborting!\n");
+ fprintf(stderr,"pbzip2: For help type: %s -h\n", argv[0]);
+ return 1;
}
// expecting data from stdin via TAR
OutputStdOut = 1;
FileListCount++;
}
else
- {
- // probably trying to input data from stdin
- if (QuietMode != 1)
- fprintf(stderr,"pbzip2: Assuming input data coming from stdin...\n\n");
+ {
+ if (OutputStdOut == 0)
+ {
+ // probably trying to input data from stdin
+ if (QuietMode != 1)
+ fprintf(stderr,"pbzip2: Assuming input data coming from stdin...\n\n");
+
+ OutputStdOut = 1;
+ keep = 1;
+ }
- OutputStdOut = 1;
- keep = 1;
#ifndef WIN32
if (isatty(fileno(stdout)))
#else
if (_isatty(_fileno(stdout)))
#endif
{
- fprintf(stderr,"pbzip2: *ERROR: Won't write compressed data to terminal. Aborting!\n");
- fprintf(stderr,"pbzip2: For help type: %s -h\n", argv[0]);
- return 1;
+ fprintf(stderr,"pbzip2: *ERROR: Won't write compressed data to terminal. Aborting!\n");
+ fprintf(stderr,"pbzip2: For help type: %s -h\n", argv[0]);
+ return 1;
}
// expecting data from stdin
FileList[FileListCount] = stdinFile;
FileListCount++;
- }
+ }
}
if (QuietMode != 1)
}
LastGoodBlock = -1;
+ MinErrorBlock = -1;
// create output buffer
outputBufferInit(NumBufferedBlocksMax);