aria2 now doesn't assume download's completed just because file size matched

The only exception is zero-length file.  If server tells file is
zero-length and --checksum option is given, aria2 now correctly checks
its checksum. There is one known issue: If downloaded file is
zero-length file and .aria2 file exists, it will not be deleted on
successful verification, because .aria2 file is not loaded.
pull/8/head
Tatsuhiro Tsujikawa 2012-01-08 17:46:03 +09:00
parent b6f8a3dbb3
commit 1c292f469e
6 changed files with 121 additions and 71 deletions

View File

@ -69,9 +69,7 @@ void ChecksumCheckIntegrityEntry::initValidator()
void void
ChecksumCheckIntegrityEntry::onDownloadFinished ChecksumCheckIntegrityEntry::onDownloadFinished
(std::vector<Command*>& commands, DownloadEngine* e) (std::vector<Command*>& commands, DownloadEngine* e)
{ {}
getRequestGroup()->getDownloadContext()->setChecksumVerified(true);
}
void void
ChecksumCheckIntegrityEntry::onDownloadIncomplete ChecksumCheckIntegrityEntry::onDownloadIncomplete

View File

@ -74,6 +74,9 @@
#include "CheckIntegrityEntry.h" #include "CheckIntegrityEntry.h"
#include "error_code.h" #include "error_code.h"
#include "SocketRecvBuffer.h" #include "SocketRecvBuffer.h"
#ifdef ENABLE_MESSAGE_DIGEST
# include "ChecksumCheckIntegrityEntry.h"
#endif // ENABLE_MESSAGE_DIGEST
namespace aria2 { namespace aria2 {
@ -398,12 +401,23 @@ bool FtpNegotiationCommand::onFileSizeDetermined(off_t totalLength)
if(getDownloadContext()->knowsTotalLength() && if(getDownloadContext()->knowsTotalLength() &&
getRequestGroup()->downloadFinishedByFileLength()) { getRequestGroup()->downloadFinishedByFileLength()) {
// TODO If metalink file does not contain size and it contains #ifdef ENABLE_MESSAGE_DIGEST
// hash and file is not zero length, but remote server says the // TODO Known issue: if .aria2 file exists, it will not be
// file size is 0, no hash check is performed in the current // deleted on successful verification, because .aria2 file is
// implementation. See also // not loaded. See also
// HttpResponseCommand::handleOtherEncoding() // HttpResponseCommand::handleOtherEncoding()
getRequestGroup()->initPieceStorage(); getRequestGroup()->initPieceStorage();
if(getDownloadContext()->isChecksumVerificationNeeded()) {
A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
SharedHandle<ChecksumCheckIntegrityEntry> entry
(new ChecksumCheckIntegrityEntry(getRequestGroup()));
entry->initValidator();
getPieceStorage()->getDiskAdaptor()->openExistingFile();
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry);
sequence_ = SEQ_EXIT;
} else
#endif // ENABLE_MESSAGE_DIGEST
{
getPieceStorage()->markAllPiecesDone(); getPieceStorage()->markAllPiecesDone();
getDownloadContext()->setChecksumVerified(true); getDownloadContext()->setChecksumVerified(true);
sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
@ -411,8 +425,8 @@ bool FtpNegotiationCommand::onFileSizeDetermined(off_t totalLength)
(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
getRequestGroup()->getGID(), getRequestGroup()->getGID(),
getRequestGroup()->getFirstFilePath().c_str())); getRequestGroup()->getFirstFilePath().c_str()));
}
poolConnection(); poolConnection();
return false; return false;
} }
@ -421,8 +435,25 @@ bool FtpNegotiationCommand::onFileSizeDetermined(off_t totalLength)
getPieceStorage()->getDiskAdaptor()->initAndOpenFile(); getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
if(getDownloadContext()->knowsTotalLength()) { if(getDownloadContext()->knowsTotalLength()) {
A2_LOG_DEBUG("File length becomes zero and it means download completed.");
#ifdef ENABLE_MESSAGE_DIGEST
// TODO Known issue: if .aria2 file exists, it will not be
// deleted on successful verification, because .aria2 file is
// not loaded. See also
// HttpResponseCommand::handleOtherEncoding()
if(getDownloadContext()->isChecksumVerificationNeeded()) {
A2_LOG_DEBUG("Verify checksum for zero-length file");
SharedHandle<ChecksumCheckIntegrityEntry> entry
(new ChecksumCheckIntegrityEntry(getRequestGroup()));
entry->initValidator();
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry);
sequence_ = SEQ_EXIT;
} else
#endif // ENABLE_MESSAGE_DIGEST
{
sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
getPieceStorage()->markAllPiecesDone(); getPieceStorage()->markAllPiecesDone();
}
poolConnection(); poolConnection();
return false; return false;
} }

