1 // Copyright 2014 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/utility/image_writer/image_writer.h"
7 #include "base/location.h"
8 #include "base/memory/aligned_memory.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "chrome/utility/image_writer/error_messages.h"
12 #include "chrome/utility/image_writer/image_writer_handler.h"
13 #include "content/public/utility/utility_thread.h"
15 #if defined(OS_MACOSX)
16 #include "chrome/utility/image_writer/disk_unmounter_mac.h"
19 namespace image_writer
{
21 // Since block devices like large sequential access and IPC is expensive we're
22 // doing work in 1MB chunks.
23 const int kBurningBlockSize
= 1 << 20; // 1 MB
24 const int kMemoryAlignment
= 4096;
26 ImageWriter::ImageWriter(ImageWriterHandler
* handler
,
27 const base::FilePath
& image_path
,
28 const base::FilePath
& device_path
)
29 : image_path_(image_path
),
30 device_path_(device_path
),
35 ImageWriter::~ImageWriter() {
37 for (std::vector
<HANDLE
>::const_iterator it
= volume_handles_
.begin();
38 it
!= volume_handles_
.end();
45 void ImageWriter::Write() {
46 if (!InitializeFiles()) {
51 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
54 void ImageWriter::Verify() {
55 if (!InitializeFiles()) {
60 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
63 void ImageWriter::Cancel() {
65 handler_
->SendCancelled();
68 bool ImageWriter::IsRunning() const { return running_
; }
70 const base::FilePath
& ImageWriter::GetImagePath() { return image_path_
; }
72 const base::FilePath
& ImageWriter::GetDevicePath() { return device_path_
; }
74 void ImageWriter::PostTask(const base::Closure
& task
) {
75 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, task
);
78 void ImageWriter::PostProgress(int64 progress
) {
79 handler_
->SendProgress(progress
);
82 void ImageWriter::Error(const std::string
& message
) {
84 handler_
->SendFailed(message
);
87 bool ImageWriter::InitializeFiles() {
88 if (!image_file_
.IsValid()) {
89 image_file_
.Initialize(image_path_
,
90 base::File::FLAG_OPEN
| base::File::FLAG_READ
|
91 base::File::FLAG_EXCLUSIVE_READ
);
93 if (!image_file_
.IsValid()) {
94 DLOG(ERROR
) << "Unable to open file for read: " << image_path_
.value();
95 Error(error::kOpenImage
);
100 if (!device_file_
.IsValid()) {
102 Error(error::kOpenDevice
);
107 bytes_processed_
= 0;
113 void ImageWriter::WriteChunk() {
118 // DASD buffers require memory alignment on some systems.
119 scoped_ptr
<char, base::AlignedFreeDeleter
> buffer(static_cast<char*>(
120 base::AlignedAlloc(kBurningBlockSize
, kMemoryAlignment
)));
121 memset(buffer
.get(), 0, kBurningBlockSize
);
123 int bytes_read
= image_file_
.Read(bytes_processed_
, buffer
.get(),
126 if (bytes_read
> 0) {
127 // Always attempt to write a whole block, as writing DASD requires sector-
128 // aligned writes to devices.
129 int bytes_to_write
= bytes_read
+ (kMemoryAlignment
- 1) -
130 (bytes_read
- 1) % kMemoryAlignment
;
131 DCHECK_EQ(0, bytes_to_write
% kMemoryAlignment
);
133 device_file_
.Write(bytes_processed_
, buffer
.get(), bytes_to_write
);
135 if (bytes_written
< bytes_read
) {
136 Error(error::kWriteImage
);
140 bytes_processed_
+= bytes_read
;
141 PostProgress(bytes_processed_
);
143 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
144 } else if (bytes_read
== 0) {
146 device_file_
.Flush();
148 handler_
->SendSucceeded();
150 // Unable to read entire file.
151 Error(error::kReadImage
);
155 void ImageWriter::VerifyChunk() {
160 scoped_ptr
<char[]> image_buffer(new char[kBurningBlockSize
]);
161 // DASD buffers require memory alignment on some systems.
162 scoped_ptr
<char, base::AlignedFreeDeleter
> device_buffer(static_cast<char*>(
163 base::AlignedAlloc(kBurningBlockSize
, kMemoryAlignment
)));
165 int bytes_read
= image_file_
.Read(bytes_processed_
, image_buffer
.get(),
168 if (bytes_read
> 0) {
169 if (device_file_
.Read(bytes_processed_
,
171 kBurningBlockSize
) < bytes_read
) {
172 LOG(ERROR
) << "Failed to read " << bytes_read
<< " bytes of "
173 << "device at offset " << bytes_processed_
;
174 Error(error::kReadDevice
);
178 if (memcmp(image_buffer
.get(), device_buffer
.get(), bytes_read
) != 0) {
179 LOG(ERROR
) << "Write verification failed when comparing " << bytes_read
180 << " bytes at " << bytes_processed_
;
181 Error(error::kVerificationFailed
);
185 bytes_processed_
+= bytes_read
;
186 PostProgress(bytes_processed_
);
188 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
189 } else if (bytes_read
== 0) {
191 handler_
->SendSucceeded();
194 // Unable to read entire file.
195 LOG(ERROR
) << "Failed to read " << kBurningBlockSize
<< " bytes of image "
196 << "at offset " << bytes_processed_
;
197 Error(error::kReadImage
);
201 } // namespace image_writer