Remove PlatformFile from profile_browsertest
[chromium-blink-merge.git] / content / browser / download / base_file_win.cc
blob252f0495d7da5ba1446d1a9a766849e873fc37dc
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.
5 #include "content/browser/download/base_file.h"
7 #include <windows.h>
8 #include <cguid.h>
9 #include <objbase.h>
10 #include <shellapi.h>
12 #include "base/file_util.h"
13 #include "base/guid.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "content/browser/download/download_interrupt_reasons_impl.h"
18 #include "content/browser/download/download_stats.h"
19 #include "content/browser/safe_util_win.h"
20 #include "content/public/browser/browser_thread.h"
22 namespace content {
23 namespace {
25 const int kAllSpecialShFileOperationCodes[] = {
26 // Should be kept in sync with the case statement below.
27 ERROR_ACCESS_DENIED,
28 0x71,
29 0x72,
30 0x73,
31 0x74,
32 0x75,
33 0x76,
34 0x78,
35 0x79,
36 0x7A,
37 0x7C,
38 0x7D,
39 0x7E,
40 0x80,
41 0x81,
42 0x82,
43 0x83,
44 0x84,
45 0x85,
46 0x86,
47 0x87,
48 0x88,
49 0xB7,
50 0x402,
51 0x10000,
52 0x10074,
55 // Maps the result of a call to |SHFileOperation()| onto a
56 // |DownloadInterruptReason|.
58 // These return codes are *old* (as in, DOS era), and specific to
59 // |SHFileOperation()|.
60 // They do not appear in any windows header.
62 // See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx.
63 DownloadInterruptReason MapShFileOperationCodes(int code) {
64 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
66 // Check these pre-Win32 error codes first, then check for matches
67 // in Winerror.h.
68 // This switch statement should be kept in sync with the list of codes
69 // above.
70 switch (code) {
71 // Not a pre-Win32 error code; here so that this particular
72 // case shows up in our histograms. This is redundant with the
73 // mapping function net::MapSystemError used later.
74 case ERROR_ACCESS_DENIED: // Access is denied.
75 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
76 break;
78 // The source and destination files are the same file.
79 // DE_SAMEFILE == 0x71
80 case 0x71:
81 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
82 break;
84 // The operation was canceled by the user, or silently canceled if the
85 // appropriate flags were supplied to SHFileOperation.
86 // DE_OPCANCELLED == 0x75
87 case 0x75:
88 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
89 break;
91 // Security settings denied access to the source.
92 // DE_ACCESSDENIEDSRC == 0x78
93 case 0x78:
94 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
95 break;
97 // The source or destination path exceeded or would exceed MAX_PATH.
98 // DE_PATHTOODEEP == 0x79
99 case 0x79:
100 result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
101 break;
103 // The path in the source or destination or both was invalid.
104 // DE_INVALIDFILES == 0x7C
105 case 0x7C:
106 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
107 break;
109 // The destination path is an existing file.
110 // DE_FLDDESTISFILE == 0x7E
111 case 0x7E:
112 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
113 break;
115 // The destination path is an existing folder.
116 // DE_FILEDESTISFLD == 0x80
117 case 0x80:
118 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
119 break;
121 // The name of the file exceeds MAX_PATH.
122 // DE_FILENAMETOOLONG == 0x81
123 case 0x81:
124 result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
125 break;
127 // The destination is a read-only CD-ROM, possibly unformatted.
128 // DE_DEST_IS_CDROM == 0x82
129 case 0x82:
130 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
131 break;
133 // The destination is a read-only DVD, possibly unformatted.
134 // DE_DEST_IS_DVD == 0x83
135 case 0x83:
136 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
137 break;
139 // The destination is a writable CD-ROM, possibly unformatted.
140 // DE_DEST_IS_CDRECORD == 0x84
141 case 0x84:
142 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
143 break;
145 // The file involved in the operation is too large for the destination
146 // media or file system.
147 // DE_FILE_TOO_LARGE == 0x85
148 case 0x85:
149 result = DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE;
150 break;
152 // The source is a read-only CD-ROM, possibly unformatted.
153 // DE_SRC_IS_CDROM == 0x86
154 case 0x86:
155 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
156 break;
158 // The source is a read-only DVD, possibly unformatted.
159 // DE_SRC_IS_DVD == 0x87
160 case 0x87:
161 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
162 break;
164 // The source is a writable CD-ROM, possibly unformatted.
165 // DE_SRC_IS_CDRECORD == 0x88
166 case 0x88:
167 result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
168 break;
170 // MAX_PATH was exceeded during the operation.
171 // DE_ERROR_MAX == 0xB7
172 case 0xB7:
173 result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG;
174 break;
176 // An unspecified error occurred on the destination.
177 // XE_ERRORONDEST == 0x10000
178 case 0x10000:
179 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
180 break;
182 // Multiple file paths were specified in the source buffer, but only one
183 // destination file path.
184 // DE_MANYSRC1DEST == 0x72
185 case 0x72:
186 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
187 break;
189 // Rename operation was specified but the destination path is
190 // a different directory. Use the move operation instead.
191 // DE_DIFFDIR == 0x73
192 case 0x73:
193 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
194 break;
196 // The source is a root directory, which cannot be moved or renamed.
197 // DE_ROOTDIR == 0x74
198 case 0x74:
199 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
200 break;
202 // The destination is a subtree of the source.
203 // DE_DESTSUBTREE == 0x76
204 case 0x76:
205 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
206 break;
208 // The operation involved multiple destination paths,
209 // which can fail in the case of a move operation.
210 // DE_MANYDEST == 0x7A
211 case 0x7A:
212 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
213 break;
215 // The source and destination have the same parent folder.
216 // DE_DESTSAMETREE == 0x7D
217 case 0x7D:
218 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
219 break;
221 // An unknown error occurred. This is typically due to an invalid path in
222 // the source or destination. This error does not occur on Windows Vista
223 // and later.
224 // DE_UNKNOWN_ERROR == 0x402
225 case 0x402:
226 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
227 break;
229 // Destination is a root directory and cannot be renamed.
230 // DE_ROOTDIR | ERRORONDEST == 0x10074
231 case 0x10074:
232 result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
233 break;
236 // Narrow down on the reason we're getting some catch-all interrupt reasons.
237 if (result == DOWNLOAD_INTERRUPT_REASON_FILE_FAILED) {
238 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
239 "Download.MapWinShErrorFileFailed", code,
240 base::CustomHistogram::ArrayToCustomRanges(
241 kAllSpecialShFileOperationCodes,
242 arraysize(kAllSpecialShFileOperationCodes)));
245 if (result == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED) {
246 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
247 "Download.MapWinShErrorAccessDenied", code,
248 base::CustomHistogram::ArrayToCustomRanges(
249 kAllSpecialShFileOperationCodes,
250 arraysize(kAllSpecialShFileOperationCodes)));
253 if (result != DOWNLOAD_INTERRUPT_REASON_NONE)
254 return result;
256 // If not one of the above codes, it should be a standard Windows error code.
257 return ConvertNetErrorToInterruptReason(
258 net::MapSystemError(code), DOWNLOAD_INTERRUPT_FROM_DISK);
261 // Maps a return code from ScanAndSaveDownloadedFile() to a
262 // DownloadInterruptReason. The return code in |result| is usually from the
263 // final IAttachmentExecute::Save() call.
264 DownloadInterruptReason MapScanAndSaveErrorCodeToInterruptReason(
265 HRESULT result) {
266 if (SUCCEEDED(result))
267 return DOWNLOAD_INTERRUPT_REASON_NONE;
269 switch (result) {
270 case INET_E_SECURITY_PROBLEM: // 0x800c000e
271 // This is returned if the download was blocked due to security
272 // restrictions. E.g. if the source URL was in the Restricted Sites zone
273 // and downloads are blocked on that zone, then the download would be
274 // deleted and this error code is returned.
275 return DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED;
277 case E_FAIL: // 0x80004005
278 // Returned if an anti-virus product reports an infection in the
279 // downloaded file during IAE::Save().
280 return DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED;
282 default:
283 // Any other error that occurs during IAttachmentExecute::Save() likely
284 // indicates a problem with the security check, but not necessarily the
285 // download. See http://crbug.com/153212.
286 return DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED;
290 } // namespace
292 // Renames a file using the SHFileOperation API to ensure that the target file
293 // gets the correct default security descriptor in the new path.
294 // Returns a network error, or net::OK for success.
295 DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions(
296 const base::FilePath& new_path) {
297 base::ThreadRestrictions::AssertIOAllowed();
299 // The parameters to SHFileOperation must be terminated with 2 NULL chars.
300 base::FilePath::StringType source = full_path_.value();
301 base::FilePath::StringType target = new_path.value();
303 source.append(1, L'\0');
304 target.append(1, L'\0');
306 SHFILEOPSTRUCT move_info = {0};
307 move_info.wFunc = FO_MOVE;
308 move_info.pFrom = source.c_str();
309 move_info.pTo = target.c_str();
310 move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI |
311 FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
313 int result = SHFileOperation(&move_info);
314 DownloadInterruptReason interrupt_reason = DOWNLOAD_INTERRUPT_REASON_NONE;
316 if (result == 0 && move_info.fAnyOperationsAborted)
317 interrupt_reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
318 else if (result != 0)
319 interrupt_reason = MapShFileOperationCodes(result);
321 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE)
322 return LogInterruptReason("SHFileOperation", result, interrupt_reason);
323 return interrupt_reason;
326 DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
328 DCHECK(!detached_);
330 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED);
331 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
332 std::string braces_guid = "{" + client_guid_ + "}";
333 GUID guid = GUID_NULL;
334 if (base::IsValidGUID(client_guid_)) {
335 HRESULT hr = CLSIDFromString(
336 base::UTF8ToUTF16(braces_guid).c_str(), &guid);
337 if (FAILED(hr))
338 guid = GUID_NULL;
341 HRESULT hr = AVScanFile(full_path_, source_url_.spec(), guid);
343 // If the download file is missing after the call, then treat this as an
344 // interrupted download.
346 // If the ScanAndSaveDownloadedFile() call failed, but the downloaded file is
347 // still around, then don't interrupt the download. Attachment Execution
348 // Services deletes the submitted file if the downloaded file is blocked by
349 // policy or if it was found to be infected.
351 // If the file is still there, then the error could be due to AES not being
352 // available or some other error during the AES invocation. In either case,
353 // we don't surface the error to the user.
354 if (!base::PathExists(full_path_)) {
355 DCHECK(FAILED(hr));
356 result = MapScanAndSaveErrorCodeToInterruptReason(hr);
357 if (result == DOWNLOAD_INTERRUPT_REASON_NONE) {
358 RecordDownloadCount(FILE_MISSING_AFTER_SUCCESSFUL_SCAN_COUNT);
359 result = DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED;
361 LogInterruptReason("ScanAndSaveDownloadedFile", hr, result);
363 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED);
364 return result;
367 } // namespace content