View File

@ -75,6 +75,7 @@
#include "MetalinkHttpEntry.h" #include "MetalinkHttpEntry.h"
#ifdef ENABLE_MESSAGE_DIGEST #ifdef ENABLE_MESSAGE_DIGEST
# include "Checksum.h" # include "Checksum.h"
# include "ChecksumCheckIntegrityEntry.h"
#endif // ENABLE_MESSAGE_DIGEST #endif // ENABLE_MESSAGE_DIGEST
#ifdef HAVE_ZLIB #ifdef HAVE_ZLIB
# include "GZipDecodingStreamFilter.h" # include "GZipDecodingStreamFilter.h"
@ -431,18 +432,28 @@ bool HttpResponseCommand::handleOtherEncoding
// For zero-length file, check existing file comparing its size // For zero-length file, check existing file comparing its size
if(!chunkedUsed && getDownloadContext()->knowsTotalLength() && if(!chunkedUsed && getDownloadContext()->knowsTotalLength() &&
getRequestGroup()->downloadFinishedByFileLength()) { getRequestGroup()->downloadFinishedByFileLength()) {
// TODO If metalink file does not contain size and it contains
// hash and file is not zero length, but remote server says the
// file size is 0, no hash check is performed in the current
// implementation. See also
// FtpNegotiationCommand::onFileSizeDetermined()
getRequestGroup()->initPieceStorage(); getRequestGroup()->initPieceStorage();
#ifdef ENABLE_MESSAGE_DIGEST
// TODO Known issue: if .aria2 file exists, it will not be deleted
// on successful verification, because .aria2 file is not loaded.
// See also FtpNegotiationCommand::onFileSizeDetermined()
if(getDownloadContext()->isChecksumVerificationNeeded()) {
A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
SharedHandle<ChecksumCheckIntegrityEntry> entry
(new ChecksumCheckIntegrityEntry(getRequestGroup()));
entry->initValidator();
getPieceStorage()->getDiskAdaptor()->openExistingFile();
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry);
} else
#endif // ENABLE_MESSAGE_DIGEST
{
getPieceStorage()->markAllPiecesDone(); getPieceStorage()->markAllPiecesDone();
getDownloadContext()->setChecksumVerified(true); getDownloadContext()->setChecksumVerified(true);
A2_LOG_NOTICE A2_LOG_NOTICE
(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
getRequestGroup()->getGID(), getRequestGroup()->getGID(),
getRequestGroup()->getFirstFilePath().c_str())); getRequestGroup()->getFirstFilePath().c_str()));
}
poolConnection(); poolConnection();
return true; return true;
} }
@ -455,7 +466,22 @@ bool HttpResponseCommand::handleOtherEncoding
// is called. So zero-length file is complete if chunked encoding is // is called. So zero-length file is complete if chunked encoding is
// not used. // not used.
if(!chunkedUsed && getDownloadContext()->knowsTotalLength()) { if(!chunkedUsed && getDownloadContext()->knowsTotalLength()) {
A2_LOG_DEBUG("File length becomes zero and it means download completed.");
// TODO Known issue: if .aria2 file exists, it will not be deleted
// on successful verification, because .aria2 file is not loaded.
// See also FtpNegotiationCommand::onFileSizeDetermined()
#ifdef ENABLE_MESSAGE_DIGEST
if(getDownloadContext()->isChecksumVerificationNeeded()) {
A2_LOG_DEBUG("Verify checksum for zero-length file");
SharedHandle<ChecksumCheckIntegrityEntry> entry
(new ChecksumCheckIntegrityEntry(getRequestGroup()));
entry->initValidator();
getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry);
} else
#endif // ENABLE_MESSAGE_DIGEST
{
getRequestGroup()->getPieceStorage()->markAllPiecesDone(); getRequestGroup()->getPieceStorage()->markAllPiecesDone();
}
poolConnection(); poolConnection();
return true; return true;
} }

View File

