summaryrefslogtreecommitdiffstats
path: root/content/browser
diff options
context:
space:
mode:
authorasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-25 01:23:48 +0000
committerasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-25 01:23:48 +0000
commit192d68082fcd74a8de473ed931a9931d28a66bc0 (patch)
treea4ad2578bc9c14bd13b4f21ae4b9c204c5fd33ba /content/browser
parenta33efc040cc07ab8526c9701a69103489862b455 (diff)
downloadchromium_src-192d68082fcd74a8de473ed931a9931d28a66bc0.zip
chromium_src-192d68082fcd74a8de473ed931a9931d28a66bc0.tar.gz
chromium_src-192d68082fcd74a8de473ed931a9931d28a66bc0.tar.bz2
Refactor BaseFile methods to return a DownloadInterruptReason instead of a net::Error.
Also split the implementation into platform specific files. BUG=157245 Review URL: https://chromiumcodereview.appspot.com/11238044 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@163983 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser')
-rw-r--r--content/browser/download/base_file.cc401
-rw-r--r--content/browser/download/base_file.h79
-rw-r--r--content/browser/download/base_file_linux.cc15
-rw-r--r--content/browser/download/base_file_mac.cc16
-rw-r--r--content/browser/download/base_file_posix.cc40
-rw-r--r--content/browser/download/base_file_unittest.cc166
-rw-r--r--content/browser/download/base_file_win.cc189
-rw-r--r--content/browser/download/download_file_impl.cc23
-rw-r--r--content/browser/download/download_net_log_parameters.cc15
-rw-r--r--content/browser/download/download_net_log_parameters.h5
-rw-r--r--content/browser/download/save_file.cc7
-rw-r--r--content/browser/download/save_file.h7
-rw-r--r--content/browser/download/save_file_manager.cc4
13 files changed, 544 insertions, 423 deletions
diff --git a/content/browser/download/base_file.cc b/content/browser/download/base_file.cc
index 6cbe98e..65fd7a1 100644
--- a/content/browser/download/base_file.cc
+++ b/content/browser/download/base_file.cc
@@ -11,7 +11,7 @@
#include "base/pickle.h"
#include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h"
-#include "base/utf_string_conversions.h"
+#include "content/browser/download/download_interrupt_reasons_impl.h"
#include "content/browser/download/download_net_log_parameters.h"
#include "content/browser/download/download_stats.h"
#include "content/public/browser/browser_thread.h"
@@ -20,186 +20,8 @@
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"
-#if defined(OS_WIN)
-#include <windows.h>
-#include <shellapi.h>
-
-#include "content/browser/safe_util_win.h"
-#elif defined(OS_MACOSX)
-#include "content/browser/download/file_metadata_mac.h"
-#elif defined(OS_LINUX)
-#include "content/browser/download/file_metadata_linux.h"
-#endif
-
using content::BrowserThread;
-namespace {
-
-#define LOG_ERROR(o, e) \
- LogError(__FILE__, __LINE__, __FUNCTION__, bound_net_log_, o, e)
-
-// Logs the value and passes error on through, converting to a |net::Error|.
-// Returns |ERR_UNEXPECTED| if the value is not in the enum.
-net::Error LogError(const char* file,
- int line,
- const char* func,
- const net::BoundNetLog& bound_net_log,
- const char* operation,
- int error) {
- const char* err_string = "";
- net::Error net_error = net::OK;
-
-#define NET_ERROR(label, value) \
- case net::ERR_##label: \
- err_string = #label; \
- net_error = net::ERR_##label; \
- break;
-
- switch (error) {
- case net::OK:
- return net::OK;
-
-#include "net/base/net_error_list.h"
-
- default:
- err_string = "Unexpected enum value";
- net_error = net::ERR_UNEXPECTED;
- break;
- }
-
-#undef NET_ERROR
-
- VLOG(1) << " " << func << "(): " << operation
- << "() returned error " << error << " (" << err_string << ")";
-
- bound_net_log.AddEvent(
- net::NetLog::TYPE_DOWNLOAD_FILE_ERROR,
- base::Bind(&download_net_logs::FileErrorCallback, operation, net_error));
-
- return net_error;
-}
-
-#if defined(OS_WIN)
-
-#define SHFILE_TO_NET_ERROR(symbol, value, mapping, description) \
- case value: return net::ERR_##mapping;
-
-// Maps the result of a call to |SHFileOperation()| onto a |net::Error|.
-//
-// These return codes are *old* (as in, DOS era), and specific to
-// |SHFileOperation()|.
-// They do not appear in any windows header.
-//
-// See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx.
-net::Error MapShFileOperationCodes(int code) {
- // Check these pre-Win32 error codes first, then check for matches
- // in Winerror.h.
-
- switch (code) {
- // Error Code, Value, Platform Error Mapping, Meaning
- SHFILE_TO_NET_ERROR(DE_SAMEFILE, 0x71, FILE_EXISTS,
- "The source and destination files are the same file.")
- SHFILE_TO_NET_ERROR(DE_OPCANCELLED, 0x75, ABORTED,
- "The operation was canceled by the user, or silently canceled if "
- "the appropriate flags were supplied to SHFileOperation.")
- SHFILE_TO_NET_ERROR(DE_ACCESSDENIEDSRC, 0x78, ACCESS_DENIED,
- "Security settings denied access to the source.")
- SHFILE_TO_NET_ERROR(DE_PATHTOODEEP, 0x79, FILE_PATH_TOO_LONG,
- "The source or destination path exceeded or would exceed MAX_PATH.")
- SHFILE_TO_NET_ERROR(DE_INVALIDFILES, 0x7C, FILE_NOT_FOUND,
- "The path in the source or destination or both was invalid.")
- SHFILE_TO_NET_ERROR(DE_FLDDESTISFILE, 0x7E, FILE_EXISTS,
- "The destination path is an existing file.")
- SHFILE_TO_NET_ERROR(DE_FILEDESTISFLD, 0x80, FILE_EXISTS,
- "The destination path is an existing folder.")
- SHFILE_TO_NET_ERROR(DE_FILENAMETOOLONG, 0x81, FILE_PATH_TOO_LONG,
- "The name of the file exceeds MAX_PATH.")
- SHFILE_TO_NET_ERROR(DE_DEST_IS_CDROM, 0x82, ACCESS_DENIED,
- "The destination is a read-only CD-ROM, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_DEST_IS_DVD, 0x83, ACCESS_DENIED,
- "The destination is a read-only DVD, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_DEST_IS_CDRECORD, 0x84, ACCESS_DENIED,
- "The destination is a writable CD-ROM, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_FILE_TOO_LARGE, 0x85, FILE_TOO_BIG,
- "The file involved in the operation is too large for the destination "
- "media or file system.")
- SHFILE_TO_NET_ERROR(DE_SRC_IS_CDROM, 0x86, ACCESS_DENIED,
- "The source is a read-only CD-ROM, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_SRC_IS_DVD, 0x87, ACCESS_DENIED,
- "The source is a read-only DVD, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_SRC_IS_CDRECORD, 0x88, ACCESS_DENIED,
- "The source is a writable CD-ROM, possibly unformatted.")
- SHFILE_TO_NET_ERROR(DE_ERROR_MAX, 0xB7, FILE_PATH_TOO_LONG,
- "MAX_PATH was exceeded during the operation.")
- SHFILE_TO_NET_ERROR(XE_ERRORONDEST, 0x10000, UNEXPECTED,
- "An unspecified error occurred on the destination.")
-
- // These are not expected to occur for in our usage.
- SHFILE_TO_NET_ERROR(DE_MANYSRC1DEST, 0x72, FAILED,
- "Multiple file paths were specified in the source buffer, "
- "but only one destination file path.")
- SHFILE_TO_NET_ERROR(DE_DIFFDIR, 0x73, FAILED,
- "Rename operation was specified but the destination path is "
- "a different directory. Use the move operation instead.")
- SHFILE_TO_NET_ERROR(DE_ROOTDIR, 0x74, FAILED,
- "The source is a root directory, which cannot be moved or renamed.")
- SHFILE_TO_NET_ERROR(DE_DESTSUBTREE, 0x76, FAILED,
- "The destination is a subtree of the source.")
- SHFILE_TO_NET_ERROR(DE_MANYDEST, 0x7A, FAILED,
- "The operation involved multiple destination paths, "
- "which can fail in the case of a move operation.")
- SHFILE_TO_NET_ERROR(DE_DESTSAMETREE, 0x7D, FAILED,
- "The source and destination have the same parent folder.")
- SHFILE_TO_NET_ERROR(DE_UNKNOWN_ERROR, 0x402, FAILED,
- "An unknown error occurred. "
- "This is typically due to an invalid path in the source or destination."
- " This error does not occur on Windows Vista and later.")
- SHFILE_TO_NET_ERROR(DE_ROOTDIR | ERRORONDEST, 0x10074, FAILED,
- "Destination is a root directory and cannot be renamed.")
- default:
- break;
- }
-
- // If not one of the above codes, it should be a standard Windows error code.
- return static_cast<net::Error>(net::MapSystemError(code));
-}
-
-#undef SHFILE_TO_NET_ERROR
-
-// Renames a file using the SHFileOperation API to ensure that the target file
-// gets the correct default security descriptor in the new path.
-// Returns a network error, or net::OK for success.
-net::Error RenameFileAndResetSecurityDescriptor(
- const FilePath& source_file_path,
- const FilePath& target_file_path) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- // The parameters to SHFileOperation must be terminated with 2 NULL chars.
- std::wstring source = source_file_path.value();
- std::wstring target = target_file_path.value();
-
- source.append(1, L'\0');
- target.append(1, L'\0');
-
- SHFILEOPSTRUCT move_info = {0};
- move_info.wFunc = FO_MOVE;
- move_info.pFrom = source.c_str();
- move_info.pTo = target.c_str();
- move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI |
- FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
-
- int result = SHFileOperation(&move_info);
-
- if (result == 0)
- return (move_info.fAnyOperationsAborted) ? net::ERR_ABORTED : net::OK;
-
- return MapShFileOperationCodes(result);
-}
-
-#endif
-
-} // namespace
-
// This will initialize the entire array to zero.
const unsigned char BaseFile::kEmptySha256Hash[] = { 0 };
@@ -208,7 +30,7 @@ BaseFile::BaseFile(const FilePath& full_path,
const GURL& referrer_url,
int64 received_bytes,
bool calculate_hash,
- const std::string& hash_state,
+ const std::string& hash_state_bytes,
scoped_ptr<net::FileStream> file_stream,
const net::BoundNetLog& bound_net_log)
: full_path_(full_path),
@@ -224,9 +46,10 @@ BaseFile::BaseFile(const FilePath& full_path,
if (calculate_hash_) {
secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
if ((bytes_so_far_ > 0) && // Not starting at the beginning.
- (hash_state != "") && // Reasonably sure we have a hash state.
- (!IsEmptyHash(hash_state))) {
- SetHashState(hash_state);
+ (!IsEmptyHash(hash_state_bytes))) {
+ Pickle hash_state(hash_state_bytes.c_str(), hash_state_bytes.size());
+ PickleIterator data_iterator(hash_state);
+ secure_hash_->Deserialize(&data_iterator);
}
}
}
@@ -239,7 +62,8 @@ BaseFile::~BaseFile() {
Cancel(); // Will delete the file.
}
-net::Error BaseFile::Initialize(const FilePath& default_directory) {
+content::DownloadInterruptReason BaseFile::Initialize(
+ const FilePath& default_directory) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(!detached_);
@@ -260,7 +84,8 @@ net::Error BaseFile::Initialize(const FilePath& default_directory) {
if ((initial_directory.empty() ||
!file_util::CreateTemporaryFileInDir(initial_directory, &temp_file)) &&
!file_util::CreateTemporaryFile(&temp_file)) {
- return LOG_ERROR("unable to create", net::ERR_FILE_NOT_FOUND);
+ return LogInterruptReason("Unable to create", 0,
+ content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
}
full_path_ = temp_file;
}
@@ -268,7 +93,8 @@ net::Error BaseFile::Initialize(const FilePath& default_directory) {
return Open();
}
-net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) {
+content::DownloadInterruptReason BaseFile::AppendDataToFile(const char* data,
+ size_t data_len) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(!detached_);
@@ -280,11 +106,12 @@ net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) {
}
if (!file_stream_.get())
- return LOG_ERROR("get", net::ERR_INVALID_HANDLE);
+ return LogInterruptReason("No file stream on append", 0,
+ content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
// TODO(phajdan.jr): get rid of this check.
if (data_len == 0)
- return net::OK;
+ return content::DOWNLOAD_INTERRUPT_REASON_NONE;
// The Write call below is not guaranteed to write all the data.
size_t write_count = 0;
@@ -303,7 +130,7 @@ net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) {
// Report errors on file writes.
if (write_result < 0)
- return LOG_ERROR("Write", write_result);
+ return LogNetError("Write", static_cast<net::Error>(write_result));
}
// Update status.
@@ -320,84 +147,43 @@ net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) {
if (calculate_hash_)
secure_hash_->Update(data, data_len);
- return net::OK;
+ return content::DOWNLOAD_INTERRUPT_REASON_NONE;
}
-net::Error BaseFile::Rename(const FilePath& new_path) {
+content::DownloadInterruptReason BaseFile::Rename(const FilePath& new_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ content::DownloadInterruptReason rename_result =
+ content::DOWNLOAD_INTERRUPT_REASON_NONE;
+
+ // If the new path is same as the old one, there is no need to perform the
+ // following renaming logic.
+ if (new_path == full_path_)
+ return content::DOWNLOAD_INTERRUPT_REASON_NONE;
// Save the information whether the download is in progress because
// it will be overwritten by closing the file.
- bool saved_in_progress = in_progress();
+ bool was_in_progress = in_progress();
- bound_net_log_.AddEvent(
+ bound_net_log_.BeginEvent(
net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED,
base::Bind(&download_net_logs::FileRenamedCallback,
&full_path_, &new_path));
-
- // If the new path is same as the old one, there is no need to perform the
- // following renaming logic.
- if (new_path == full_path_) {
- // Don't close the file if we're not done (finished or canceled).
- if (!saved_in_progress)
- Close();
-
- return net::OK;
- }
-
Close();
-
file_util::CreateDirectory(new_path.DirName());
-#if defined(OS_WIN)
- // We cannot rename because rename will keep the same security descriptor
- // on the destination file. We want to recreate the security descriptor
- // with the security that makes sense in the new path.
- // |RenameFileAndResetSecurityDescriptor| returns a windows-specific
- // error, whch we must translate into a net::Error.
- net::Error rename_err =
- RenameFileAndResetSecurityDescriptor(full_path_, new_path);
- if (rename_err != net::OK)
- return LOG_ERROR("RenameFileAndResetSecurityDescriptor", rename_err);
-#elif defined(OS_POSIX)
- {
- // Similarly, on Unix, we're moving a temp file created with permissions
- // 600 to |new_path|. Here, we try to fix up the destination file with
- // appropriate permissions.
- struct stat st;
- // First check the file existence and create an empty file if it doesn't
- // exist.
- if (!file_util::PathExists(new_path)) {
- int write_error = file_util::WriteFile(new_path, "", 0);
- if (write_error < 0)
- return LOG_ERROR("WriteFile", net::MapSystemError(errno));
- }
- int stat_error = stat(new_path.value().c_str(), &st);
- bool stat_succeeded = (stat_error == 0);
- if (!stat_succeeded)
- LOG_ERROR("stat", net::MapSystemError(errno));
-
- // TODO(estade): Move() falls back to copying and deleting when a simple
- // rename fails. Copying sucks for large downloads. crbug.com/8737
- if (!file_util::Move(full_path_, new_path))
- return LOG_ERROR("Move", net::MapSystemError(errno));
-
- if (stat_succeeded) {
- // On Windows file systems (FAT, NTFS), chmod fails. This is OK.
- int chmod_error = chmod(new_path.value().c_str(), st.st_mode);
- if (chmod_error < 0)
- LOG_ERROR("chmod", net::MapSystemError(errno));
- }
- }
-#endif
-
- full_path_ = new_path;
+ // A simple rename wouldn't work here since we want the file to have
+ // permissions / security descriptors that makes sense in the new directory.
+ rename_result = MoveFileAndAdjustPermissions(new_path);
- // We don't need to re-open the file if we're done (finished or canceled).
- if (!saved_in_progress)
- return net::OK;
+ if (rename_result == content::DOWNLOAD_INTERRUPT_REASON_NONE) {
+ full_path_ = new_path;
+ // Re-open the file if we were still using it.
+ if (was_in_progress)
+ rename_result = Open();
+ }
- return Open();
+ bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED);
+ return rename_result;
}
void BaseFile::Detach() {
@@ -429,6 +215,17 @@ void BaseFile::Finish() {
Close();
}
+// OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations.
+#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX)
+void BaseFile::AnnotateWithSourceInformation() {
+}
+#endif
+
+int64 BaseFile::CurrentSpeed() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return CurrentSpeedAtTime(base::TimeTicks::Now());
+}
+
bool BaseFile::GetHash(std::string* hash) {
DCHECK(!detached_);
hash->assign(reinterpret_cast<const char*>(sha256_hash_),
@@ -448,36 +245,21 @@ std::string BaseFile::GetHashState() {
hash_state.size());
}
-bool BaseFile::SetHashState(const std::string& hash_state_bytes) {
- if (!calculate_hash_)
- return false;
-
- Pickle hash_state(hash_state_bytes.c_str(), hash_state_bytes.size());
- PickleIterator data_iterator(hash_state);
-
- return secure_hash_->Deserialize(&data_iterator);
-}
-
+// static
bool BaseFile::IsEmptyHash(const std::string& hash) {
return (hash.size() == kSha256HashLen &&
0 == memcmp(hash.data(), kEmptySha256Hash, sizeof(kSha256HashLen)));
}
-void BaseFile::AnnotateWithSourceInformation() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- DCHECK(!detached_);
-
-#if defined(OS_WIN)
- // Sets the Zone to tell Windows that this file comes from the internet.
- // We ignore the return value because a failure is not fatal.
- win_util::SetInternetZoneIdentifier(full_path_,
- UTF8ToWide(source_url_.spec()));
-#elif defined(OS_MACOSX)
- content::AddQuarantineMetadataToFile(full_path_, source_url_, referrer_url_);
- content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_);
-#elif defined(OS_LINUX)
- content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_);
-#endif
+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');
}
void BaseFile::CreateFileStream() {
@@ -485,7 +267,7 @@ void BaseFile::CreateFileStream() {
file_stream_->SetBoundNetLogSource(bound_net_log_);
}
-net::Error BaseFile::Open() {
+content::DownloadInterruptReason BaseFile::Open() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(!detached_);
DCHECK(!full_path_.empty());
@@ -502,19 +284,23 @@ net::Error BaseFile::Open() {
int open_result = file_stream_->OpenSync(
full_path_,
base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE);
- if (open_result != net::OK)
- return ClearStream(LOG_ERROR("Open", open_result));
+ if (open_result != net::OK) {
+ ClearStream();
+ return LogNetError("Open", static_cast<net::Error>(open_result));
+ }
// We may be re-opening the file after rename. Always make sure we're
// writing at the end of the file.
int64 seek_result = file_stream_->SeekSync(net::FROM_END, 0);
- if (seek_result < 0)
- return ClearStream(LOG_ERROR("Seek", seek_result));
+ if (seek_result < 0) {
+ ClearStream();
+ return LogNetError("Seek", static_cast<net::Error>(seek_result));
+ }
} else {
file_stream_->SetBoundNetLogSource(bound_net_log_);
}
- return net::OK;
+ return content::DOWNLOAD_INTERRUPT_REASON_NONE;
}
void BaseFile::Close() {
@@ -529,27 +315,15 @@ void BaseFile::Close() {
file_stream_->FlushSync();
#endif
file_stream_->CloseSync();
- ClearStream(net::OK);
+ ClearStream();
}
}
-net::Error BaseFile::ClearStream(net::Error net_error) {
+void BaseFile::ClearStream() {
// This should only be called when we have a stream.
DCHECK(file_stream_.get() != NULL);
file_stream_.reset();
bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_OPENED);
- return net_error;
-}
-
-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');
}
int64 BaseFile::CurrentSpeedAtTime(base::TimeTicks current_time) const {
@@ -558,7 +332,34 @@ int64 BaseFile::CurrentSpeedAtTime(base::TimeTicks current_time) const {
return diff_ms == 0 ? 0 : bytes_so_far() * 1000 / diff_ms;
}
-int64 BaseFile::CurrentSpeed() const {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- return CurrentSpeedAtTime(base::TimeTicks::Now());
+content::DownloadInterruptReason BaseFile::LogNetError(
+ const char* operation,
+ net::Error error) {
+ bound_net_log_.AddEvent(
+ net::NetLog::TYPE_DOWNLOAD_FILE_ERROR,
+ base::Bind(&download_net_logs::FileErrorCallback, operation, error));
+ return content::ConvertNetErrorToInterruptReason(
+ error, content::DOWNLOAD_INTERRUPT_FROM_DISK);
+}
+
+content::DownloadInterruptReason BaseFile::LogSystemError(
+ const char* operation,
+ int os_error) {
+ // There's no direct conversion from a system error to an interrupt reason.
+ net::Error net_error = net::MapSystemError(os_error);
+ return LogInterruptReason(
+ operation, os_error,
+ content::ConvertNetErrorToInterruptReason(
+ net_error, content::DOWNLOAD_INTERRUPT_FROM_DISK));
+}
+
+content::DownloadInterruptReason BaseFile::LogInterruptReason(
+ const char* operation,
+ int os_error,
+ content::DownloadInterruptReason reason) {
+ bound_net_log_.AddEvent(
+ net::NetLog::TYPE_DOWNLOAD_FILE_ERROR,
+ base::Bind(&download_net_logs::FileInterruptedCallback, operation,
+ os_error, reason));
+ return reason;
}
diff --git a/content/browser/download/base_file.h b/content/browser/download/base_file.h
index d1daaf1..e6ae0a3 100644
--- a/content/browser/download/base_file.h
+++ b/content/browser/download/base_file.h
@@ -13,11 +13,15 @@
#include "base/memory/scoped_ptr.h"
#include "base/time.h"
#include "content/common/content_export.h"
+#include "content/public/browser/download_interrupt_reasons.h"
#include "googleurl/src/gurl.h"
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
+namespace content {
+enum DownloadInterruptReason;
+}
namespace crypto {
class SecureHash;
}
@@ -41,21 +45,23 @@ class CONTENT_EXPORT BaseFile {
const net::BoundNetLog& bound_net_log);
virtual ~BaseFile();
- // Returns net::OK on success, or a network error code on failure.
- // |default_directory| specifies the directory to create the temporary file in
- // if |full_path()| is empty. If |default_directory| and |full_path()| are
- // empty, then a temporary file will be created in the default download
- // location as determined by ContentBrowserClient.
- net::Error Initialize(const FilePath& default_directory);
+ // Returns DOWNLOAD_INTERRUPT_REASON_NONE on success, or a
+ // DownloadInterruptReason on failure. |default_directory| specifies the
+ // directory to create the temporary file in if |full_path()| is empty. If
+ // |default_directory| and |full_path()| are empty, then a temporary file will
+ // be created in the default download location as determined by
+ // ContentBrowserClient.
+ content::DownloadInterruptReason Initialize(
+ const FilePath& default_directory);
- // Write a new chunk of data to the file.
- // Returns net::OK on success (all bytes written to the file),
- // or a network error code on failure.
- net::Error AppendDataToFile(const char* data, size_t data_len);
+ // Write a new chunk of data to the file. Returns a DownloadInterruptReason
+ // indicating the result of the operation.
+ content::DownloadInterruptReason AppendDataToFile(const char* data,
+ size_t data_len);
- // Rename the download file.
- // Returns net::OK on success, or a network error code on failure.
- virtual net::Error Rename(const FilePath& full_path);
+ // Rename the download file. Returns a DownloadInterruptReason indicating the
+ // result of the operation.
+ virtual content::DownloadInterruptReason Rename(const FilePath& full_path);
// Detach the file so it is not deleted on destruction.
virtual void Detach();
@@ -90,30 +96,53 @@ class CONTENT_EXPORT BaseFile {
virtual std::string DebugString() const;
- protected:
- virtual void CreateFileStream(); // For testing.
- // Returns net::OK on success, or a network error code on failure.
- net::Error Open();
- void Close();
-
- // Full path to the file including the file name.
- FilePath full_path_;
-
private:
friend class BaseFileTest;
FRIEND_TEST_ALL_PREFIXES(BaseFileTest, IsEmptyHash);
+ // Re-initializes file_stream_ with a newly allocated net::FileStream().
+ void CreateFileStream();
+
+ // Creates and opens the file_stream_ if it is NULL.
+ content::DownloadInterruptReason Open();
+
+ // Closes and resets file_stream_.
+ void Close();
+
+ // Resets file_stream_.
+ void ClearStream();
+
+ // Platform specific method that moves a file to a new path and adjusts the
+ // security descriptor / permissions on the file to match the defaults for the
+ // new directory.
+ content::DownloadInterruptReason MoveFileAndAdjustPermissions(
+ const FilePath& new_path);
+
// Split out from CurrentSpeed to enable testing.
int64 CurrentSpeedAtTime(base::TimeTicks current_time) const;
- // Resets the current state of the hash to the contents of |hash_state_bytes|.
- virtual bool SetHashState(const std::string& hash_state_bytes);
+ // Log a TYPE_DOWNLOAD_FILE_ERROR NetLog event with |error| and passes error
+ // on through, converting to a |content::DownloadInterruptReason|.
+ content::DownloadInterruptReason LogNetError(
+ const char* operation, net::Error error);
- net::Error ClearStream(net::Error error);
+ // Log the system error in |os_error| and converts it into a
+ // |content::DownloadInterruptReason|.
+ content::DownloadInterruptReason LogSystemError(
+ const char* operation, int os_error);
+
+ // Log a TYPE_DOWNLOAD_FILE_ERROR NetLog event with |os_error| and |reason|.
+ // Returns |reason|.
+ content::DownloadInterruptReason LogInterruptReason(
+ const char* operation, int os_error,
+ content::DownloadInterruptReason reason);
static const size_t kSha256HashLen = 32;
static const unsigned char kEmptySha256Hash[kSha256HashLen];
+ // Full path to the file including the file name.
+ FilePath full_path_;
+
// Source URL for the file being downloaded.
GURL source_url_;
diff --git a/content/browser/download/base_file_linux.cc b/content/browser/download/base_file_linux.cc
new file mode 100644
index 0000000..7f8b717
--- /dev/null
+++ b/content/browser/download/base_file_linux.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/base_file.h"
+
+#include "content/browser/download/file_metadata_linux.h"
+#include "content/public/browser/browser_thread.h"
+
+void BaseFile::AnnotateWithSourceInformation() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+ DCHECK(!detached_);
+
+ content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_);
+}
diff --git a/content/browser/download/base_file_mac.cc b/content/browser/download/base_file_mac.cc
new file mode 100644
index 0000000..88ad5f0
--- /dev/null
+++ b/content/browser/download/base_file_mac.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/base_file.h"
+
+#include "content/browser/download/file_metadata_mac.h"
+#include "content/public/browser/browser_thread.h"
+
+void BaseFile::AnnotateWithSourceInformation() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+ DCHECK(!detached_);
+
+ content::AddQuarantineMetadataToFile(full_path_, source_url_, referrer_url_);
+ content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_);
+}
diff --git a/content/browser/download/base_file_posix.cc b/content/browser/download/base_file_posix.cc
new file mode 100644
index 0000000..b5538bf
--- /dev/null
+++ b/content/browser/download/base_file_posix.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/base_file.h"
+
+#include "base/file_util.h"
+#include "content/public/browser/download_interrupt_reasons.h"
+
+content::DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions(
+ const FilePath& new_path) {
+ // Similarly, on Unix, we're moving a temp file created with permissions 600
+ // to |new_path|. Here, we try to fix up the destination file with appropriate
+ // permissions.
+ struct stat st;
+ // First check the file existence and create an empty file if it doesn't
+ // exist.
+ if (!file_util::PathExists(new_path)) {
+ int write_error = file_util::WriteFile(new_path, "", 0);
+ if (write_error < 0)
+ return LogSystemError("WriteFile", errno);
+ }
+ int stat_error = stat(new_path.value().c_str(), &st);
+ bool stat_succeeded = (stat_error == 0);
+ if (!stat_succeeded)
+ LogSystemError("stat", errno);
+
+ // TODO(estade): Move() falls back to copying and deleting when a simple
+ // rename fails. Copying sucks for large downloads. crbug.com/8737
+ if (!file_util::Move(full_path_, new_path))
+ return LogSystemError("Move", errno);
+
+ if (stat_succeeded) {
+ // On Windows file systems (FAT, NTFS), chmod fails. This is OK.
+ int chmod_error = chmod(new_path.value().c_str(), st.st_mode);
+ if (chmod_error < 0)
+ LogSystemError("chmod", errno);
+ }
+ return content::DOWNLOAD_INTERRUPT_REASON_NONE;
+}
diff --git a/content/browser/download/base_file_unittest.cc b/content/browser/download/base_file_unittest.cc
index 38206e5..e672fbb 100644
--- a/content/browser/download/base_file_unittest.cc
+++ b/content/browser/download/base_file_unittest.cc
@@ -11,10 +11,10 @@
#include "base/string_number_conversions.h"
#include "base/test/test_file_util.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/public/browser/download_interrupt_reasons.h"
#include "crypto/secure_hash.h"
#include "net/base/file_stream.h"
#include "net/base/mock_file_stream.h"
-#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
@@ -44,7 +44,7 @@ class BaseFileTest : public testing::Test {
BaseFileTest()
: expect_file_survives_(false),
expect_in_progress_(true),
- expected_error_(false),
+ expected_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
file_thread_(BrowserThread::FILE, &message_loop_) {
}
@@ -112,20 +112,29 @@ class BaseFileTest : public testing::Test {
net::BoundNetLog()));
}
- int AppendDataToFile(const std::string& data) {
+ bool InitializeFile() {
+ content::DownloadInterruptReason result =
+ base_file_->Initialize(temp_dir_.path());
+ EXPECT_EQ(expected_error_, result);
+ return result == content::DOWNLOAD_INTERRUPT_REASON_NONE;
+ }
+
+ bool AppendDataToFile(const std::string& data) {
EXPECT_EQ(expect_in_progress_, base_file_->in_progress());
- int appended = base_file_->AppendDataToFile(data.data(), data.size());
- if (appended == net::OK)
- EXPECT_TRUE(expect_in_progress_)
- << " appended = " << appended;
+ content::DownloadInterruptReason result =
+ base_file_->AppendDataToFile(data.data(), data.size());
+ if (result == content::DOWNLOAD_INTERRUPT_REASON_NONE)
+ EXPECT_TRUE(expect_in_progress_) << " result = " << result;
+
+ EXPECT_EQ(expected_error_, result);
if (base_file_->in_progress()) {
expected_data_ += data;
- if (!expected_error_) {
+ if (expected_error_ == content::DOWNLOAD_INTERRUPT_REASON_NONE) {
EXPECT_EQ(static_cast<int64>(expected_data_.size()),
base_file_->bytes_so_far());
}
}
- return appended;
+ return result == content::DOWNLOAD_INTERRUPT_REASON_NONE;
}
void set_expected_data(const std::string& data) { expected_data_ = data; }
@@ -143,11 +152,13 @@ class BaseFileTest : public testing::Test {
scoped_ptr<net::FileStream>(),
net::BoundNetLog());
- EXPECT_EQ(net::OK, file.Initialize(temp_dir_.path()));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ file.Initialize(temp_dir_.path()));
file_name = file.full_path();
EXPECT_NE(FilePath::StringType(), file_name.value());
- EXPECT_EQ(net::OK, file.AppendDataToFile(kTestData4, kTestDataLength4));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ file.AppendDataToFile(kTestData4, kTestDataLength4));
// Keep the file from getting deleted when existing_file_name is deleted.
file.Detach();
@@ -166,7 +177,8 @@ class BaseFileTest : public testing::Test {
"",
scoped_ptr<net::FileStream>(),
net::BoundNetLog());
- EXPECT_EQ(net::OK, duplicate_file.Initialize(temp_dir_.path()));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ duplicate_file.Initialize(temp_dir_.path()));
// Write something into it.
duplicate_file.AppendDataToFile(kTestData4, kTestDataLength4);
// Detach the file so it isn't deleted on destruction of |duplicate_file|.
@@ -183,7 +195,7 @@ class BaseFileTest : public testing::Test {
return base_file_->start_tick_;
}
- void set_expected_error(net::Error err) {
+ void set_expected_error(content::DownloadInterruptReason err) {
expected_error_ = err;
}
@@ -210,7 +222,7 @@ class BaseFileTest : public testing::Test {
private:
// Keep track of what data should be saved to the disk file.
std::string expected_data_;
- bool expected_error_;
+ content::DownloadInterruptReason expected_error_;
// Mock file thread to satisfy debug checks in BaseFile.
MessageLoop message_loop_;
@@ -229,7 +241,7 @@ TEST_F(BaseFileTest, CreateDestroy) {
// Cancel the download explicitly.
TEST_F(BaseFileTest, Cancel) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
EXPECT_TRUE(file_util::PathExists(base_file_->full_path()));
base_file_->Cancel();
EXPECT_FALSE(file_util::PathExists(base_file_->full_path()));
@@ -239,8 +251,8 @@ TEST_F(BaseFileTest, Cancel) {
// Write data to the file and detach it, so it doesn't get deleted
// automatically when base_file_ is destructed.
TEST_F(BaseFileTest, WriteAndDetach) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
base_file_->Finish();
base_file_->Detach();
expect_file_survives_ = true;
@@ -256,8 +268,8 @@ TEST_F(BaseFileTest, WriteWithHashAndDetach) {
base::HexEncode(expected_hash.data(), expected_hash.size());
MakeFileWithHash();
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
base_file_->Finish();
std::string hash;
@@ -272,16 +284,17 @@ TEST_F(BaseFileTest, WriteWithHashAndDetach) {
// Rename the file after writing to it, then detach.
TEST_F(BaseFileTest, WriteThenRenameAndDetach) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
FilePath initial_path(base_file_->full_path());
EXPECT_TRUE(file_util::PathExists(initial_path));
FilePath new_path(temp_dir_.path().AppendASCII("NewFile"));
EXPECT_FALSE(file_util::PathExists(new_path));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
- EXPECT_EQ(net::OK, base_file_->Rename(new_path));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ base_file_->Rename(new_path));
EXPECT_FALSE(file_util::PathExists(initial_path));
EXPECT_TRUE(file_util::PathExists(new_path));
@@ -292,17 +305,17 @@ TEST_F(BaseFileTest, WriteThenRenameAndDetach) {
// Write data to the file once.
TEST_F(BaseFileTest, SingleWrite) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
base_file_->Finish();
}
// Write data to the file multiple times.
TEST_F(BaseFileTest, MultipleWrites) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData3));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
+ ASSERT_TRUE(AppendDataToFile(kTestData3));
std::string hash;
EXPECT_FALSE(base_file_->GetHash(&hash));
base_file_->Finish();
@@ -318,10 +331,10 @@ TEST_F(BaseFileTest, SingleWriteWithHash) {
base::HexEncode(expected_hash.data(), expected_hash.size());
MakeFileWithHash();
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
// Can get partial hash states before Finish() is called.
EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str());
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str());
base_file_->Finish();
@@ -343,10 +356,10 @@ TEST_F(BaseFileTest, MultipleWritesWithHash) {
std::string hash;
MakeFileWithHash();
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData3));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
+ ASSERT_TRUE(AppendDataToFile(kTestData3));
// No hash before Finish() is called.
EXPECT_FALSE(base_file_->GetHash(&hash));
base_file_->Finish();
@@ -370,10 +383,10 @@ TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) {
base::HexEncode(expected_hash.data(), expected_hash.size());
MakeFileWithHash();
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
// Write some data
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
// Get the hash state and file name.
std::string hash_state;
hash_state = base_file_->GetHashState();
@@ -389,9 +402,11 @@ TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) {
hash_state,
scoped_ptr<net::FileStream>(),
net::BoundNetLog());
- ASSERT_EQ(net::OK, second_file.Initialize(temp_dir_.path()));
+ ASSERT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ second_file.Initialize(temp_dir_.path()));
std::string data(kTestData3);
- EXPECT_EQ(net::OK, second_file.AppendDataToFile(data.data(), data.size()));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ second_file.AppendDataToFile(data.data(), data.size()));
second_file.Finish();
std::string hash;
@@ -403,16 +418,17 @@ TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) {
// Rename the file after all writes to it.
TEST_F(BaseFileTest, WriteThenRename) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
FilePath initial_path(base_file_->full_path());
EXPECT_TRUE(file_util::PathExists(initial_path));
FilePath new_path(temp_dir_.path().AppendASCII("NewFile"));
EXPECT_FALSE(file_util::PathExists(new_path));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
- EXPECT_EQ(net::OK, base_file_->Rename(new_path));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ base_file_->Rename(new_path));
EXPECT_FALSE(file_util::PathExists(initial_path));
EXPECT_TRUE(file_util::PathExists(new_path));
@@ -421,28 +437,29 @@ TEST_F(BaseFileTest, WriteThenRename) {
// Rename the file while the download is still in progress.
TEST_F(BaseFileTest, RenameWhileInProgress) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
FilePath initial_path(base_file_->full_path());
EXPECT_TRUE(file_util::PathExists(initial_path));
FilePath new_path(temp_dir_.path().AppendASCII("NewFile"));
EXPECT_FALSE(file_util::PathExists(new_path));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
EXPECT_TRUE(base_file_->in_progress());
- EXPECT_EQ(net::OK, base_file_->Rename(new_path));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
+ base_file_->Rename(new_path));
EXPECT_FALSE(file_util::PathExists(initial_path));
EXPECT_TRUE(file_util::PathExists(new_path));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
base_file_->Finish();
}
// Test that a failed rename reports the correct error.
TEST_F(BaseFileTest, RenameWithError) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
// TestDir is a subdirectory in |temp_dir_| that we will make read-only so
// that the rename will fail.
@@ -455,7 +472,8 @@ TEST_F(BaseFileTest, RenameWithError) {
{
file_util::PermissionRestorer restore_permissions_for(test_dir);
ASSERT_TRUE(file_util::MakeFileUnwritable(test_dir));
- EXPECT_EQ(net::ERR_ACCESS_DENIED, base_file_->Rename(new_path));
+ EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
+ base_file_->Rename(new_path));
}
base_file_->Finish();
@@ -488,12 +506,12 @@ TEST_F(BaseFileTest, MultipleWritesWithError) {
"",
mock_file_stream_scoped_ptr.Pass(),
net::BoundNetLog()));
- EXPECT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
mock_file_stream->set_forced_error(net::ERR_ACCESS_DENIED);
- set_expected_error(net::ERR_ACCESS_DENIED);
- ASSERT_NE(net::OK, AppendDataToFile(kTestData3));
+ set_expected_error(content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
+ ASSERT_FALSE(AppendDataToFile(kTestData3));
std::string hash;
EXPECT_FALSE(base_file_->GetHash(&hash));
base_file_->Finish();
@@ -502,19 +520,20 @@ TEST_F(BaseFileTest, MultipleWritesWithError) {
// Try to write to uninitialized file.
TEST_F(BaseFileTest, UninitializedFile) {
expect_in_progress_ = false;
- EXPECT_EQ(net::ERR_INVALID_HANDLE, AppendDataToFile(kTestData1));
+ set_expected_error(content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
+ EXPECT_FALSE(AppendDataToFile(kTestData1));
}
// Create two |BaseFile|s with the same file, and attempt to write to both.
// Overwrite base_file_ with another file with the same name and
// non-zero contents, and make sure the last file to close 'wins'.
TEST_F(BaseFileTest, DuplicateBaseFile) {
- EXPECT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
// Create another |BaseFile| referring to the file that |base_file_| owns.
CreateFileWithName(base_file_->full_path());
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
base_file_->Finish();
}
@@ -535,13 +554,13 @@ TEST_F(BaseFileTest, AppendToBaseFile) {
scoped_ptr<net::FileStream>(),
net::BoundNetLog()));
- EXPECT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
const FilePath file_name = base_file_->full_path();
EXPECT_NE(FilePath::StringType(), file_name.value());
// Write into the file.
- EXPECT_EQ(net::OK, AppendDataToFile(kTestData1));
+ EXPECT_TRUE(AppendDataToFile(kTestData1));
base_file_->Finish();
base_file_->Detach();
@@ -570,16 +589,15 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) {
net::BoundNetLog()));
expect_in_progress_ = false;
-
- int init_error = base_file_->Initialize(temp_dir_.path());
- DVLOG(1) << " init_error = " << init_error;
- EXPECT_NE(net::OK, init_error);
+ set_expected_error(content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
+ EXPECT_FALSE(InitializeFile());
const FilePath file_name = base_file_->full_path();
EXPECT_NE(FilePath::StringType(), file_name.value());
// Write into the file.
- EXPECT_NE(net::OK, AppendDataToFile(kTestData1));
+ set_expected_error(content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
+ EXPECT_FALSE(AppendDataToFile(kTestData1));
base_file_->Finish();
base_file_->Detach();
@@ -596,7 +614,7 @@ TEST_F(BaseFileTest, IsEmptyHash) {
// Test that calculating speed after no writes.
TEST_F(BaseFileTest, SpeedWithoutWrite) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
base::TimeTicks current = StartTick() + kElapsedTimeDelta;
ASSERT_EQ(0, CurrentSpeedAtTime(current));
base_file_->Finish();
@@ -604,8 +622,8 @@ TEST_F(BaseFileTest, SpeedWithoutWrite) {
// Test that calculating speed after a single write.
TEST_F(BaseFileTest, SpeedAfterSingleWrite) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
base::TimeTicks current = StartTick() + kElapsedTimeDelta;
int64 expected_speed = kTestDataLength1 / kElapsedTimeSeconds;
ASSERT_EQ(expected_speed, CurrentSpeedAtTime(current));
@@ -614,11 +632,11 @@ TEST_F(BaseFileTest, SpeedAfterSingleWrite) {
// Test that calculating speed after a multiple writes.
TEST_F(BaseFileTest, SpeedAfterMultipleWrite) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData2));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData3));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData4));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
+ ASSERT_TRUE(AppendDataToFile(kTestData2));
+ ASSERT_TRUE(AppendDataToFile(kTestData3));
+ ASSERT_TRUE(AppendDataToFile(kTestData4));
base::TimeTicks current = StartTick() + kElapsedTimeDelta;
int64 expected_speed = (kTestDataLength1 + kTestDataLength2 +
kTestDataLength3 + kTestDataLength4) / kElapsedTimeSeconds;
@@ -628,8 +646,8 @@ TEST_F(BaseFileTest, SpeedAfterMultipleWrite) {
// Test that calculating speed after no delay - should not divide by 0.
TEST_F(BaseFileTest, SpeedAfterNoElapsedTime) {
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
- ASSERT_EQ(net::OK, AppendDataToFile(kTestData1));
+ ASSERT_TRUE(InitializeFile());
+ ASSERT_TRUE(AppendDataToFile(kTestData1));
ASSERT_EQ(0, CurrentSpeedAtTime(StartTick()));
base_file_->Finish();
}
@@ -637,7 +655,7 @@ TEST_F(BaseFileTest, SpeedAfterNoElapsedTime) {
// Test that a temporary file is created in the default download directory.
TEST_F(BaseFileTest, CreatedInDefaultDirectory) {
ASSERT_TRUE(base_file_->full_path().empty());
- ASSERT_EQ(net::OK, base_file_->Initialize(temp_dir_.path()));
+ ASSERT_TRUE(InitializeFile());
EXPECT_FALSE(base_file_->full_path().empty());
// On Windows, CreateTemporaryFileInDir() will cause a path with short names
diff --git a/content/browser/download/base_file_win.cc b/content/browser/download/base_file_win.cc
new file mode 100644
index 0000000..b3b7e18
--- /dev/null
+++ b/content/browser/download/base_file_win.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/base_file.h"
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/download/download_interrupt_reasons_impl.h"
+#include "content/browser/safe_util_win.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// Maps the result of a call to |SHFileOperation()| onto a
+// |content::DownloadInterruptReason|.
+//
+// These return codes are *old* (as in, DOS era), and specific to
+// |SHFileOperation()|.
+// They do not appear in any windows header.
+//
+// See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx.
+content::DownloadInterruptReason MapShFileOperationCodes(int code) {
+ // Check these pre-Win32 error codes first, then check for matches
+ // in Winerror.h.
+
+ switch (code) {
+ // The source and destination files are the same file.
+ // DE_SAMEFILE == 0x71
+ case 0x71: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The operation was canceled by the user, or silently canceled if the
+ // appropriate flags were supplied to SHFileOperation.
+ // DE_OPCANCELLED == 0x75
+ case 0x75: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Security settings denied access to the source.
+ // DE_ACCESSDENIEDSRC == 0x78
+ case 0x78: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source or destination path exceeded or would exceed MAX_PATH.
+ // DE_PATHTOODEEP == 0x79
+ case 0x79: return content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
+
+ // The path in the source or destination or both was invalid.
+ // DE_INVALIDFILES == 0x7C
+ case 0x7C: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination path is an existing file.
+ // DE_FLDDESTISFILE == 0x7E
+ case 0x7E: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination path is an existing folder.
+ // DE_FILEDESTISFLD == 0x80
+ case 0x80: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The name of the file exceeds MAX_PATH.
+ // DE_FILENAMETOOLONG == 0x81
+ case 0x81: return content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
+
+ // The destination is a read-only CD-ROM, possibly unformatted.
+ // DE_DEST_IS_CDROM == 0x82
+ case 0x82: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The destination is a read-only DVD, possibly unformatted.
+ // DE_DEST_IS_DVD == 0x83
+ case 0x83: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The destination is a writable CD-ROM, possibly unformatted.
+ // DE_DEST_IS_CDRECORD == 0x84
+ case 0x84: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The file involved in the operation is too large for the destination
+ // media or file system.
+ // DE_FILE_TOO_LARGE == 0x85
+ case 0x85: return content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE;
+
+ // The source is a read-only CD-ROM, possibly unformatted.
+ // DE_SRC_IS_CDROM == 0x86
+ case 0x86: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source is a read-only DVD, possibly unformatted.
+ // DE_SRC_IS_DVD == 0x87
+ case 0x87: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source is a writable CD-ROM, possibly unformatted.
+ // DE_SRC_IS_CDRECORD == 0x88
+ case 0x88: return content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // MAX_PATH was exceeded during the operation.
+ // DE_ERROR_MAX == 0xB7
+ case 0xB7: return content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
+
+ // An unspecified error occurred on the destination.
+ // XE_ERRORONDEST == 0x10000
+ case 0x10000: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Multiple file paths were specified in the source buffer, but only one
+ // destination file path.
+ // DE_MANYSRC1DEST == 0x72
+ case 0x72: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Rename operation was specified but the destination path is
+ // a different directory. Use the move operation instead.
+ // DE_DIFFDIR == 0x73
+ case 0x73: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The source is a root directory, which cannot be moved or renamed.
+ // DE_ROOTDIR == 0x74
+ case 0x74: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination is a subtree of the source.
+ // DE_DESTSUBTREE == 0x76
+ case 0x76: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The operation involved multiple destination paths,
+ // which can fail in the case of a move operation.
+ // DE_MANYDEST == 0x7A
+ case 0x7A: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The source and destination have the same parent folder.
+ // DE_DESTSAMETREE == 0x7D
+ case 0x7D: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // An unknown error occurred. This is typically due to an invalid path in
+ // the source or destination. This error does not occur on Windows Vista
+ // and later.
+ // DE_UNKNOWN_ERROR == 0x402
+ case 0x402: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Destination is a root directory and cannot be renamed.
+ // DE_ROOTDIR | ERRORONDEST == 0x10074
+ case 0x10074: return content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+ }
+
+ // If not one of the above codes, it should be a standard Windows error code.
+ return content::ConvertNetErrorToInterruptReason(
+ net::MapSystemError(code), content::DOWNLOAD_INTERRUPT_FROM_DISK);
+}
+
+} // namespace
+
+// Renames a file using the SHFileOperation API to ensure that the target file
+// gets the correct default security descriptor in the new path.
+// Returns a network error, or net::OK for success.
+content::DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions(
+ const FilePath& new_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // The parameters to SHFileOperation must be terminated with 2 NULL chars.
+ FilePath::StringType source = full_path_.value();
+ FilePath::StringType target = new_path.value();
+
+ source.append(1, L'\0');
+ target.append(1, L'\0');
+
+ SHFILEOPSTRUCT move_info = {0};
+ move_info.wFunc = FO_MOVE;
+ move_info.pFrom = source.c_str();
+ move_info.pTo = target.c_str();
+ move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI |
+ FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
+
+ int result = SHFileOperation(&move_info);
+ content::DownloadInterruptReason interrupt_reason =
+ content::DOWNLOAD_INTERRUPT_REASON_NONE;
+
+ if (result == 0 && move_info.fAnyOperationsAborted)
+ interrupt_reason = content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+ else if (result != 0)
+ interrupt_reason = MapShFileOperationCodes(result);
+
+ if (interrupt_reason != content::DOWNLOAD_INTERRUPT_REASON_NONE)
+ return LogInterruptReason("SHFileOperation", result, interrupt_reason);
+ return interrupt_reason;
+}
+
+void BaseFile::AnnotateWithSourceInformation() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+ DCHECK(!detached_);
+
+ // Sets the Zone to tell Windows that this file comes from the internet.
+ // We ignore the return value because a failure is not fatal.
+ win_util::SetInternetZoneIdentifier(full_path_,
+ UTF8ToWide(source_url_.spec()));
+}
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc
index 1020816..c018599 100644
--- a/content/browser/download/download_file_impl.cc
+++ b/content/browser/download/download_file_impl.cc
@@ -66,12 +66,11 @@ void DownloadFileImpl::Initialize(const InitializeCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
update_timer_.reset(new base::RepeatingTimer<DownloadFileImpl>());
- net::Error net_result = file_.Initialize(default_download_directory_);
- if (net_result != net::OK) {
+ content::DownloadInterruptReason result =
+ file_.Initialize(default_download_directory_);
+ if (result != content::DOWNLOAD_INTERRUPT_REASON_NONE) {
BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE, base::Bind(
- callback, content::ConvertNetErrorToInterruptReason(
- net_result, content::DOWNLOAD_INTERRUPT_FROM_DISK)));
+ BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
return;
}
@@ -99,9 +98,7 @@ content::DownloadInterruptReason DownloadFileImpl::AppendDataToFile(
base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
this, &DownloadFileImpl::SendUpdate);
}
- return content::ConvertNetErrorToInterruptReason(
- file_.AppendDataToFile(data, data_len),
- content::DOWNLOAD_INTERRUPT_FROM_DISK);
+ return file_.AppendDataToFile(data, data_len);
}
void DownloadFileImpl::Rename(const FilePath& full_path,
@@ -120,10 +117,8 @@ void DownloadFileImpl::Rename(const FilePath& full_path,
}
}
- net::Error rename_error = file_.Rename(new_path);
- content::DownloadInterruptReason reason(
- content::DOWNLOAD_INTERRUPT_REASON_NONE);
- if (net::OK != rename_error) {
+ content::DownloadInterruptReason reason = file_.Rename(new_path);
+ if (reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) {
// Make sure our information is updated, since we're about to
// error out.
SendUpdate();
@@ -131,10 +126,6 @@ void DownloadFileImpl::Rename(const FilePath& full_path,
// Null out callback so that we don't do any more stream processing.
stream_reader_->RegisterCallback(base::Closure());
- reason =
- content::ConvertNetErrorToInterruptReason(
- rename_error,
- content::DOWNLOAD_INTERRUPT_FROM_DISK);
new_path.clear();
}
diff --git a/content/browser/download/download_net_log_parameters.cc b/content/browser/download/download_net_log_parameters.cc
index 00a9d5d..c0d42cf 100644
--- a/content/browser/download/download_net_log_parameters.cc
+++ b/content/browser/download/download_net_log_parameters.cc
@@ -183,4 +183,19 @@ base::Value* FileErrorCallback(const char* operation,
return dict;
}
+base::Value* FileInterruptedCallback(const char* operation,
+ int os_error,
+ content::DownloadInterruptReason reason,
+ net::NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+
+ dict->SetString("operation", operation);
+ if (os_error != 0)
+ dict->SetInteger("os_error", os_error);
+ dict->SetString("interrupt_reason", InterruptReasonDebugString(reason));
+
+ return dict;
+}
+
+
} // namespace download_net_logs
diff --git a/content/browser/download/download_net_log_parameters.h b/content/browser/download/download_net_log_parameters.h
index 5167772..1d94233 100644
--- a/content/browser/download/download_net_log_parameters.h
+++ b/content/browser/download/download_net_log_parameters.h
@@ -80,6 +80,11 @@ base::Value* FileErrorCallback(const char* operation,
net::Error net_error,
net::NetLog::LogLevel log_level);
+base::Value* FileInterruptedCallback(const char* operation,
+ int os_error,
+ content::DownloadInterruptReason reason,
+ net::NetLog::LogLevel log_level);
+
} // namespace download_net_logs
#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_NET_LOG_PARAMETERS_H_
diff --git a/content/browser/download/save_file.cc b/content/browser/download/save_file.cc
index cd0acba..91fe2ac 100644
--- a/content/browser/download/save_file.cc
+++ b/content/browser/download/save_file.cc
@@ -34,15 +34,16 @@ SaveFile::~SaveFile() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
}
-net::Error SaveFile::Initialize() {
+content::DownloadInterruptReason SaveFile::Initialize() {
return file_.Initialize(FilePath());
}
-net::Error SaveFile::AppendDataToFile(const char* data, size_t data_len) {
+content::DownloadInterruptReason SaveFile::AppendDataToFile(const char* data,
+ size_t data_len) {
return file_.AppendDataToFile(data, data_len);
}
-net::Error SaveFile::Rename(const FilePath& full_path) {
+content::DownloadInterruptReason SaveFile::Rename(const FilePath& full_path) {
return file_.Rename(full_path);
}
diff --git a/content/browser/download/save_file.h b/content/browser/download/save_file.h
index cf0bf64..fdcc33f 100644
--- a/content/browser/download/save_file.h
+++ b/content/browser/download/save_file.h
@@ -24,9 +24,10 @@ class SaveFile {
virtual ~SaveFile();
// BaseFile delegated functions.
- net::Error Initialize();
- net::Error AppendDataToFile(const char* data, size_t data_len);
- net::Error Rename(const FilePath& full_path);
+ content::DownloadInterruptReason Initialize();
+ content::DownloadInterruptReason AppendDataToFile(const char* data,
+ size_t data_len);
+ content::DownloadInterruptReason Rename(const FilePath& full_path);
void Detach();
void Cancel();
void Finish();
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index d4fd8bf..52b24cb 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -242,7 +242,7 @@ void SaveFileManager::UpdateSaveProgress(int save_id,
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
SaveFile* save_file = LookupSaveFile(save_id);
if (save_file) {
- net::Error write_success =
+ content::DownloadInterruptReason reason =
save_file->AppendDataToFile(data->data(), data_len);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@@ -250,7 +250,7 @@ void SaveFileManager::UpdateSaveProgress(int save_id,
this,
save_file->save_id(),
save_file->BytesSoFar(),
- write_success == net::OK));
+ reason == content::DOWNLOAD_INTERRUPT_REASON_NONE));
}
}