• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "chrome/browser/download/base_file.h"
6 
7 #include "base/file_util.h"
8 #include "base/format_macros.h"
9 #include "base/logging.h"
10 #include "base/stringprintf.h"
11 #include "crypto/secure_hash.h"
12 #include "net/base/file_stream.h"
13 #include "net/base/net_errors.h"
14 #include "chrome/browser/download/download_util.h"
15 #include "content/browser/browser_thread.h"
16 
17 #if defined(OS_WIN)
18 #include "chrome/common/win_safe_util.h"
19 #elif defined(OS_MACOSX)
20 #include "chrome/browser/cocoa/file_metadata.h"
21 #endif
22 
BaseFile(const FilePath & full_path,const GURL & source_url,const GURL & referrer_url,int64 received_bytes,const linked_ptr<net::FileStream> & file_stream)23 BaseFile::BaseFile(const FilePath& full_path,
24                    const GURL& source_url,
25                    const GURL& referrer_url,
26                    int64 received_bytes,
27                    const linked_ptr<net::FileStream>& file_stream)
28     : full_path_(full_path),
29       source_url_(source_url),
30       referrer_url_(referrer_url),
31       file_stream_(file_stream),
32       bytes_so_far_(received_bytes),
33       power_save_blocker_(true),
34       calculate_hash_(false),
35       detached_(false) {
36   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
37   memset(sha256_hash_, 0, sizeof(sha256_hash_));
38 }
39 
~BaseFile()40 BaseFile::~BaseFile() {
41   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
42   if (detached_)
43     Close();
44   else
45     Cancel();  // Will delete the file.
46 }
47 
Initialize(bool calculate_hash)48 bool BaseFile::Initialize(bool calculate_hash) {
49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
50   DCHECK(!detached_);
51 
52   calculate_hash_ = calculate_hash;
53 
54   if (calculate_hash_)
55     secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
56 
57   if (!full_path_.empty() ||
58       download_util::CreateTemporaryFileForDownload(&full_path_))
59     return Open();
60   return false;
61 }
62 
AppendDataToFile(const char * data,size_t data_len)63 bool BaseFile::AppendDataToFile(const char* data, size_t data_len) {
64   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
65   DCHECK(!detached_);
66 
67   if (!file_stream_.get())
68     return false;
69 
70   // TODO(phajdan.jr): get rid of this check.
71   if (data_len == 0)
72     return true;
73 
74   bytes_so_far_ += data_len;
75 
76   // TODO(phajdan.jr): handle errors on file writes. http://crbug.com/58355
77   size_t written = file_stream_->Write(data, data_len, NULL);
78   if (written != data_len)
79     return false;
80 
81   if (calculate_hash_)
82     secure_hash_->Update(data, data_len);
83 
84   return true;
85 }
86 
Rename(const FilePath & new_path)87 bool BaseFile::Rename(const FilePath& new_path) {
88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
89 
90   // Save the information whether the download is in progress because
91   // it will be overwritten by closing the file.
92   bool saved_in_progress = in_progress();
93 
94   // If the new path is same as the old one, there is no need to perform the
95   // following renaming logic.
96   if (new_path == full_path_) {
97     // Don't close the file if we're not done (finished or canceled).
98     if (!saved_in_progress)
99       Close();
100 
101     return true;
102   }
103 
104   Close();
105 
106   file_util::CreateDirectory(new_path.DirName());
107 
108 #if defined(OS_WIN)
109   // We cannot rename because rename will keep the same security descriptor
110   // on the destination file. We want to recreate the security descriptor
111   // with the security that makes sense in the new path.
112   if (!file_util::RenameFileAndResetSecurityDescriptor(full_path_, new_path))
113     return false;
114 #elif defined(OS_POSIX)
115   {
116     // Similarly, on Unix, we're moving a temp file created with permissions
117     // 600 to |new_path|. Here, we try to fix up the destination file with
118     // appropriate permissions.
119     struct stat st;
120     // First check the file existence and create an empty file if it doesn't
121     // exist.
122     if (!file_util::PathExists(new_path))
123       file_util::WriteFile(new_path, "", 0);
124     bool stat_succeeded = (stat(new_path.value().c_str(), &st) == 0);
125 
126     // TODO(estade): Move() falls back to copying and deleting when a simple
127     // rename fails. Copying sucks for large downloads. crbug.com/8737
128     if (!file_util::Move(full_path_, new_path))
129       return false;
130 
131     if (stat_succeeded)
132       chmod(new_path.value().c_str(), st.st_mode);
133   }
134 #endif
135 
136   full_path_ = new_path;
137 
138   // We don't need to re-open the file if we're done (finished or canceled).
139   if (!saved_in_progress)
140     return true;
141 
142   if (!Open())
143     return false;
144 
145   return true;
146 }
147 
Detach()148 void BaseFile::Detach() {
149   detached_ = true;
150 }
151 
Cancel()152 void BaseFile::Cancel() {
153   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
154   DCHECK(!detached_);
155 
156   Close();
157 
158   if (!full_path_.empty())
159     file_util::Delete(full_path_, false);
160 }
161 
Finish()162 void BaseFile::Finish() {
163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
164 
165   if (calculate_hash_)
166     secure_hash_->Finish(sha256_hash_, kSha256HashLen);
167 
168   Close();
169 }
170 
GetSha256Hash(std::string * hash)171 bool BaseFile::GetSha256Hash(std::string* hash) {
172   DCHECK(!detached_);
173   if (!calculate_hash_ || in_progress())
174     return false;
175   hash->assign(reinterpret_cast<const char*>(sha256_hash_),
176                sizeof(sha256_hash_));
177   return true;
178 }
179 
AnnotateWithSourceInformation()180 void BaseFile::AnnotateWithSourceInformation() {
181   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
182   DCHECK(!detached_);
183 
184 #if defined(OS_WIN)
185   // Sets the Zone to tell Windows that this file comes from the internet.
186   // We ignore the return value because a failure is not fatal.
187   win_util::SetInternetZoneIdentifier(full_path_);
188 #elif defined(OS_MACOSX)
189   file_metadata::AddQuarantineMetadataToFile(full_path_, source_url_,
190                                              referrer_url_);
191   file_metadata::AddOriginMetadataToFile(full_path_, source_url_,
192                                          referrer_url_);
193 #endif
194 }
195 
Open()196 bool BaseFile::Open() {
197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
198   DCHECK(!detached_);
199   DCHECK(!full_path_.empty());
200 
201   // Create a new file stream if it is not provided.
202   if (!file_stream_.get()) {
203     file_stream_.reset(new net::FileStream);
204     if (file_stream_->Open(full_path_,
205                            base::PLATFORM_FILE_OPEN_ALWAYS |
206                                base::PLATFORM_FILE_WRITE) != net::OK) {
207       file_stream_.reset();
208       return false;
209     }
210 
211     // We may be re-opening the file after rename. Always make sure we're
212     // writing at the end of the file.
213     if (file_stream_->Seek(net::FROM_END, 0) < 0) {
214       file_stream_.reset();
215       return false;
216     }
217   }
218 
219 #if defined(OS_WIN)
220   AnnotateWithSourceInformation();
221 #endif
222   return true;
223 }
224 
Close()225 void BaseFile::Close() {
226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
227   if (file_stream_.get()) {
228 #if defined(OS_CHROMEOS)
229     // Currently we don't really care about the return value, since if it fails
230     // theres not much we can do.  But we might in the future.
231     file_stream_->Flush();
232 #endif
233     file_stream_->Close();
234     file_stream_.reset();
235   }
236 }
237 
DebugString() const238 std::string BaseFile::DebugString() const {
239   return base::StringPrintf("{ source_url_ = \"%s\""
240                             " full_path_ = \"%" PRFilePath "\""
241                             " bytes_so_far_ = %" PRId64 " detached_ = %c }",
242                             source_url_.spec().c_str(),
243                             full_path_.value().c_str(),
244                             bytes_so_far_,
245                             detached_ ? 'T' : 'F');
246 }
247