Index: content/browser/download/base_file.cc |
diff --git a/content/browser/download/base_file.cc b/content/browser/download/base_file.cc |
index 8fda8de6a5c48834ab579aabaceedf5c79055e9f..74072bb1f723818c1bf565e5fda1f1c0e09a2750 100644 |
--- a/content/browser/download/base_file.cc |
+++ b/content/browser/download/base_file.cc |
@@ -25,38 +25,8 @@ |
namespace content { |
-// This will initialize the entire array to zero. |
-const unsigned char BaseFile::kEmptySha256Hash[] = { 0 }; |
- |
-BaseFile::BaseFile(const base::FilePath& full_path, |
- const GURL& source_url, |
- const GURL& referrer_url, |
- int64_t received_bytes, |
- bool calculate_hash, |
- const std::string& hash_state_bytes, |
- base::File file, |
- const net::BoundNetLog& bound_net_log) |
- : full_path_(full_path), |
- source_url_(source_url), |
- referrer_url_(referrer_url), |
- file_(std::move(file)), |
- bytes_so_far_(received_bytes), |
- start_tick_(base::TimeTicks::Now()), |
- calculate_hash_(calculate_hash), |
- detached_(false), |
- bound_net_log_(bound_net_log) { |
- memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length); |
- if (calculate_hash_) { |
- secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
- if ((bytes_so_far_ > 0) && // Not starting at the beginning. |
- (!IsEmptyHash(hash_state_bytes))) { |
- base::Pickle hash_state(hash_state_bytes.c_str(), |
- hash_state_bytes.size()); |
- base::PickleIterator data_iterator(hash_state); |
- secure_hash_->Deserialize(&data_iterator); |
- } |
- } |
-} |
+BaseFile::BaseFile(const net::BoundNetLog& bound_net_log) |
+ : bound_net_log_(bound_net_log) {} |
BaseFile::~BaseFile() { |
DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
@@ -67,11 +37,16 @@ BaseFile::~BaseFile() { |
} |
DownloadInterruptReason BaseFile::Initialize( |
- const base::FilePath& default_directory) { |
+ const base::FilePath& full_path, |
+ const base::FilePath& default_directory, |
+ base::File file, |
+ int64_t bytes_so_far, |
+ const std::string& hash_so_far, |
+ scoped_ptr<crypto::SecureHash> hash_state) { |
DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
DCHECK(!detached_); |
- if (full_path_.empty()) { |
+ if (full_path.empty()) { |
base::FilePath initial_directory(default_directory); |
base::FilePath temp_file; |
if (initial_directory.empty()) { |
@@ -87,9 +62,15 @@ DownloadInterruptReason BaseFile::Initialize( |
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
} |
full_path_ = temp_file; |
+ } else { |
+ full_path_ = full_path; |
} |
- return Open(); |
+ bytes_so_far_ = bytes_so_far; |
+ secure_hash_ = std::move(hash_state); |
+ file_ = std::move(file); |
+ |
+ return Open(hash_so_far); |
} |
DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, |
@@ -134,7 +115,7 @@ DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, |
RecordDownloadWriteSize(data_len); |
RecordDownloadWriteLoopCount(write_count); |
- if (calculate_hash_) |
+ if (secure_hash_) |
secure_hash_->Update(data, data_len); |
return DOWNLOAD_INTERRUPT_REASON_NONE; |
@@ -170,7 +151,7 @@ DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { |
// reason. |
DownloadInterruptReason open_result = DOWNLOAD_INTERRUPT_REASON_NONE; |
if (was_in_progress) |
- open_result = Open(); |
+ open_result = Open(std::string()); |
bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED); |
return rename_result == DOWNLOAD_INTERRUPT_REASON_NONE ? open_result |
@@ -198,92 +179,127 @@ void BaseFile::Cancel() { |
Detach(); |
} |
-void BaseFile::Finish() { |
- DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
- |
- if (calculate_hash_) |
- secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length); |
- Close(); |
-} |
- |
-void BaseFile::FinishWithError() { |
+scoped_ptr<crypto::SecureHash> BaseFile::Finish() { |
DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
Close(); |
-} |
- |
-void BaseFile::SetClientGuid(const std::string& guid) { |
- client_guid_ = guid; |
+ return std::move(secure_hash_); |
} |
// OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations. |
#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) |
-DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { |
+DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( |
+ const std::string& client_guid, |
+ const GURL& source_url, |
+ const GURL& referrer_url) { |
return DOWNLOAD_INTERRUPT_REASON_NONE; |
} |
#endif |
-bool BaseFile::GetHash(std::string* hash) { |
- DCHECK(!detached_); |
- hash->assign(reinterpret_cast<const char*>(sha256_hash_), |
- sizeof(sha256_hash_)); |
- return (calculate_hash_ && !in_progress()); |
+std::string BaseFile::DebugString() const { |
+ return base::StringPrintf( |
+ "{ " |
+ " full_path_ = \"%" PRFilePath |
+ "\"" |
+ " bytes_so_far_ = %" PRId64 " detached_ = %c }", |
+ full_path_.value().c_str(), |
+ bytes_so_far_, |
+ detached_ ? 'T' : 'F'); |
} |
-std::string BaseFile::GetHashState() { |
- if (!calculate_hash_) |
- return std::string(); |
+DownloadInterruptReason BaseFile::CalculatePartialHash( |
+ const std::string& hash_to_expect) { |
+ secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
- base::Pickle hash_state; |
- if (!secure_hash_->Serialize(&hash_state)) |
- return std::string(); |
+ if (bytes_so_far_ == 0) |
+ return DOWNLOAD_INTERRUPT_REASON_NONE; |
- return std::string(reinterpret_cast<const char*>(hash_state.data()), |
- hash_state.size()); |
-} |
+ if (file_.Seek(base::File::FROM_BEGIN, 0) != 0) |
+ return LogSystemError("Seek partial file", |
+ logging::GetLastSystemErrorCode()); |
+ |
+ const size_t kMinBufferSize = secure_hash_->GetHashLength(); |
+ const size_t kMaxBufferSize = 1024 * 512; |
+ |
+ // The size of the buffer is: |
+ // - at least kMinBufferSize so that we can use it to hold the hash as well. |
+ // - at most kMaxBufferSize so that there's a reasonable bound. |
+ // - not larger than |bytes_so_far_| unless bytes_so_far_ is less than the |
+ // hash size. |
+ std::vector<char> buffer(std::max( |
+ kMinBufferSize, std::min<size_t>(kMaxBufferSize, bytes_so_far_))); |
+ |
+ int64_t current_position = 0; |
+ while (current_position < bytes_so_far_) { |
+ int length = file_.ReadAtCurrentPos(&buffer.front(), buffer.size()); |
+ if (length == -1) { |
+ return LogInterruptReason("Reading partial file", |
+ logging::GetLastSystemErrorCode(), |
+ DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
+ } |
-// static |
-bool BaseFile::IsEmptyHash(const std::string& hash) { |
- return (hash.size() == crypto::kSHA256Length && |
- 0 == memcmp(hash.data(), kEmptySha256Hash, crypto::kSHA256Length)); |
-} |
+ if (length == 0) |
+ break; |
-std::string BaseFile::DebugString() const { |
- return base::StringPrintf("{ source_url_ = \"%s\"" |
- " full_path_ = \"%" PRFilePath "\"" |
- " bytes_so_far_ = %" PRId64 |
- " detached_ = %c }", |
- source_url_.spec().c_str(), |
- full_path_.value().c_str(), |
- bytes_so_far_, |
- detached_ ? 'T' : 'F'); |
+ secure_hash_->Update(&buffer.front(), length); |
+ current_position += length; |
+ } |
+ |
+ if (current_position != bytes_so_far_) { |
+ return LogInterruptReason( |
+ "Verifying prefix hash", 0, DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
+ } |
+ |
+ if (!hash_to_expect.empty()) { |
+ DCHECK_EQ(secure_hash_->GetHashLength(), hash_to_expect.size()); |
+ DCHECK(buffer.size() >= secure_hash_->GetHashLength()); |
+ scoped_ptr<crypto::SecureHash> partial_hash(secure_hash_->Clone()); |
+ partial_hash->Finish(&buffer.front(), buffer.size()); |
+ |
+ if (memcmp(&buffer.front(), |
+ hash_to_expect.c_str(), |
+ partial_hash->GetHashLength())) { |
+ return LogInterruptReason("Verifying prefix hash", |
+ 0, |
+ DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH); |
+ } |
+ } |
+ |
+ return DOWNLOAD_INTERRUPT_REASON_NONE; |
} |
-DownloadInterruptReason BaseFile::Open() { |
+DownloadInterruptReason BaseFile::Open(const std::string& hash_so_far) { |
DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
DCHECK(!detached_); |
DCHECK(!full_path_.empty()); |
- bound_net_log_.BeginEvent( |
- net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, |
- base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); |
- |
// Create a new file if it is not provided. |
if (!file_.IsValid()) { |
- file_.Initialize( |
- full_path_, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE); |
+ file_.Initialize(full_path_, |
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE | |
+ base::File::FLAG_READ); |
if (!file_.IsValid()) { |
- return LogNetError("Open", |
+ return LogNetError("Open/Initialize File", |
net::FileErrorToNetError(file_.error_details())); |
} |
} |
- // We may be re-opening the file after rename. Always make sure we're |
- // writing at the end of the file. |
+ bound_net_log_.BeginEvent( |
+ net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, |
+ base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); |
+ |
+ if (!secure_hash_) { |
+ DownloadInterruptReason reason = CalculatePartialHash(hash_so_far); |
+ if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { |
+ ClearFile(); |
+ return reason; |
+ } |
+ } |
+ |
int64_t file_size = file_.Seek(base::File::FROM_END, 0); |
if (file_size < 0) { |
logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
ClearFile(); |
- return LogSystemError("Seek", error); |
+ return LogSystemError("Seeking to end", error); |
} else if (file_size > bytes_so_far_) { |
// The file is larger than we expected. |
// This is OK, as long as we don't use the extra. |
@@ -292,7 +308,7 @@ DownloadInterruptReason BaseFile::Open() { |
file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) { |
logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
ClearFile(); |
- return LogSystemError("Truncate", error); |
+ return LogSystemError("Truncating to last known offset", error); |
} |
} else if (file_size < bytes_so_far_) { |
// The file is shorter than we expected. Our hashes won't be valid. |
@@ -347,6 +363,9 @@ DownloadInterruptReason BaseFile::LogInterruptReason( |
const char* operation, |
int os_error, |
DownloadInterruptReason reason) { |
+ DVLOG(1) << __FUNCTION__ << "() operation:" << operation |
+ << " os_error:" << os_error |
+ << " reason:" << DownloadInterruptReasonToString(reason); |
bound_net_log_.AddEvent( |
net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, |
base::Bind(&FileInterruptedNetLogCallback, operation, os_error, reason)); |