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.
5 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/lazy_instance.h"
10 #include "base/threading/worker_pool.h"
11 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
12 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
13 #include "content/public/browser/browser_thread.h"
15 namespace extensions
{
16 namespace image_writer
{
18 using content::BrowserThread
;
20 const int kMD5BufferSize
= 1024;
21 #if defined(OS_CHROMEOS)
22 // Chrome OS only has a 1 GB temporary partition. This is too small to hold our
23 // unzipped image. Fortunately we mount part of the temporary partition under
25 const char kChromeOSTempRoot
[] = "/var/tmp";
28 #if !defined(OS_CHROMEOS)
29 static base::LazyInstance
<scoped_refptr
<ImageWriterUtilityClient
> >
30 g_utility_client
= LAZY_INSTANCE_INITIALIZER
;
33 Operation::Operation(base::WeakPtr
<OperationManager
> manager
,
34 const ExtensionId
& extension_id
,
35 const std::string
& device_path
)
37 extension_id_(extension_id
),
39 device_path_(base::FilePath::FromUTF8Unsafe(device_path
)),
41 device_path_(device_path
),
43 stage_(image_writer_api::STAGE_UNKNOWN
),
47 Operation::~Operation() {}
49 void Operation::Cancel() {
50 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
52 stage_
= image_writer_api::STAGE_NONE
;
57 void Operation::Abort() {
58 Error(error::kAborted
);
61 int Operation::GetProgress() {
65 image_writer_api::Stage
Operation::GetStage() {
69 #if !defined(OS_CHROMEOS)
71 void Operation::SetUtilityClientForTesting(
72 scoped_refptr
<ImageWriterUtilityClient
> client
) {
73 g_utility_client
.Get() = client
;
77 void Operation::Start() {
78 #if defined(OS_CHROMEOS)
79 if (!temp_dir_
.CreateUniqueTempDirUnderPath(
80 base::FilePath(kChromeOSTempRoot
))) {
82 if (!temp_dir_
.CreateUniqueTempDir()) {
84 Error(error::kTempDirError
);
89 base::Bind(base::IgnoreResult(&base::ScopedTempDir::Delete
),
90 base::Unretained(&temp_dir_
)));
95 void Operation::Unzip(const base::Closure
& continuation
) {
96 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
101 if (image_path_
.Extension() != FILE_PATH_LITERAL(".zip")) {
102 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, continuation
);
106 SetStage(image_writer_api::STAGE_UNZIP
);
108 if (!(zip_reader_
.Open(image_path_
) && zip_reader_
.AdvanceToNextEntry() &&
109 zip_reader_
.OpenCurrentEntryInZip())) {
110 Error(error::kUnzipGenericError
);
114 if (zip_reader_
.HasMore()) {
115 Error(error::kUnzipInvalidArchive
);
119 // Create a new target to unzip to. The original file is opened by the
121 zip::ZipReader::EntryInfo
* entry_info
= zip_reader_
.current_entry_info();
123 image_path_
= temp_dir_
.path().Append(entry_info
->file_path().BaseName());
125 Error(error::kTempDirError
);
129 zip_reader_
.ExtractCurrentEntryToFilePathAsync(
131 base::Bind(&Operation::CompleteAndContinue
, this, continuation
),
132 base::Bind(&Operation::OnUnzipFailure
, this),
133 base::Bind(&Operation::OnUnzipProgress
,
135 zip_reader_
.current_entry_info()->original_size()));
138 void Operation::Finish() {
139 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
140 BrowserThread::PostTask(
141 BrowserThread::FILE, FROM_HERE
, base::Bind(&Operation::Finish
, this));
147 BrowserThread::PostTask(
150 base::Bind(&OperationManager::OnComplete
, manager_
, extension_id_
));
153 void Operation::Error(const std::string
& error_message
) {
154 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
155 BrowserThread::PostTask(BrowserThread::FILE,
157 base::Bind(&Operation::Error
, this, error_message
));
161 BrowserThread::PostTask(
164 base::Bind(&OperationManager::OnError
,
174 void Operation::SetProgress(int progress
) {
175 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
176 BrowserThread::PostTask(
179 base::Bind(&Operation::SetProgress
,
185 if (progress
<= progress_
) {
193 progress_
= progress
;
195 BrowserThread::PostTask(BrowserThread::UI
,
197 base::Bind(&OperationManager::OnProgress
,
204 void Operation::SetStage(image_writer_api::Stage stage
) {
205 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
206 BrowserThread::PostTask(
209 base::Bind(&Operation::SetStage
,
222 BrowserThread::PostTask(
225 base::Bind(&OperationManager::OnProgress
,
232 bool Operation::IsCancelled() {
233 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
235 return stage_
== image_writer_api::STAGE_NONE
;
238 void Operation::AddCleanUpFunction(const base::Closure
& callback
) {
239 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
240 cleanup_functions_
.push_back(callback
);
243 void Operation::CompleteAndContinue(const base::Closure
& continuation
) {
244 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
245 SetProgress(kProgressComplete
);
246 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, continuation
);
249 #if !defined(OS_CHROMEOS)
250 void Operation::StartUtilityClient() {
251 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
252 if (g_utility_client
.Get().get()) {
253 image_writer_client_
= g_utility_client
.Get();
256 if (!image_writer_client_
.get()) {
257 image_writer_client_
= new ImageWriterUtilityClient();
258 AddCleanUpFunction(base::Bind(&Operation::StopUtilityClient
, this));
262 void Operation::StopUtilityClient() {
263 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
264 BrowserThread::PostTask(
267 base::Bind(&ImageWriterUtilityClient::Shutdown
, image_writer_client_
));
270 void Operation::WriteImageProgress(int64 total_bytes
, int64 curr_bytes
) {
271 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
276 int progress
= kProgressComplete
* curr_bytes
/ total_bytes
;
278 if (progress
> GetProgress()) {
279 SetProgress(progress
);
284 void Operation::GetMD5SumOfFile(
285 const base::FilePath
& file_path
,
289 const base::Callback
<void(const std::string
&)>& callback
) {
294 base::MD5Init(&md5_context_
);
296 base::File
file(file_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
297 if (!file
.IsValid()) {
298 Error(error::kImageOpenError
);
302 if (file_size
<= 0) {
303 file_size
= file
.GetLength();
305 Error(error::kImageOpenError
);
310 BrowserThread::PostTask(BrowserThread::FILE,
312 base::Bind(&Operation::MD5Chunk
,
322 void Operation::MD5Chunk(
324 int64 bytes_processed
,
328 const base::Callback
<void(const std::string
&)>& callback
) {
332 CHECK_LE(bytes_processed
, bytes_total
);
334 scoped_ptr
<char[]> buffer(new char[kMD5BufferSize
]);
335 int read_size
= std::min(bytes_total
- bytes_processed
,
336 static_cast<int64
>(kMD5BufferSize
));
338 if (read_size
== 0) {
339 // Nothing to read, we are done.
340 base::MD5Digest digest
;
341 base::MD5Final(&digest
, &md5_context_
);
342 callback
.Run(base::MD5DigestToBase16(digest
));
344 int len
= file
.Read(bytes_processed
, buffer
.get(), read_size
);
346 if (len
== read_size
) {
348 base::MD5Update(&md5_context_
, base::StringPiece(buffer
.get(), len
));
350 ((bytes_processed
+ len
) * progress_scale
) / bytes_total
+
352 SetProgress(percent_curr
);
354 BrowserThread::PostTask(BrowserThread::FILE,
356 base::Bind(&Operation::MD5Chunk
,
359 bytes_processed
+ len
,
364 // Skip closing the file.
367 // We didn't read the bytes we expected.
368 Error(error::kHashReadError
);
373 void Operation::OnUnzipFailure() {
374 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
375 Error(error::kUnzipGenericError
);
378 void Operation::OnUnzipProgress(int64 total_bytes
, int64 progress_bytes
) {
379 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
381 int progress_percent
= kProgressComplete
* progress_bytes
/ total_bytes
;
382 SetProgress(progress_percent
);
385 void Operation::CleanUp() {
386 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
387 for (std::vector
<base::Closure
>::iterator it
= cleanup_functions_
.begin();
388 it
!= cleanup_functions_
.end();
392 cleanup_functions_
.clear();
395 } // namespace image_writer
396 } // namespace extensions