@ -61,7 +61,8 @@ IteratableChecksumValidator::~IteratableChecksumValidator() {}
void IteratableChecksumValidator::validateChunk() void IteratableChecksumValidator::validateChunk()
{ {
if(!finished()) { // Don't guard with !finished() to allow zero-length file to be
// verified.
unsigned char buf[4096]; unsigned char buf[4096];
size_t length = pieceStorage_->getDiskAdaptor()->readData size_t length = pieceStorage_->getDiskAdaptor()->readData
(buf, sizeof(buf), currentOffset_); (buf, sizeof(buf), currentOffset_);
@ -71,6 +72,7 @@ void IteratableChecksumValidator::validateChunk()
std::string actualDigest = ctx_->digest(); std::string actualDigest = ctx_->digest();
if(dctx_->getDigest() == actualDigest) { if(dctx_->getDigest() == actualDigest) {
pieceStorage_->markAllPiecesDone(); pieceStorage_->markAllPiecesDone();
dctx_->setChecksumVerified(true);
} else { } else {
A2_LOG_INFO(fmt("Checksum validation failed. expected=%s, actual=%s", A2_LOG_INFO(fmt("Checksum validation failed. expected=%s, actual=%s",
util::toHex(dctx_->getDigest()).c_str(), util::toHex(dctx_->getDigest()).c_str(),
@ -80,7 +82,6 @@ void IteratableChecksumValidator::validateChunk()
} }
} }
} }
}
bool IteratableChecksumValidator::finished() const bool IteratableChecksumValidator::finished() const
{ {

View File

@ -167,10 +167,11 @@ RequestGroup::RequestGroup(const SharedHandle<Option>& option)
RequestGroup::~RequestGroup() {} RequestGroup::~RequestGroup() {}
bool RequestGroup::isCheckIntegrityReady() const bool RequestGroup::isCheckIntegrityReady()
{ {
return option_->getAsBool(PREF_CHECK_INTEGRITY) && return option_->getAsBool(PREF_CHECK_INTEGRITY) &&
(downloadContext_->isChecksumVerificationAvailable() || ((downloadContext_->isChecksumVerificationAvailable() &&
downloadFinishedByFileLength()) ||
downloadContext_->isPieceHashVerificationAvailable()); downloadContext_->isPieceHashVerificationAvailable());
} }
@ -252,11 +253,10 @@ SharedHandle<CheckIntegrityEntry> RequestGroup::createCheckIntegrityEntry()
} else { } else {
checkEntry.reset(new StreamCheckIntegrityEntry(this)); checkEntry.reset(new StreamCheckIntegrityEntry(this));
} }
} else if(downloadFinishedByFileLength()) {
pieceStorage_->markAllPiecesDone();
#ifdef ENABLE_MESSAGE_DIGEST #ifdef ENABLE_MESSAGE_DIGEST
if(option_->getAsBool(PREF_CHECK_INTEGRITY) && } else if(downloadFinishedByFileLength() &&
downloadContext_->isChecksumVerificationAvailable()) { downloadContext_->isChecksumVerificationAvailable()) {
pieceStorage_->markAllPiecesDone();
loadAndOpenFile(infoFile); loadAndOpenFile(infoFile);
ChecksumCheckIntegrityEntry* tempEntry = ChecksumCheckIntegrityEntry* tempEntry =
new ChecksumCheckIntegrityEntry(this); new ChecksumCheckIntegrityEntry(this);
@ -265,11 +265,6 @@ SharedHandle<CheckIntegrityEntry> RequestGroup::createCheckIntegrityEntry()
} else } else
#endif // ENABLE_MESSAGE_DIGEST #endif // ENABLE_MESSAGE_DIGEST
{ {
downloadContext_->setChecksumVerified(true);
A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
gid_, downloadContext_->getBasePath().c_str()));
}
} else {
loadAndOpenFile(infoFile); loadAndOpenFile(infoFile);
checkEntry.reset(new StreamCheckIntegrityEntry(this)); checkEntry.reset(new StreamCheckIntegrityEntry(this));
} }
@ -689,8 +684,6 @@ void RequestGroup::adjustFilename
} }
if(infoFile->exists()) { if(infoFile->exists()) {
// Use current filename // Use current filename
} else if(downloadFinishedByFileLength()) {
// File was downloaded already, no need to change file name.
} else { } else {
File outfile(getFirstFilePath()); File outfile(getFirstFilePath());
if(outfile.exists() && option_->getAsBool(PREF_CONTINUE) && if(outfile.exists() && option_->getAsBool(PREF_CONTINUE) &&

View File

@ -195,12 +195,13 @@ private:
void removeDefunctControlFile void removeDefunctControlFile
(const SharedHandle<BtProgressInfoFile>& progressInfoFile); (const SharedHandle<BtProgressInfoFile>& progressInfoFile);
bool isCheckIntegrityReady() const;
public: public:
RequestGroup(const SharedHandle<Option>& option); RequestGroup(const SharedHandle<Option>& option);
~RequestGroup(); ~RequestGroup();
bool isCheckIntegrityReady();
const SharedHandle<SegmentMan>& getSegmentMan() const const SharedHandle<SegmentMan>& getSegmentMan() const
{ {
return segmentMan_; return segmentMan_;