summaryrefslogtreecommitdiff
path: root/content/browser/download/base_file_win.cc
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
committerTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
commit5821806d5e7f356e8fa4b058a389a808ea183019 (patch)
treee19f4793aac92e2c0d9a01087019a60d6657d838 /content/browser/download/base_file_win.cc
parent8e79a8efe247f109aafd917a69e8a392961b3687 (diff)
downloadchromium_org-5821806d5e7f356e8fa4b058a389a808ea183019.tar.gz
Merge from Chromium at DEPS revision r167172
This commit was generated by merge_to_master.py. Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
Diffstat (limited to 'content/browser/download/base_file_win.cc')
-rw-r--r--content/browser/download/base_file_win.cc244
1 files changed, 244 insertions, 0 deletions
diff --git a/content/browser/download/base_file_win.cc b/content/browser/download/base_file_win.cc
new file mode 100644
index 0000000000..4714439a84
--- /dev/null
+++ b/content/browser/download/base_file_win.cc
@@ -0,0 +1,244 @@
+// 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/file_util.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_stats.h"
+#include "content/browser/safe_util_win.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+namespace {
+
+// Maps the result of a call to |SHFileOperation()| onto a
+// |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.
+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 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 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Security settings denied access to the source.
+ // DE_ACCESSDENIEDSRC == 0x78
+ case 0x78: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source or destination path exceeded or would exceed MAX_PATH.
+ // DE_PATHTOODEEP == 0x79
+ case 0x79: return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
+
+ // The path in the source or destination or both was invalid.
+ // DE_INVALIDFILES == 0x7C
+ case 0x7C: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination path is an existing file.
+ // DE_FLDDESTISFILE == 0x7E
+ case 0x7E: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination path is an existing folder.
+ // DE_FILEDESTISFLD == 0x80
+ case 0x80: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The name of the file exceeds MAX_PATH.
+ // DE_FILENAMETOOLONG == 0x81
+ case 0x81: return 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 DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The destination is a read-only DVD, possibly unformatted.
+ // DE_DEST_IS_DVD == 0x83
+ case 0x83: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The destination is a writable CD-ROM, possibly unformatted.
+ // DE_DEST_IS_CDRECORD == 0x84
+ case 0x84: return 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 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE;
+
+ // The source is a read-only CD-ROM, possibly unformatted.
+ // DE_SRC_IS_CDROM == 0x86
+ case 0x86: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source is a read-only DVD, possibly unformatted.
+ // DE_SRC_IS_DVD == 0x87
+ case 0x87: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // The source is a writable CD-ROM, possibly unformatted.
+ // DE_SRC_IS_CDRECORD == 0x88
+ case 0x88: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
+
+ // MAX_PATH was exceeded during the operation.
+ // DE_ERROR_MAX == 0xB7
+ case 0xB7: return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
+
+ // An unspecified error occurred on the destination.
+ // XE_ERRORONDEST == 0x10000
+ case 0x10000: return 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 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 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The source is a root directory, which cannot be moved or renamed.
+ // DE_ROOTDIR == 0x74
+ case 0x74: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The destination is a subtree of the source.
+ // DE_DESTSUBTREE == 0x76
+ case 0x76: return 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 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // The source and destination have the same parent folder.
+ // DE_DESTSAMETREE == 0x7D
+ case 0x7D: return 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 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+
+ // Destination is a root directory and cannot be renamed.
+ // DE_ROOTDIR | ERRORONDEST == 0x10074
+ case 0x10074: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+ }
+
+ // If not one of the above codes, it should be a standard Windows error code.
+ return ConvertNetErrorToInterruptReason(
+ net::MapSystemError(code), DOWNLOAD_INTERRUPT_FROM_DISK);
+}
+
+// Maps a return code from ScanAndSaveDownloadedFile() to a
+// DownloadInterruptReason. The return code in |result| is usually from the
+// final IAttachmentExecute::Save() call.
+DownloadInterruptReason MapScanAndSaveErrorCodeToInterruptReason(
+ HRESULT result) {
+ if (SUCCEEDED(result))
+ return DOWNLOAD_INTERRUPT_REASON_NONE;
+
+ switch (result) {
+ case INET_E_SECURITY_PROBLEM: // 0x800c000e
+ // This is returned if the download was blocked due to security
+ // restrictions. E.g. if the source URL was in the Restricted Sites zone
+ // and downloads are blocked on that zone, then the download would be
+ // deleted and this error code is returned.
+ return DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED;
+
+ case E_FAIL: // 0x80004005
+ // Returned if an anti-virus product reports an infection in the
+ // downloaded file during IAE::Save().
+ return DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED;
+
+ default:
+ // Any other error that occurs during IAttachmentExecute::Save() likely
+ // indicates a problem with the security check, but not necessarily the
+ // download. See http://crbug.com/153212.
+ return DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED;
+ }
+}
+
+} // 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.
+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);
+ DownloadInterruptReason interrupt_reason = DOWNLOAD_INTERRUPT_REASON_NONE;
+
+ if (result == 0 && move_info.fAnyOperationsAborted)
+ interrupt_reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
+ else if (result != 0)
+ interrupt_reason = MapShFileOperationCodes(result);
+
+ if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE)
+ return LogInterruptReason("SHFileOperation", result, interrupt_reason);
+ return interrupt_reason;
+}
+
+DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(!detached_);
+
+ bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED);
+ DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
+ HRESULT hr = ScanAndSaveDownloadedFile(full_path_, source_url_);
+
+ // If the download file is missing after the call, then treat this as an
+ // interrupted download.
+ //
+ // If the ScanAndSaveDownloadedFile() call failed, but the downloaded file is
+ // still around, then don't interrupt the download. Attachment Execution
+ // Services deletes the submitted file if the downloaded file is blocked by
+ // policy or if it was found to be infected.
+ //
+ // If the file is still there, then the error could be due to AES not being
+ // available or some other error during the AES invocation. In either case,
+ // we don't surface the error to the user.
+ if (!file_util::PathExists(full_path_)) {
+ DCHECK(FAILED(hr));
+ result = MapScanAndSaveErrorCodeToInterruptReason(hr);
+ if (result == DOWNLOAD_INTERRUPT_REASON_NONE) {
+ RecordDownloadCount(FILE_MISSING_AFTER_SUCCESSFUL_SCAN_COUNT);
+ result = DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED;
+ }
+ LogInterruptReason("ScanAndSaveDownloadedFile", hr, result);
+ }
+ bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED);
+ return result;
+}
+
+} // namespace content