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