base_file_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/download/base_file.h" 6 7#include <windows.h> 8#include <shellapi.h> 9 10#include "base/file_util.h" 11#include "base/threading/thread_restrictions.h" 12#include "base/utf_string_conversions.h" 13#include "content/browser/download/download_interrupt_reasons_impl.h" 14#include "content/browser/download/download_stats.h" 15#include "content/browser/safe_util_win.h" 16#include "content/public/browser/browser_thread.h" 17 18namespace content { 19namespace { 20 21// Maps the result of a call to |SHFileOperation()| onto a 22// |DownloadInterruptReason|. 23// 24// These return codes are *old* (as in, DOS era), and specific to 25// |SHFileOperation()|. 26// They do not appear in any windows header. 27// 28// See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx. 29DownloadInterruptReason MapShFileOperationCodes(int code) { 30 // Check these pre-Win32 error codes first, then check for matches 31 // in Winerror.h. 32 33 switch (code) { 34 // The source and destination files are the same file. 35 // DE_SAMEFILE == 0x71 36 case 0x71: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 37 38 // The operation was canceled by the user, or silently canceled if the 39 // appropriate flags were supplied to SHFileOperation. 40 // DE_OPCANCELLED == 0x75 41 case 0x75: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 42 43 // Security settings denied access to the source. 44 // DE_ACCESSDENIEDSRC == 0x78 45 case 0x78: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 46 47 // The source or destination path exceeded or would exceed MAX_PATH. 48 // DE_PATHTOODEEP == 0x79 49 case 0x79: return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; 50 51 // The path in the source or destination or both was invalid. 52 // DE_INVALIDFILES == 0x7C 53 case 0x7C: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 54 55 // The destination path is an existing file. 56 // DE_FLDDESTISFILE == 0x7E 57 case 0x7E: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 58 59 // The destination path is an existing folder. 60 // DE_FILEDESTISFLD == 0x80 61 case 0x80: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 62 63 // The name of the file exceeds MAX_PATH. 64 // DE_FILENAMETOOLONG == 0x81 65 case 0x81: return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; 66 67 // The destination is a read-only CD-ROM, possibly unformatted. 68 // DE_DEST_IS_CDROM == 0x82 69 case 0x82: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 70 71 // The destination is a read-only DVD, possibly unformatted. 72 // DE_DEST_IS_DVD == 0x83 73 case 0x83: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 74 75 // The destination is a writable CD-ROM, possibly unformatted. 76 // DE_DEST_IS_CDRECORD == 0x84 77 case 0x84: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 78 79 // The file involved in the operation is too large for the destination 80 // media or file system. 81 // DE_FILE_TOO_LARGE == 0x85 82 case 0x85: return DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE; 83 84 // The source is a read-only CD-ROM, possibly unformatted. 85 // DE_SRC_IS_CDROM == 0x86 86 case 0x86: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 87 88 // The source is a read-only DVD, possibly unformatted. 89 // DE_SRC_IS_DVD == 0x87 90 case 0x87: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 91 92 // The source is a writable CD-ROM, possibly unformatted. 93 // DE_SRC_IS_CDRECORD == 0x88 94 case 0x88: return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 95 96 // MAX_PATH was exceeded during the operation. 97 // DE_ERROR_MAX == 0xB7 98 case 0xB7: return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; 99 100 // An unspecified error occurred on the destination. 101 // XE_ERRORONDEST == 0x10000 102 case 0x10000: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 103 104 // Multiple file paths were specified in the source buffer, but only one 105 // destination file path. 106 // DE_MANYSRC1DEST == 0x72 107 case 0x72: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 108 109 // Rename operation was specified but the destination path is 110 // a different directory. Use the move operation instead. 111 // DE_DIFFDIR == 0x73 112 case 0x73: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 113 114 // The source is a root directory, which cannot be moved or renamed. 115 // DE_ROOTDIR == 0x74 116 case 0x74: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 117 118 // The destination is a subtree of the source. 119 // DE_DESTSUBTREE == 0x76 120 case 0x76: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 121 122 // The operation involved multiple destination paths, 123 // which can fail in the case of a move operation. 124 // DE_MANYDEST == 0x7A 125 case 0x7A: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 126 127 // The source and destination have the same parent folder. 128 // DE_DESTSAMETREE == 0x7D 129 case 0x7D: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 130 131 // An unknown error occurred. This is typically due to an invalid path in 132 // the source or destination. This error does not occur on Windows Vista 133 // and later. 134 // DE_UNKNOWN_ERROR == 0x402 135 case 0x402: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 136 137 // Destination is a root directory and cannot be renamed. 138 // DE_ROOTDIR | ERRORONDEST == 0x10074 139 case 0x10074: return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 140 } 141 142 // If not one of the above codes, it should be a standard Windows error code. 143 return ConvertNetErrorToInterruptReason( 144 net::MapSystemError(code), DOWNLOAD_INTERRUPT_FROM_DISK); 145} 146 147// Maps a return code from ScanAndSaveDownloadedFile() to a 148// DownloadInterruptReason. The return code in |result| is usually from the 149// final IAttachmentExecute::Save() call. 150DownloadInterruptReason MapScanAndSaveErrorCodeToInterruptReason( 151 HRESULT result) { 152 if (SUCCEEDED(result)) 153 return DOWNLOAD_INTERRUPT_REASON_NONE; 154 155 switch (result) { 156 case INET_E_SECURITY_PROBLEM: // 0x800c000e 157 // This is returned if the download was blocked due to security 158 // restrictions. E.g. if the source URL was in the Restricted Sites zone 159 // and downloads are blocked on that zone, then the download would be 160 // deleted and this error code is returned. 161 return DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED; 162 163 case E_FAIL: // 0x80004005 164 // Returned if an anti-virus product reports an infection in the 165 // downloaded file during IAE::Save(). 166 return DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED; 167 168 default: 169 // Any other error that occurs during IAttachmentExecute::Save() likely 170 // indicates a problem with the security check, but not necessarily the 171 // download. See http://crbug.com/153212. 172 return DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED; 173 } 174} 175 176} // namespace 177 178// Renames a file using the SHFileOperation API to ensure that the target file 179// gets the correct default security descriptor in the new path. 180// Returns a network error, or net::OK for success. 181DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions( 182 const FilePath& new_path) { 183 base::ThreadRestrictions::AssertIOAllowed(); 184 185 // The parameters to SHFileOperation must be terminated with 2 NULL chars. 186 FilePath::StringType source = full_path_.value(); 187 FilePath::StringType target = new_path.value(); 188 189 source.append(1, L'\0'); 190 target.append(1, L'\0'); 191 192 SHFILEOPSTRUCT move_info = {0}; 193 move_info.wFunc = FO_MOVE; 194 move_info.pFrom = source.c_str(); 195 move_info.pTo = target.c_str(); 196 move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | 197 FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS; 198 199 int result = SHFileOperation(&move_info); 200 DownloadInterruptReason interrupt_reason = DOWNLOAD_INTERRUPT_REASON_NONE; 201 202 if (result == 0 && move_info.fAnyOperationsAborted) 203 interrupt_reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; 204 else if (result != 0) 205 interrupt_reason = MapShFileOperationCodes(result); 206 207 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) 208 return LogInterruptReason("SHFileOperation", result, interrupt_reason); 209 return interrupt_reason; 210} 211 212DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 214 DCHECK(!detached_); 215 216 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED); 217 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; 218 HRESULT hr = ScanAndSaveDownloadedFile(full_path_, source_url_); 219 220 // If the download file is missing after the call, then treat this as an 221 // interrupted download. 222 // 223 // If the ScanAndSaveDownloadedFile() call failed, but the downloaded file is 224 // still around, then don't interrupt the download. Attachment Execution 225 // Services deletes the submitted file if the downloaded file is blocked by 226 // policy or if it was found to be infected. 227 // 228 // If the file is still there, then the error could be due to AES not being 229 // available or some other error during the AES invocation. In either case, 230 // we don't surface the error to the user. 231 if (!file_util::PathExists(full_path_)) { 232 DCHECK(FAILED(hr)); 233 result = MapScanAndSaveErrorCodeToInterruptReason(hr); 234 if (result == DOWNLOAD_INTERRUPT_REASON_NONE) { 235 RecordDownloadCount(FILE_MISSING_AFTER_SUCCESSFUL_SCAN_COUNT); 236 result = DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED; 237 } 238 LogInterruptReason("ScanAndSaveDownloadedFile", hr, result); 239 } 240 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED); 241 return result; 242} 243 244} // namespace content 245