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