| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/base_file.h" | 5 #include "content/browser/download/base_file.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/pickle.h" | 11 #include "base/pickle.h" |
| 12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
| 14 #include "base/utf_string_conversions.h" | 14 #include "content/browser/download/download_interrupt_reasons_impl.h" |
| 15 #include "content/browser/download/download_net_log_parameters.h" | 15 #include "content/browser/download/download_net_log_parameters.h" |
| 16 #include "content/browser/download/download_stats.h" | 16 #include "content/browser/download/download_stats.h" |
| 17 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
| 18 #include "content/public/browser/content_browser_client.h" | 18 #include "content/public/browser/content_browser_client.h" |
| 19 #include "crypto/secure_hash.h" | 19 #include "crypto/secure_hash.h" |
| 20 #include "net/base/file_stream.h" | 20 #include "net/base/file_stream.h" |
| 21 #include "net/base/net_errors.h" | 21 #include "net/base/net_errors.h" |
| 22 | 22 |
| 23 #if defined(OS_WIN) | |
| 24 #include <windows.h> | |
| 25 #include <shellapi.h> | |
| 26 | |
| 27 #include "content/browser/safe_util_win.h" | |
| 28 #elif defined(OS_MACOSX) | |
| 29 #include "content/browser/download/file_metadata_mac.h" | |
| 30 #elif defined(OS_LINUX) | |
| 31 #include "content/browser/download/file_metadata_linux.h" | |
| 32 #endif | |
| 33 | |
| 34 using content::BrowserThread; | 23 using content::BrowserThread; |
| 35 | 24 |
| 36 namespace { | |
| 37 | |
| 38 #define LOG_ERROR(o, e) \ | |
| 39 LogError(__FILE__, __LINE__, __FUNCTION__, bound_net_log_, o, e) | |
| 40 | |
| 41 // Logs the value and passes error on through, converting to a |net::Error|. | |
| 42 // Returns |ERR_UNEXPECTED| if the value is not in the enum. | |
| 43 net::Error LogError(const char* file, | |
| 44 int line, | |
| 45 const char* func, | |
| 46 const net::BoundNetLog& bound_net_log, | |
| 47 const char* operation, | |
| 48 int error) { | |
| 49 const char* err_string = ""; | |
| 50 net::Error net_error = net::OK; | |
| 51 | |
| 52 #define NET_ERROR(label, value) \ | |
| 53 case net::ERR_##label: \ | |
| 54 err_string = #label; \ | |
| 55 net_error = net::ERR_##label; \ | |
| 56 break; | |
| 57 | |
| 58 switch (error) { | |
| 59 case net::OK: | |
| 60 return net::OK; | |
| 61 | |
| 62 #include "net/base/net_error_list.h" | |
| 63 | |
| 64 default: | |
| 65 err_string = "Unexpected enum value"; | |
| 66 net_error = net::ERR_UNEXPECTED; | |
| 67 break; | |
| 68 } | |
| 69 | |
| 70 #undef NET_ERROR | |
| 71 | |
| 72 VLOG(1) << " " << func << "(): " << operation | |
| 73 << "() returned error " << error << " (" << err_string << ")"; | |
| 74 | |
| 75 bound_net_log.AddEvent( | |
| 76 net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, | |
| 77 base::Bind(&download_net_logs::FileErrorCallback, operation, net_error)); | |
| 78 | |
| 79 return net_error; | |
| 80 } | |
| 81 | |
| 82 #if defined(OS_WIN) | |
| 83 | |
| 84 #define SHFILE_TO_NET_ERROR(symbol, value, mapping, description) \ | |
| 85 case value: return net::ERR_##mapping; | |
| 86 | |
| 87 // Maps the result of a call to |SHFileOperation()| onto a |net::Error|. | |
| 88 // | |
| 89 // These return codes are *old* (as in, DOS era), and specific to | |
| 90 // |SHFileOperation()|. | |
| 91 // They do not appear in any windows header. | |
| 92 // | |
| 93 // See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx. | |
| 94 net::Error MapShFileOperationCodes(int code) { | |
| 95 // Check these pre-Win32 error codes first, then check for matches | |
| 96 // in Winerror.h. | |
| 97 | |
| 98 switch (code) { | |
| 99 // Error Code, Value, Platform Error Mapping, Meaning | |
| 100 SHFILE_TO_NET_ERROR(DE_SAMEFILE, 0x71, FILE_EXISTS, | |
| 101 "The source and destination files are the same file.") | |
| 102 SHFILE_TO_NET_ERROR(DE_OPCANCELLED, 0x75, ABORTED, | |
| 103 "The operation was canceled by the user, or silently canceled if " | |
| 104 "the appropriate flags were supplied to SHFileOperation.") | |
| 105 SHFILE_TO_NET_ERROR(DE_ACCESSDENIEDSRC, 0x78, ACCESS_DENIED, | |
| 106 "Security settings denied access to the source.") | |
| 107 SHFILE_TO_NET_ERROR(DE_PATHTOODEEP, 0x79, FILE_PATH_TOO_LONG, | |
| 108 "The source or destination path exceeded or would exceed MAX_PATH.") | |
| 109 SHFILE_TO_NET_ERROR(DE_INVALIDFILES, 0x7C, FILE_NOT_FOUND, | |
| 110 "The path in the source or destination or both was invalid.") | |
| 111 SHFILE_TO_NET_ERROR(DE_FLDDESTISFILE, 0x7E, FILE_EXISTS, | |
| 112 "The destination path is an existing file.") | |
| 113 SHFILE_TO_NET_ERROR(DE_FILEDESTISFLD, 0x80, FILE_EXISTS, | |
| 114 "The destination path is an existing folder.") | |
| 115 SHFILE_TO_NET_ERROR(DE_FILENAMETOOLONG, 0x81, FILE_PATH_TOO_LONG, | |
| 116 "The name of the file exceeds MAX_PATH.") | |
| 117 SHFILE_TO_NET_ERROR(DE_DEST_IS_CDROM, 0x82, ACCESS_DENIED, | |
| 118 "The destination is a read-only CD-ROM, possibly unformatted.") | |
| 119 SHFILE_TO_NET_ERROR(DE_DEST_IS_DVD, 0x83, ACCESS_DENIED, | |
| 120 "The destination is a read-only DVD, possibly unformatted.") | |
| 121 SHFILE_TO_NET_ERROR(DE_DEST_IS_CDRECORD, 0x84, ACCESS_DENIED, | |
| 122 "The destination is a writable CD-ROM, possibly unformatted.") | |
| 123 SHFILE_TO_NET_ERROR(DE_FILE_TOO_LARGE, 0x85, FILE_TOO_BIG, | |
| 124 "The file involved in the operation is too large for the destination " | |
| 125 "media or file system.") | |
| 126 SHFILE_TO_NET_ERROR(DE_SRC_IS_CDROM, 0x86, ACCESS_DENIED, | |
| 127 "The source is a read-only CD-ROM, possibly unformatted.") | |
| 128 SHFILE_TO_NET_ERROR(DE_SRC_IS_DVD, 0x87, ACCESS_DENIED, | |
| 129 "The source is a read-only DVD, possibly unformatted.") | |
| 130 SHFILE_TO_NET_ERROR(DE_SRC_IS_CDRECORD, 0x88, ACCESS_DENIED, | |
| 131 "The source is a writable CD-ROM, possibly unformatted.") | |
| 132 SHFILE_TO_NET_ERROR(DE_ERROR_MAX, 0xB7, FILE_PATH_TOO_LONG, | |
| 133 "MAX_PATH was exceeded during the operation.") | |
| 134 SHFILE_TO_NET_ERROR(XE_ERRORONDEST, 0x10000, UNEXPECTED, | |
| 135 "An unspecified error occurred on the destination.") | |
| 136 | |
| 137 // These are not expected to occur for in our usage. | |
| 138 SHFILE_TO_NET_ERROR(DE_MANYSRC1DEST, 0x72, FAILED, | |
| 139 "Multiple file paths were specified in the source buffer, " | |
| 140 "but only one destination file path.") | |
| 141 SHFILE_TO_NET_ERROR(DE_DIFFDIR, 0x73, FAILED, | |
| 142 "Rename operation was specified but the destination path is " | |
| 143 "a different directory. Use the move operation instead.") | |
| 144 SHFILE_TO_NET_ERROR(DE_ROOTDIR, 0x74, FAILED, | |
| 145 "The source is a root directory, which cannot be moved or renamed.") | |
| 146 SHFILE_TO_NET_ERROR(DE_DESTSUBTREE, 0x76, FAILED, | |
| 147 "The destination is a subtree of the source.") | |
| 148 SHFILE_TO_NET_ERROR(DE_MANYDEST, 0x7A, FAILED, | |
| 149 "The operation involved multiple destination paths, " | |
| 150 "which can fail in the case of a move operation.") | |
| 151 SHFILE_TO_NET_ERROR(DE_DESTSAMETREE, 0x7D, FAILED, | |
| 152 "The source and destination have the same parent folder.") | |
| 153 SHFILE_TO_NET_ERROR(DE_UNKNOWN_ERROR, 0x402, FAILED, | |
| 154 "An unknown error occurred. " | |
| 155 "This is typically due to an invalid path in the source or destination." | |
| 156 " This error does not occur on Windows Vista and later.") | |
| 157 SHFILE_TO_NET_ERROR(DE_ROOTDIR | ERRORONDEST, 0x10074, FAILED, | |
| 158 "Destination is a root directory and cannot be renamed.") | |
| 159 default: | |
| 160 break; | |
| 161 } | |
| 162 | |
| 163 // If not one of the above codes, it should be a standard Windows error code. | |
| 164 return static_cast<net::Error>(net::MapSystemError(code)); | |
| 165 } | |
| 166 | |
| 167 #undef SHFILE_TO_NET_ERROR | |
| 168 | |
| 169 // Renames a file using the SHFileOperation API to ensure that the target file | |
| 170 // gets the correct default security descriptor in the new path. | |
| 171 // Returns a network error, or net::OK for success. | |
| 172 net::Error RenameFileAndResetSecurityDescriptor( | |
| 173 const FilePath& source_file_path, | |
| 174 const FilePath& target_file_path) { | |
| 175 base::ThreadRestrictions::AssertIOAllowed(); | |
| 176 | |
| 177 // The parameters to SHFileOperation must be terminated with 2 NULL chars. | |
| 178 std::wstring source = source_file_path.value(); | |
| 179 std::wstring target = target_file_path.value(); | |
| 180 | |
| 181 source.append(1, L'\0'); | |
| 182 target.append(1, L'\0'); | |
| 183 | |
| 184 SHFILEOPSTRUCT move_info = {0}; | |
| 185 move_info.wFunc = FO_MOVE; | |
| 186 move_info.pFrom = source.c_str(); | |
| 187 move_info.pTo = target.c_str(); | |
| 188 move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | | |
| 189 FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS; | |
| 190 | |
| 191 int result = SHFileOperation(&move_info); | |
| 192 | |
| 193 if (result == 0) | |
| 194 return (move_info.fAnyOperationsAborted) ? net::ERR_ABORTED : net::OK; | |
| 195 | |
| 196 return MapShFileOperationCodes(result); | |
| 197 } | |
| 198 | |
| 199 #endif | |
| 200 | |
| 201 } // namespace | |
| 202 | |
| 203 // This will initialize the entire array to zero. | 25 // This will initialize the entire array to zero. |
| 204 const unsigned char BaseFile::kEmptySha256Hash[] = { 0 }; | 26 const unsigned char BaseFile::kEmptySha256Hash[] = { 0 }; |
| 205 | 27 |
| 206 BaseFile::BaseFile(const FilePath& full_path, | 28 BaseFile::BaseFile(const FilePath& full_path, |
| 207 const GURL& source_url, | 29 const GURL& source_url, |
| 208 const GURL& referrer_url, | 30 const GURL& referrer_url, |
| 209 int64 received_bytes, | 31 int64 received_bytes, |
| 210 bool calculate_hash, | 32 bool calculate_hash, |
| 211 const std::string& hash_state, | 33 const std::string& hash_state_bytes, |
| 212 scoped_ptr<net::FileStream> file_stream, | 34 scoped_ptr<net::FileStream> file_stream, |
| 213 const net::BoundNetLog& bound_net_log) | 35 const net::BoundNetLog& bound_net_log) |
| 214 : full_path_(full_path), | 36 : full_path_(full_path), |
| 215 source_url_(source_url), | 37 source_url_(source_url), |
| 216 referrer_url_(referrer_url), | 38 referrer_url_(referrer_url), |
| 217 file_stream_(file_stream.Pass()), | 39 file_stream_(file_stream.Pass()), |
| 218 bytes_so_far_(received_bytes), | 40 bytes_so_far_(received_bytes), |
| 219 start_tick_(base::TimeTicks::Now()), | 41 start_tick_(base::TimeTicks::Now()), |
| 220 calculate_hash_(calculate_hash), | 42 calculate_hash_(calculate_hash), |
| 221 detached_(false), | 43 detached_(false), |
| 222 bound_net_log_(bound_net_log) { | 44 bound_net_log_(bound_net_log) { |
| 223 memcpy(sha256_hash_, kEmptySha256Hash, kSha256HashLen); | 45 memcpy(sha256_hash_, kEmptySha256Hash, kSha256HashLen); |
| 224 if (calculate_hash_) { | 46 if (calculate_hash_) { |
| 225 secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | 47 secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
| 226 if ((bytes_so_far_ > 0) && // Not starting at the beginning. | 48 if ((bytes_so_far_ > 0) && // Not starting at the beginning. |
| 227 (hash_state != "") && // Reasonably sure we have a hash state. | 49 (!IsEmptyHash(hash_state_bytes))) { |
| 228 (!IsEmptyHash(hash_state))) { | 50 Pickle hash_state(hash_state_bytes.c_str(), hash_state_bytes.size()); |
| 229 SetHashState(hash_state); | 51 PickleIterator data_iterator(hash_state); |
| 52 secure_hash_->Deserialize(&data_iterator); |
| 230 } | 53 } |
| 231 } | 54 } |
| 232 } | 55 } |
| 233 | 56 |
| 234 BaseFile::~BaseFile() { | 57 BaseFile::~BaseFile() { |
| 235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 236 if (detached_) | 59 if (detached_) |
| 237 Close(); | 60 Close(); |
| 238 else | 61 else |
| 239 Cancel(); // Will delete the file. | 62 Cancel(); // Will delete the file. |
| 240 } | 63 } |
| 241 | 64 |
| 242 net::Error BaseFile::Initialize(const FilePath& default_directory) { | 65 content::DownloadInterruptReason BaseFile::Initialize( |
| 66 const FilePath& default_directory) { |
| 243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 244 DCHECK(!detached_); | 68 DCHECK(!detached_); |
| 245 | 69 |
| 246 if (file_stream_.get()) { | 70 if (file_stream_.get()) { |
| 247 file_stream_->SetBoundNetLogSource(bound_net_log_); | 71 file_stream_->SetBoundNetLogSource(bound_net_log_); |
| 248 file_stream_->EnableErrorStatistics(); | 72 file_stream_->EnableErrorStatistics(); |
| 249 } | 73 } |
| 250 | 74 |
| 251 if (full_path_.empty()) { | 75 if (full_path_.empty()) { |
| 252 FilePath initial_directory(default_directory); | 76 FilePath initial_directory(default_directory); |
| 253 FilePath temp_file; | 77 FilePath temp_file; |
| 254 if (initial_directory.empty()) { | 78 if (initial_directory.empty()) { |
| 255 initial_directory = | 79 initial_directory = |
| 256 content::GetContentClient()->browser()->GetDefaultDownloadDirectory(); | 80 content::GetContentClient()->browser()->GetDefaultDownloadDirectory(); |
| 257 } | 81 } |
| 258 // |initial_directory| can still be empty if ContentBrowserClient returned | 82 // |initial_directory| can still be empty if ContentBrowserClient returned |
| 259 // an empty path for the downloads directory. | 83 // an empty path for the downloads directory. |
| 260 if ((initial_directory.empty() || | 84 if ((initial_directory.empty() || |
| 261 !file_util::CreateTemporaryFileInDir(initial_directory, &temp_file)) && | 85 !file_util::CreateTemporaryFileInDir(initial_directory, &temp_file)) && |
| 262 !file_util::CreateTemporaryFile(&temp_file)) { | 86 !file_util::CreateTemporaryFile(&temp_file)) { |
| 263 return LOG_ERROR("unable to create", net::ERR_FILE_NOT_FOUND); | 87 return LogInterruptReason("Unable to create", 0, |
| 88 content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| 264 } | 89 } |
| 265 full_path_ = temp_file; | 90 full_path_ = temp_file; |
| 266 } | 91 } |
| 267 | 92 |
| 268 return Open(); | 93 return Open(); |
| 269 } | 94 } |
| 270 | 95 |
| 271 net::Error BaseFile::AppendDataToFile(const char* data, size_t data_len) { | 96 content::DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, |
| 97 size_t data_len) { |
| 272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 273 DCHECK(!detached_); | 99 DCHECK(!detached_); |
| 274 | 100 |
| 275 // NOTE(benwells): The above DCHECK won't be present in release builds, | 101 // NOTE(benwells): The above DCHECK won't be present in release builds, |
| 276 // so we log any occurences to see how common this error is in the wild. | 102 // so we log any occurences to see how common this error is in the wild. |
| 277 if (detached_) { | 103 if (detached_) { |
| 278 download_stats::RecordDownloadCount( | 104 download_stats::RecordDownloadCount( |
| 279 download_stats::APPEND_TO_DETACHED_FILE_COUNT); | 105 download_stats::APPEND_TO_DETACHED_FILE_COUNT); |
| 280 } | 106 } |
| 281 | 107 |
| 282 if (!file_stream_.get()) | 108 if (!file_stream_.get()) |
| 283 return LOG_ERROR("get", net::ERR_INVALID_HANDLE); | 109 return LogInterruptReason("No file stream on append", 0, |
| 110 content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| 284 | 111 |
| 285 // TODO(phajdan.jr): get rid of this check. | 112 // TODO(phajdan.jr): get rid of this check. |
| 286 if (data_len == 0) | 113 if (data_len == 0) |
| 287 return net::OK; | 114 return content::DOWNLOAD_INTERRUPT_REASON_NONE; |
| 288 | 115 |
| 289 // The Write call below is not guaranteed to write all the data. | 116 // The Write call below is not guaranteed to write all the data. |
| 290 size_t write_count = 0; | 117 size_t write_count = 0; |
| 291 size_t len = data_len; | 118 size_t len = data_len; |
| 292 const char* current_data = data; | 119 const char* current_data = data; |
| 293 while (len > 0) { | 120 while (len > 0) { |
| 294 write_count++; | 121 write_count++; |
| 295 int write_result = | 122 int write_result = |
| 296 file_stream_->WriteSync(current_data, len); | 123 file_stream_->WriteSync(current_data, len); |
| 297 DCHECK_NE(0, write_result); | 124 DCHECK_NE(0, write_result); |
| 298 | 125 |
| 299 // Check for errors. | 126 // Check for errors. |
| 300 if (static_cast<size_t>(write_result) != data_len) { | 127 if (static_cast<size_t>(write_result) != data_len) { |
| 301 // We should never get ERR_IO_PENDING, as the Write above is synchronous. | 128 // We should never get ERR_IO_PENDING, as the Write above is synchronous. |
| 302 DCHECK_NE(net::ERR_IO_PENDING, write_result); | 129 DCHECK_NE(net::ERR_IO_PENDING, write_result); |
| 303 | 130 |
| 304 // Report errors on file writes. | 131 // Report errors on file writes. |
| 305 if (write_result < 0) | 132 if (write_result < 0) |
| 306 return LOG_ERROR("Write", write_result); | 133 return LogNetError("Write", static_cast<net::Error>(write_result)); |
| 307 } | 134 } |
| 308 | 135 |
| 309 // Update status. | 136 // Update status. |
| 310 size_t write_size = static_cast<size_t>(write_result); | 137 size_t write_size = static_cast<size_t>(write_result); |
| 311 DCHECK_LE(write_size, len); | 138 DCHECK_LE(write_size, len); |
| 312 len -= write_size; | 139 len -= write_size; |
| 313 current_data += write_size; | 140 current_data += write_size; |
| 314 bytes_so_far_ += write_size; | 141 bytes_so_far_ += write_size; |
| 315 } | 142 } |
| 316 | 143 |
| 317 download_stats::RecordDownloadWriteSize(data_len); | 144 download_stats::RecordDownloadWriteSize(data_len); |
| 318 download_stats::RecordDownloadWriteLoopCount(write_count); | 145 download_stats::RecordDownloadWriteLoopCount(write_count); |
| 319 | 146 |
| 320 if (calculate_hash_) | 147 if (calculate_hash_) |
| 321 secure_hash_->Update(data, data_len); | 148 secure_hash_->Update(data, data_len); |
| 322 | 149 |
| 323 return net::OK; | 150 return content::DOWNLOAD_INTERRUPT_REASON_NONE; |
| 324 } | 151 } |
| 325 | 152 |
| 326 net::Error BaseFile::Rename(const FilePath& new_path) { | 153 content::DownloadInterruptReason BaseFile::Rename(const FilePath& new_path) { |
| 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 155 content::DownloadInterruptReason rename_result = |
| 156 content::DOWNLOAD_INTERRUPT_REASON_NONE; |
| 157 |
| 158 // If the new path is same as the old one, there is no need to perform the |
| 159 // following renaming logic. |
| 160 if (new_path == full_path_) |
| 161 return content::DOWNLOAD_INTERRUPT_REASON_NONE; |
| 328 | 162 |
| 329 // Save the information whether the download is in progress because | 163 // Save the information whether the download is in progress because |
| 330 // it will be overwritten by closing the file. | 164 // it will be overwritten by closing the file. |
| 331 bool saved_in_progress = in_progress(); | 165 bool was_in_progress = in_progress(); |
| 332 | 166 |
| 333 bound_net_log_.AddEvent( | 167 bound_net_log_.BeginEvent( |
| 334 net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED, | 168 net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED, |
| 335 base::Bind(&download_net_logs::FileRenamedCallback, | 169 base::Bind(&download_net_logs::FileRenamedCallback, |
| 336 &full_path_, &new_path)); | 170 &full_path_, &new_path)); |
| 171 Close(); |
| 172 file_util::CreateDirectory(new_path.DirName()); |
| 337 | 173 |
| 338 // If the new path is same as the old one, there is no need to perform the | 174 // A simple rename wouldn't work here since we want the file to have |
| 339 // following renaming logic. | 175 // permissions / security descriptors that makes sense in the new directory. |
| 340 if (new_path == full_path_) { | 176 rename_result = MoveFileAndAdjustPermissions(new_path); |
| 341 // Don't close the file if we're not done (finished or canceled). | |
| 342 if (!saved_in_progress) | |
| 343 Close(); | |
| 344 | 177 |
| 345 return net::OK; | 178 if (rename_result == content::DOWNLOAD_INTERRUPT_REASON_NONE) { |
| 179 full_path_ = new_path; |
| 180 // Re-open the file if we were still using it. |
| 181 if (was_in_progress) |
| 182 rename_result = Open(); |
| 346 } | 183 } |
| 347 | 184 |
| 348 Close(); | 185 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED); |
| 349 | 186 return rename_result; |
| 350 file_util::CreateDirectory(new_path.DirName()); | |
| 351 | |
| 352 #if defined(OS_WIN) | |
| 353 // We cannot rename because rename will keep the same security descriptor | |
| 354 // on the destination file. We want to recreate the security descriptor | |
| 355 // with the security that makes sense in the new path. | |
| 356 // |RenameFileAndResetSecurityDescriptor| returns a windows-specific | |
| 357 // error, whch we must translate into a net::Error. | |
| 358 net::Error rename_err = | |
| 359 RenameFileAndResetSecurityDescriptor(full_path_, new_path); | |
| 360 if (rename_err != net::OK) | |
| 361 return LOG_ERROR("RenameFileAndResetSecurityDescriptor", rename_err); | |
| 362 #elif defined(OS_POSIX) | |
| 363 { | |
| 364 // Similarly, on Unix, we're moving a temp file created with permissions | |
| 365 // 600 to |new_path|. Here, we try to fix up the destination file with | |
| 366 // appropriate permissions. | |
| 367 struct stat st; | |
| 368 // First check the file existence and create an empty file if it doesn't | |
| 369 // exist. | |
| 370 if (!file_util::PathExists(new_path)) { | |
| 371 int write_error = file_util::WriteFile(new_path, "", 0); | |
| 372 if (write_error < 0) | |
| 373 return LOG_ERROR("WriteFile", net::MapSystemError(errno)); | |
| 374 } | |
| 375 int stat_error = stat(new_path.value().c_str(), &st); | |
| 376 bool stat_succeeded = (stat_error == 0); | |
| 377 if (!stat_succeeded) | |
| 378 LOG_ERROR("stat", net::MapSystemError(errno)); | |
| 379 | |
| 380 // TODO(estade): Move() falls back to copying and deleting when a simple | |
| 381 // rename fails. Copying sucks for large downloads. crbug.com/8737 | |
| 382 if (!file_util::Move(full_path_, new_path)) | |
| 383 return LOG_ERROR("Move", net::MapSystemError(errno)); | |
| 384 | |
| 385 if (stat_succeeded) { | |
| 386 // On Windows file systems (FAT, NTFS), chmod fails. This is OK. | |
| 387 int chmod_error = chmod(new_path.value().c_str(), st.st_mode); | |
| 388 if (chmod_error < 0) | |
| 389 LOG_ERROR("chmod", net::MapSystemError(errno)); | |
| 390 } | |
| 391 } | |
| 392 #endif | |
| 393 | |
| 394 full_path_ = new_path; | |
| 395 | |
| 396 // We don't need to re-open the file if we're done (finished or canceled). | |
| 397 if (!saved_in_progress) | |
| 398 return net::OK; | |
| 399 | |
| 400 return Open(); | |
| 401 } | 187 } |
| 402 | 188 |
| 403 void BaseFile::Detach() { | 189 void BaseFile::Detach() { |
| 404 detached_ = true; | 190 detached_ = true; |
| 405 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DETACHED); | 191 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_DETACHED); |
| 406 } | 192 } |
| 407 | 193 |
| 408 void BaseFile::Cancel() { | 194 void BaseFile::Cancel() { |
| 409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 410 DCHECK(!detached_); | 196 DCHECK(!detached_); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 422 | 208 |
| 423 void BaseFile::Finish() { | 209 void BaseFile::Finish() { |
| 424 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 425 | 211 |
| 426 if (calculate_hash_) | 212 if (calculate_hash_) |
| 427 secure_hash_->Finish(sha256_hash_, kSha256HashLen); | 213 secure_hash_->Finish(sha256_hash_, kSha256HashLen); |
| 428 | 214 |
| 429 Close(); | 215 Close(); |
| 430 } | 216 } |
| 431 | 217 |
| 218 // OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations. |
| 219 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) |
| 220 void BaseFile::AnnotateWithSourceInformation() { |
| 221 } |
| 222 #endif |
| 223 |
| 224 int64 BaseFile::CurrentSpeed() const { |
| 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 226 return CurrentSpeedAtTime(base::TimeTicks::Now()); |
| 227 } |
| 228 |
| 432 bool BaseFile::GetHash(std::string* hash) { | 229 bool BaseFile::GetHash(std::string* hash) { |
| 433 DCHECK(!detached_); | 230 DCHECK(!detached_); |
| 434 hash->assign(reinterpret_cast<const char*>(sha256_hash_), | 231 hash->assign(reinterpret_cast<const char*>(sha256_hash_), |
| 435 sizeof(sha256_hash_)); | 232 sizeof(sha256_hash_)); |
| 436 return (calculate_hash_ && !in_progress()); | 233 return (calculate_hash_ && !in_progress()); |
| 437 } | 234 } |
| 438 | 235 |
| 439 std::string BaseFile::GetHashState() { | 236 std::string BaseFile::GetHashState() { |
| 440 if (!calculate_hash_) | 237 if (!calculate_hash_) |
| 441 return ""; | 238 return ""; |
| 442 | 239 |
| 443 Pickle hash_state; | 240 Pickle hash_state; |
| 444 if (!secure_hash_->Serialize(&hash_state)) | 241 if (!secure_hash_->Serialize(&hash_state)) |
| 445 return ""; | 242 return ""; |
| 446 | 243 |
| 447 return std::string(reinterpret_cast<const char*>(hash_state.data()), | 244 return std::string(reinterpret_cast<const char*>(hash_state.data()), |
| 448 hash_state.size()); | 245 hash_state.size()); |
| 449 } | 246 } |
| 450 | 247 |
| 451 bool BaseFile::SetHashState(const std::string& hash_state_bytes) { | 248 // static |
| 452 if (!calculate_hash_) | |
| 453 return false; | |
| 454 | |
| 455 Pickle hash_state(hash_state_bytes.c_str(), hash_state_bytes.size()); | |
| 456 PickleIterator data_iterator(hash_state); | |
| 457 | |
| 458 return secure_hash_->Deserialize(&data_iterator); | |
| 459 } | |
| 460 | |
| 461 bool BaseFile::IsEmptyHash(const std::string& hash) { | 249 bool BaseFile::IsEmptyHash(const std::string& hash) { |
| 462 return (hash.size() == kSha256HashLen && | 250 return (hash.size() == kSha256HashLen && |
| 463 0 == memcmp(hash.data(), kEmptySha256Hash, sizeof(kSha256HashLen))); | 251 0 == memcmp(hash.data(), kEmptySha256Hash, sizeof(kSha256HashLen))); |
| 464 } | 252 } |
| 465 | 253 |
| 466 void BaseFile::AnnotateWithSourceInformation() { | 254 std::string BaseFile::DebugString() const { |
| 467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 255 return base::StringPrintf("{ source_url_ = \"%s\"" |
| 468 DCHECK(!detached_); | 256 " full_path_ = \"%" PRFilePath "\"" |
| 469 | 257 " bytes_so_far_ = %" PRId64 |
| 470 #if defined(OS_WIN) | 258 " detached_ = %c }", |
| 471 // Sets the Zone to tell Windows that this file comes from the internet. | 259 source_url_.spec().c_str(), |
| 472 // We ignore the return value because a failure is not fatal. | 260 full_path_.value().c_str(), |
| 473 win_util::SetInternetZoneIdentifier(full_path_, | 261 bytes_so_far_, |
| 474 UTF8ToWide(source_url_.spec())); | 262 detached_ ? 'T' : 'F'); |
| 475 #elif defined(OS_MACOSX) | |
| 476 content::AddQuarantineMetadataToFile(full_path_, source_url_, referrer_url_); | |
| 477 content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_); | |
| 478 #elif defined(OS_LINUX) | |
| 479 content::AddOriginMetadataToFile(full_path_, source_url_, referrer_url_); | |
| 480 #endif | |
| 481 } | 263 } |
| 482 | 264 |
| 483 void BaseFile::CreateFileStream() { | 265 void BaseFile::CreateFileStream() { |
| 484 file_stream_.reset(new net::FileStream(bound_net_log_.net_log())); | 266 file_stream_.reset(new net::FileStream(bound_net_log_.net_log())); |
| 485 file_stream_->SetBoundNetLogSource(bound_net_log_); | 267 file_stream_->SetBoundNetLogSource(bound_net_log_); |
| 486 } | 268 } |
| 487 | 269 |
| 488 net::Error BaseFile::Open() { | 270 content::DownloadInterruptReason BaseFile::Open() { |
| 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 490 DCHECK(!detached_); | 272 DCHECK(!detached_); |
| 491 DCHECK(!full_path_.empty()); | 273 DCHECK(!full_path_.empty()); |
| 492 | 274 |
| 493 bound_net_log_.BeginEvent( | 275 bound_net_log_.BeginEvent( |
| 494 net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, | 276 net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, |
| 495 base::Bind(&download_net_logs::FileOpenedCallback, | 277 base::Bind(&download_net_logs::FileOpenedCallback, |
| 496 &full_path_, bytes_so_far_)); | 278 &full_path_, bytes_so_far_)); |
| 497 | 279 |
| 498 // Create a new file stream if it is not provided. | 280 // Create a new file stream if it is not provided. |
| 499 if (!file_stream_.get()) { | 281 if (!file_stream_.get()) { |
| 500 CreateFileStream(); | 282 CreateFileStream(); |
| 501 file_stream_->EnableErrorStatistics(); | 283 file_stream_->EnableErrorStatistics(); |
| 502 int open_result = file_stream_->OpenSync( | 284 int open_result = file_stream_->OpenSync( |
| 503 full_path_, | 285 full_path_, |
| 504 base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE); | 286 base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE); |
| 505 if (open_result != net::OK) | 287 if (open_result != net::OK) { |
| 506 return ClearStream(LOG_ERROR("Open", open_result)); | 288 ClearStream(); |
| 289 return LogNetError("Open", static_cast<net::Error>(open_result)); |
| 290 } |
| 507 | 291 |
| 508 // We may be re-opening the file after rename. Always make sure we're | 292 // We may be re-opening the file after rename. Always make sure we're |
| 509 // writing at the end of the file. | 293 // writing at the end of the file. |
| 510 int64 seek_result = file_stream_->SeekSync(net::FROM_END, 0); | 294 int64 seek_result = file_stream_->SeekSync(net::FROM_END, 0); |
| 511 if (seek_result < 0) | 295 if (seek_result < 0) { |
| 512 return ClearStream(LOG_ERROR("Seek", seek_result)); | 296 ClearStream(); |
| 297 return LogNetError("Seek", static_cast<net::Error>(seek_result)); |
| 298 } |
| 513 } else { | 299 } else { |
| 514 file_stream_->SetBoundNetLogSource(bound_net_log_); | 300 file_stream_->SetBoundNetLogSource(bound_net_log_); |
| 515 } | 301 } |
| 516 | 302 |
| 517 return net::OK; | 303 return content::DOWNLOAD_INTERRUPT_REASON_NONE; |
| 518 } | 304 } |
| 519 | 305 |
| 520 void BaseFile::Close() { | 306 void BaseFile::Close() { |
| 521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 522 | 308 |
| 523 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_CLOSED); | 309 bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_FILE_CLOSED); |
| 524 | 310 |
| 525 if (file_stream_.get()) { | 311 if (file_stream_.get()) { |
| 526 #if defined(OS_CHROMEOS) | 312 #if defined(OS_CHROMEOS) |
| 527 // Currently we don't really care about the return value, since if it fails | 313 // Currently we don't really care about the return value, since if it fails |
| 528 // theres not much we can do. But we might in the future. | 314 // theres not much we can do. But we might in the future. |
| 529 file_stream_->FlushSync(); | 315 file_stream_->FlushSync(); |
| 530 #endif | 316 #endif |
| 531 file_stream_->CloseSync(); | 317 file_stream_->CloseSync(); |
| 532 ClearStream(net::OK); | 318 ClearStream(); |
| 533 } | 319 } |
| 534 } | 320 } |
| 535 | 321 |
| 536 net::Error BaseFile::ClearStream(net::Error net_error) { | 322 void BaseFile::ClearStream() { |
| 537 // This should only be called when we have a stream. | 323 // This should only be called when we have a stream. |
| 538 DCHECK(file_stream_.get() != NULL); | 324 DCHECK(file_stream_.get() != NULL); |
| 539 file_stream_.reset(); | 325 file_stream_.reset(); |
| 540 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_OPENED); | 326 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_OPENED); |
| 541 return net_error; | |
| 542 } | |
| 543 | |
| 544 std::string BaseFile::DebugString() const { | |
| 545 return base::StringPrintf("{ source_url_ = \"%s\"" | |
| 546 " full_path_ = \"%" PRFilePath "\"" | |
| 547 " bytes_so_far_ = %" PRId64 | |
| 548 " detached_ = %c }", | |
| 549 source_url_.spec().c_str(), | |
| 550 full_path_.value().c_str(), | |
| 551 bytes_so_far_, | |
| 552 detached_ ? 'T' : 'F'); | |
| 553 } | 327 } |
| 554 | 328 |
| 555 int64 BaseFile::CurrentSpeedAtTime(base::TimeTicks current_time) const { | 329 int64 BaseFile::CurrentSpeedAtTime(base::TimeTicks current_time) const { |
| 556 base::TimeDelta diff = current_time - start_tick_; | 330 base::TimeDelta diff = current_time - start_tick_; |
| 557 int64 diff_ms = diff.InMilliseconds(); | 331 int64 diff_ms = diff.InMilliseconds(); |
| 558 return diff_ms == 0 ? 0 : bytes_so_far() * 1000 / diff_ms; | 332 return diff_ms == 0 ? 0 : bytes_so_far() * 1000 / diff_ms; |
| 559 } | 333 } |
| 560 | 334 |
| 561 int64 BaseFile::CurrentSpeed() const { | 335 content::DownloadInterruptReason BaseFile::LogNetError( |
| 562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 336 const char* operation, |
| 563 return CurrentSpeedAtTime(base::TimeTicks::Now()); | 337 net::Error error) { |
| 338 bound_net_log_.AddEvent( |
| 339 net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, |
| 340 base::Bind(&download_net_logs::FileErrorCallback, operation, error)); |
| 341 return content::ConvertNetErrorToInterruptReason( |
| 342 error, content::DOWNLOAD_INTERRUPT_FROM_DISK); |
| 564 } | 343 } |
| 344 |
| 345 content::DownloadInterruptReason BaseFile::LogSystemError( |
| 346 const char* operation, |
| 347 int os_error) { |
| 348 // There's no direct conversion from a system error to an interrupt reason. |
| 349 net::Error net_error = net::MapSystemError(os_error); |
| 350 return LogInterruptReason( |
| 351 operation, os_error, |
| 352 content::ConvertNetErrorToInterruptReason( |
| 353 net_error, content::DOWNLOAD_INTERRUPT_FROM_DISK)); |
| 354 } |
| 355 |
| 356 content::DownloadInterruptReason BaseFile::LogInterruptReason( |
| 357 const char* operation, |
| 358 int os_error, |
| 359 content::DownloadInterruptReason reason) { |
| 360 bound_net_log_.AddEvent( |
| 361 net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, |
| 362 base::Bind(&download_net_logs::FileInterruptedCallback, operation, |
| 363 os_error, reason)); |
| 364 return reason; |
| 365 } |
| OLD | NEW |