OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "base/file_util.h" | 5 #include "base/file_util.h" |
6 #include "base/files/file_enumerator.h" | 6 #include "base/files/file_enumerator.h" |
7 #include "base/threading/worker_pool.h" | 7 #include "base/threading/worker_pool.h" |
8 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" | 8 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" |
9 #include "chrome/browser/extensions/api/image_writer_private/operation.h" | 9 #include "chrome/browser/extensions/api/image_writer_private/operation.h" |
10 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h
" | 10 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h
" |
11 #include "content/public/browser/browser_thread.h" | 11 #include "content/public/browser/browser_thread.h" |
12 | 12 |
13 namespace extensions { | 13 namespace extensions { |
14 namespace image_writer { | 14 namespace image_writer { |
15 | 15 |
16 using content::BrowserThread; | 16 using content::BrowserThread; |
17 | 17 |
18 namespace { | |
19 | |
20 const int kMD5BufferSize = 1024; | 18 const int kMD5BufferSize = 1024; |
21 | 19 #if defined(OS_CHROMEOS) |
22 void RemoveTempDirectory(const base::FilePath path) { | 20 // Chrome OS only has a 1 GB temporary partition. This is too small to hold our |
23 base::DeleteFile(path, true); | 21 // unzipped image. Fortunately we mount part of the temporary partition under |
24 } | 22 // /var/tmp. |
25 | 23 const char kChromeOSTempRoot[] = "/var/tmp"; |
26 } // namespace | 24 #endif |
27 | 25 |
28 Operation::Operation(base::WeakPtr<OperationManager> manager, | 26 Operation::Operation(base::WeakPtr<OperationManager> manager, |
29 const ExtensionId& extension_id, | 27 const ExtensionId& extension_id, |
30 const std::string& storage_unit_id) | 28 const std::string& device_path) |
31 : manager_(manager), | 29 : manager_(manager), |
32 extension_id_(extension_id), | 30 extension_id_(extension_id), |
33 storage_unit_id_(storage_unit_id), | 31 #if defined(OS_WIN) |
34 verify_write_(true), | 32 device_path_(base::FilePath::FromUTF8Unsafe(device_path)), |
| 33 #else |
| 34 device_path_(device_path), |
| 35 #endif |
35 stage_(image_writer_api::STAGE_UNKNOWN), | 36 stage_(image_writer_api::STAGE_UNKNOWN), |
36 progress_(0) { | 37 progress_(0) { |
37 } | 38 } |
38 | 39 |
39 Operation::~Operation() { | 40 Operation::~Operation() { |
40 } | 41 } |
41 | 42 |
42 void Operation::Cancel() { | 43 void Operation::Cancel() { |
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
44 | 45 |
45 DVLOG(1) << "Cancelling image writing operation for ext: " << extension_id_; | |
46 | |
47 stage_ = image_writer_api::STAGE_NONE; | 46 stage_ = image_writer_api::STAGE_NONE; |
48 | 47 |
49 CleanUp(); | 48 CleanUp(); |
50 } | 49 } |
51 | 50 |
52 void Operation::Abort() { | 51 void Operation::Abort() { |
53 Error(error::kAborted); | 52 Error(error::kAborted); |
54 } | 53 } |
55 | 54 |
56 int Operation::GetProgress() { | 55 int Operation::GetProgress() { |
57 return progress_; | 56 return progress_; |
58 } | 57 } |
59 | 58 |
60 image_writer_api::Stage Operation::GetStage() { | 59 image_writer_api::Stage Operation::GetStage() { |
61 return stage_; | 60 return stage_; |
62 } | 61 } |
63 | 62 |
| 63 void Operation::Start() { |
| 64 #if defined(OS_CHROMEOS) |
| 65 if (!temp_dir_.CreateUniqueTempDirUnderPath( |
| 66 base::FilePath(kChromeOSTempRoot))) { |
| 67 #else |
| 68 if (!temp_dir_.CreateUniqueTempDir()) { |
| 69 #endif |
| 70 Error(error::kTempDirError); |
| 71 return; |
| 72 } |
| 73 |
| 74 AddCleanUpFunction( |
| 75 base::Bind(base::IgnoreResult(&base::ScopedTempDir::Delete), |
| 76 base::Unretained(&temp_dir_))); |
| 77 |
| 78 StartImpl(); |
| 79 } |
| 80 |
| 81 void Operation::Unzip(const base::Closure& continuation) { |
| 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 83 if (IsCancelled()) { |
| 84 return; |
| 85 } |
| 86 |
| 87 if (image_path_.Extension() != FILE_PATH_LITERAL(".zip")) { |
| 88 continuation.Run(); |
| 89 return; |
| 90 } |
| 91 |
| 92 SetStage(image_writer_api::STAGE_UNZIP); |
| 93 |
| 94 if (!(zip_reader_.Open(image_path_) && zip_reader_.AdvanceToNextEntry() && |
| 95 zip_reader_.OpenCurrentEntryInZip())) { |
| 96 Error(error::kUnzipGenericError); |
| 97 return; |
| 98 } |
| 99 |
| 100 if (zip_reader_.HasMore()) { |
| 101 Error(error::kUnzipInvalidArchive); |
| 102 return; |
| 103 } |
| 104 |
| 105 // Create a new target to unzip to. The original file is opened by the |
| 106 // zip_reader_. |
| 107 zip::ZipReader::EntryInfo* entry_info = zip_reader_.current_entry_info(); |
| 108 if (entry_info) { |
| 109 image_path_ = temp_dir_.path().Append(entry_info->file_path().BaseName()); |
| 110 } else { |
| 111 Error(error::kTempDirError); |
| 112 return; |
| 113 } |
| 114 |
| 115 zip_reader_.ExtractCurrentEntryToFilePathAsync( |
| 116 image_path_, |
| 117 base::Bind(&Operation::OnUnzipSuccess, this, continuation), |
| 118 base::Bind(&Operation::OnUnzipFailure, this), |
| 119 base::Bind(&Operation::OnUnzipProgress, |
| 120 this, |
| 121 zip_reader_.current_entry_info()->original_size())); |
| 122 } |
| 123 |
| 124 void Operation::Finish() { |
| 125 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
| 126 BrowserThread::PostTask( |
| 127 BrowserThread::FILE, FROM_HERE, base::Bind(&Operation::Finish, this)); |
| 128 return; |
| 129 } |
| 130 |
| 131 CleanUp(); |
| 132 |
| 133 BrowserThread::PostTask( |
| 134 BrowserThread::UI, |
| 135 FROM_HERE, |
| 136 base::Bind(&OperationManager::OnComplete, manager_, extension_id_)); |
| 137 } |
| 138 |
64 void Operation::Error(const std::string& error_message) { | 139 void Operation::Error(const std::string& error_message) { |
65 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | 140 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
66 BrowserThread::PostTask(BrowserThread::FILE, | 141 BrowserThread::PostTask(BrowserThread::FILE, |
67 FROM_HERE, | 142 FROM_HERE, |
68 base::Bind(&Operation::Error, this, error_message)); | 143 base::Bind(&Operation::Error, this, error_message)); |
69 return; | 144 return; |
70 } | 145 } |
71 | 146 |
72 BrowserThread::PostTask( | 147 BrowserThread::PostTask( |
73 BrowserThread::UI, | 148 BrowserThread::UI, |
(...skipping 12 matching lines...) Expand all Loading... |
86 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | 161 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
87 BrowserThread::PostTask( | 162 BrowserThread::PostTask( |
88 BrowserThread::FILE, | 163 BrowserThread::FILE, |
89 FROM_HERE, | 164 FROM_HERE, |
90 base::Bind(&Operation::SetProgress, | 165 base::Bind(&Operation::SetProgress, |
91 this, | 166 this, |
92 progress)); | 167 progress)); |
93 return; | 168 return; |
94 } | 169 } |
95 | 170 |
| 171 if (progress <= progress_) { |
| 172 return; |
| 173 } |
| 174 |
96 if (IsCancelled()) { | 175 if (IsCancelled()) { |
97 return; | 176 return; |
98 } | 177 } |
99 | 178 |
100 progress_ = progress; | 179 progress_ = progress; |
101 | 180 |
102 BrowserThread::PostTask( | 181 BrowserThread::PostTask( |
103 BrowserThread::UI, | 182 BrowserThread::UI, |
104 FROM_HERE, | 183 FROM_HERE, |
105 base::Bind(&OperationManager::OnProgress, | 184 base::Bind(&OperationManager::OnProgress, |
(...skipping 24 matching lines...) Expand all Loading... |
130 BrowserThread::PostTask( | 209 BrowserThread::PostTask( |
131 BrowserThread::UI, | 210 BrowserThread::UI, |
132 FROM_HERE, | 211 FROM_HERE, |
133 base::Bind(&OperationManager::OnProgress, | 212 base::Bind(&OperationManager::OnProgress, |
134 manager_, | 213 manager_, |
135 extension_id_, | 214 extension_id_, |
136 stage_, | 215 stage_, |
137 progress_)); | 216 progress_)); |
138 } | 217 } |
139 | 218 |
140 void Operation::Finish() { | |
141 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | |
142 BrowserThread::PostTask(BrowserThread::FILE, | |
143 FROM_HERE, | |
144 base::Bind(&Operation::Finish, this)); | |
145 return; | |
146 } | |
147 DVLOG(1) << "Write operation complete."; | |
148 | |
149 CleanUp(); | |
150 | |
151 BrowserThread::PostTask( | |
152 BrowserThread::UI, | |
153 FROM_HERE, | |
154 base::Bind(&OperationManager::OnComplete, | |
155 manager_, | |
156 extension_id_)); | |
157 } | |
158 | |
159 bool Operation::IsCancelled() { | 219 bool Operation::IsCancelled() { |
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
161 | 221 |
162 return stage_ == image_writer_api::STAGE_NONE; | 222 return stage_ == image_writer_api::STAGE_NONE; |
163 } | 223 } |
164 | 224 |
165 void Operation::AddCleanUpFunction(base::Closure callback) { | 225 void Operation::AddCleanUpFunction(const base::Closure& callback) { |
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
167 | |
168 cleanup_functions_.push_back(callback); | 227 cleanup_functions_.push_back(callback); |
169 } | 228 } |
170 | 229 |
171 void Operation::CleanUp() { | |
172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
173 for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin(); | |
174 it != cleanup_functions_.end(); | |
175 ++it) { | |
176 it->Run(); | |
177 } | |
178 cleanup_functions_.clear(); | |
179 } | |
180 | |
181 void Operation::UnzipStart(scoped_ptr<base::FilePath> zip_path) { | |
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
183 if (IsCancelled()) { | |
184 return; | |
185 } | |
186 | |
187 DVLOG(1) << "Starting unzip stage for " << zip_path->value(); | |
188 | |
189 SetStage(image_writer_api::STAGE_UNZIP); | |
190 | |
191 base::FilePath tmp_dir; | |
192 if (!base::CreateTemporaryDirInDir(zip_path->DirName(), | |
193 FILE_PATH_LITERAL("image_writer"), | |
194 &tmp_dir)) { | |
195 Error(error::kTempDirError); | |
196 return; | |
197 } | |
198 | |
199 AddCleanUpFunction(base::Bind(&RemoveTempDirectory, tmp_dir)); | |
200 | |
201 if (!base::CreateTemporaryFileInDir(tmp_dir, &image_path_)) { | |
202 DLOG(ERROR) << "Failed create temporary unzip target in " | |
203 << tmp_dir.value(); | |
204 Error(error::kTempDirError); | |
205 return; | |
206 } | |
207 | |
208 if (!(zip_reader_.Open(*zip_path) && | |
209 zip_reader_.AdvanceToNextEntry() && | |
210 zip_reader_.OpenCurrentEntryInZip())) { | |
211 DLOG(ERROR) << "Failed to open zip file."; | |
212 Error(error::kUnzipGenericError); | |
213 return; | |
214 } | |
215 | |
216 if (zip_reader_.HasMore()) { | |
217 DLOG(ERROR) << "Image zip has more than one file."; | |
218 Error(error::kUnzipInvalidArchive); | |
219 return; | |
220 } | |
221 | |
222 zip_reader_.ExtractCurrentEntryToFilePathAsync( | |
223 image_path_, | |
224 base::Bind(&Operation::OnUnzipSuccess, this), | |
225 base::Bind(&Operation::OnUnzipFailure, this), | |
226 base::Bind(&Operation::OnUnzipProgress, | |
227 this, | |
228 zip_reader_.current_entry_info()->original_size())); | |
229 } | |
230 | |
231 void Operation::GetMD5SumOfFile( | 230 void Operation::GetMD5SumOfFile( |
232 scoped_ptr<base::FilePath> file_path, | 231 const base::FilePath& file_path, |
233 int64 file_size, | 232 int64 file_size, |
234 int progress_offset, | 233 int progress_offset, |
235 int progress_scale, | 234 int progress_scale, |
236 const base::Callback<void(scoped_ptr<std::string>)>& callback) { | 235 const base::Callback<void(const std::string&)>& callback) { |
237 if (IsCancelled()) { | 236 if (IsCancelled()) { |
238 return; | 237 return; |
239 } | 238 } |
240 | 239 |
241 base::MD5Init(&md5_context_); | 240 base::MD5Init(&md5_context_); |
242 scoped_ptr<image_writer_utils::ImageReader> reader( | |
243 new image_writer_utils::ImageReader()); | |
244 | 241 |
245 if (!reader->Open(*file_path)) { | 242 base::PlatformFile file = base::CreatePlatformFile( |
| 243 file_path, |
| 244 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, |
| 245 NULL, |
| 246 NULL); |
| 247 if (file == base::kInvalidPlatformFileValue) { |
246 Error(error::kImageOpenError); | 248 Error(error::kImageOpenError); |
247 return; | 249 return; |
248 } | 250 } |
| 251 |
249 if (file_size <= 0) { | 252 if (file_size <= 0) { |
250 file_size = reader->GetSize(); | 253 if (!base::GetFileSize(file_path, &file_size)) { |
| 254 Error(error::kImageOpenError); |
| 255 return; |
| 256 } |
251 } | 257 } |
252 | 258 |
253 BrowserThread::PostTask(BrowserThread::FILE, | 259 BrowserThread::PostTask(BrowserThread::FILE, |
254 FROM_HERE, | 260 FROM_HERE, |
255 base::Bind(&Operation::MD5Chunk, | 261 base::Bind(&Operation::MD5Chunk, |
256 this, | 262 this, |
257 base::Passed(&reader), | 263 file, |
258 0, | 264 0, |
259 file_size, | 265 file_size, |
260 progress_offset, | 266 progress_offset, |
261 progress_scale, | 267 progress_scale, |
262 callback)); | 268 callback)); |
263 } | 269 } |
264 | 270 |
265 void Operation::MD5Chunk( | 271 void Operation::MD5Chunk( |
266 scoped_ptr<image_writer_utils::ImageReader> reader, | 272 const base::PlatformFile& file, |
267 int64 bytes_processed, | 273 int64 bytes_processed, |
268 int64 bytes_total, | 274 int64 bytes_total, |
269 int progress_offset, | 275 int progress_offset, |
270 int progress_scale, | 276 int progress_scale, |
271 const base::Callback<void(scoped_ptr<std::string>)>& callback) { | 277 const base::Callback<void(const std::string&)>& callback) { |
272 if (IsCancelled()) { | 278 if (IsCancelled()) { |
273 reader->Close(); | 279 base::ClosePlatformFile(file); |
274 return; | 280 return; |
275 } | 281 } |
276 | 282 |
277 char buffer[kMD5BufferSize]; | 283 CHECK_LE(bytes_processed, bytes_total); |
278 int len; | |
279 | 284 |
280 if (bytes_total - bytes_processed <= kMD5BufferSize) { | 285 scoped_ptr<char[]> buffer(new char[kMD5BufferSize]); |
281 len = reader->Read(buffer, bytes_total - bytes_processed); | 286 int read_size = std::min(bytes_total - bytes_processed, |
| 287 static_cast<int64>(kMD5BufferSize)); |
| 288 |
| 289 if (read_size == 0) { |
| 290 // Nothing to read, we are done. |
| 291 base::MD5Digest digest; |
| 292 base::MD5Final(&digest, &md5_context_); |
| 293 callback.Run(base::MD5DigestToBase16(digest)); |
282 } else { | 294 } else { |
283 len = reader->Read(buffer, kMD5BufferSize); | 295 int len = |
| 296 base::ReadPlatformFile(file, bytes_processed, buffer.get(), read_size); |
| 297 |
| 298 if (len == read_size) { |
| 299 // Process data. |
| 300 base::MD5Update(&md5_context_, base::StringPiece(buffer.get(), len)); |
| 301 int percent_curr = |
| 302 ((bytes_processed + len) * progress_scale) / bytes_total + |
| 303 progress_offset; |
| 304 SetProgress(percent_curr); |
| 305 |
| 306 BrowserThread::PostTask(BrowserThread::FILE, |
| 307 FROM_HERE, |
| 308 base::Bind(&Operation::MD5Chunk, |
| 309 this, |
| 310 file, |
| 311 bytes_processed + len, |
| 312 bytes_total, |
| 313 progress_offset, |
| 314 progress_scale, |
| 315 callback)); |
| 316 // Skip closing the file. |
| 317 return; |
| 318 } else { |
| 319 // We didn't read the bytes we expected. |
| 320 Error(error::kHashReadError); |
| 321 } |
284 } | 322 } |
285 | 323 base::ClosePlatformFile(file); |
286 if (len > 0) { | |
287 base::MD5Update(&md5_context_, base::StringPiece(buffer, len)); | |
288 int percent_prev = (bytes_processed * progress_scale + progress_offset) / | |
289 (bytes_total); | |
290 int percent_curr = ((bytes_processed + len) * progress_scale + | |
291 progress_offset) / | |
292 (bytes_total); | |
293 if (percent_curr > percent_prev) { | |
294 SetProgress(progress_); | |
295 } | |
296 | |
297 BrowserThread::PostTask(BrowserThread::FILE, | |
298 FROM_HERE, | |
299 base::Bind(&Operation::MD5Chunk, | |
300 this, | |
301 base::Passed(&reader), | |
302 bytes_processed + len, | |
303 bytes_total, | |
304 progress_offset, | |
305 progress_scale, | |
306 callback)); | |
307 } else if (len == 0) { | |
308 if (bytes_processed + len < bytes_total) { | |
309 reader->Close(); | |
310 Error(error::kHashReadError); | |
311 } else { | |
312 base::MD5Digest digest; | |
313 base::MD5Final(&digest, &md5_context_); | |
314 scoped_ptr<std::string> hash( | |
315 new std::string(base::MD5DigestToBase16(digest))); | |
316 callback.Run(hash.Pass()); | |
317 } | |
318 } else { // len < 0 | |
319 reader->Close(); | |
320 Error(error::kHashReadError); | |
321 } | |
322 } | 324 } |
323 | 325 |
324 void Operation::OnUnzipSuccess() { | 326 void Operation::OnUnzipSuccess(const base::Closure& continuation) { |
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
326 SetProgress(kProgressComplete); | 328 SetProgress(kProgressComplete); |
327 WriteStart(); | 329 continuation.Run(); |
328 } | 330 } |
329 | 331 |
330 void Operation::OnUnzipFailure() { | 332 void Operation::OnUnzipFailure() { |
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
332 Error(error::kUnzipGenericError); | 334 Error(error::kUnzipGenericError); |
333 } | 335 } |
334 | 336 |
335 void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) { | 337 void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) { |
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
337 | 339 |
338 int progress_percent = 100 * progress_bytes / total_bytes; | 340 int progress_percent = 100 * progress_bytes / total_bytes; |
339 SetProgress(progress_percent); | 341 SetProgress(progress_percent); |
340 } | 342 } |
341 | 343 |
| 344 void Operation::CleanUp() { |
| 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 346 for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin(); |
| 347 it != cleanup_functions_.end(); |
| 348 ++it) { |
| 349 it->Run(); |
| 350 } |
| 351 cleanup_functions_.clear(); |
| 352 } |
| 353 |
342 } // namespace image_writer | 354 } // namespace image_writer |
343 } // namespace extensions | 355 } // namespace extensions |
OLD | NEW |