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/memory/aligned_memory.h"
8 #include "chrome/utility/image_writer/error_messages.h"
9 #include "chrome/utility/image_writer/image_writer_handler.h"
10 #include "content/public/utility/utility_thread.h"
12 #if defined(OS_MACOSX)
13 #include "chrome/utility/image_writer/disk_unmounter_mac.h"
16 namespace image_writer
{
18 // Since block devices like large sequential access and IPC is expensive we're
19 // doing work in 1MB chunks.
20 const int kBurningBlockSize
= 1 << 20; // 1 MB
21 const int kMemoryAlignment
= 4096;
23 ImageWriter::ImageWriter(ImageWriterHandler
* handler
,
24 const base::FilePath
& image_path
,
25 const base::FilePath
& device_path
)
26 : image_path_(image_path
),
27 device_path_(device_path
),
32 ImageWriter::~ImageWriter() {
34 for (std::vector
<HANDLE
>::const_iterator it
= volume_handles_
.begin();
35 it
!= volume_handles_
.end();
42 void ImageWriter::Write() {
43 if (!InitializeFiles()) {
48 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
51 void ImageWriter::Verify() {
52 if (!InitializeFiles()) {
57 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
60 void ImageWriter::Cancel() {
62 handler_
->SendCancelled();
65 bool ImageWriter::IsRunning() const { return running_
; }
67 const base::FilePath
& ImageWriter::GetImagePath() { return image_path_
; }
69 const base::FilePath
& ImageWriter::GetDevicePath() { return device_path_
; }
71 void ImageWriter::PostTask(const base::Closure
& task
) {
72 base::MessageLoop::current()->PostTask(FROM_HERE
, task
);
75 void ImageWriter::PostProgress(int64 progress
) {
76 handler_
->SendProgress(progress
);
79 void ImageWriter::Error(const std::string
& message
) {
81 handler_
->SendFailed(message
);
84 bool ImageWriter::InitializeFiles() {
85 if (!image_file_
.IsValid()) {
86 image_file_
.Initialize(image_path_
,
87 base::File::FLAG_OPEN
| base::File::FLAG_READ
|
88 base::File::FLAG_EXCLUSIVE_READ
);
90 if (!image_file_
.IsValid()) {
91 DLOG(ERROR
) << "Unable to open file for read: " << image_path_
.value();
92 Error(error::kOpenImage
);
97 if (!device_file_
.IsValid()) {
99 Error(error::kOpenDevice
);
104 bytes_processed_
= 0;
110 void ImageWriter::WriteChunk() {
115 // DASD buffers require memory alignment on some systems.
116 scoped_ptr
<char, base::AlignedFreeDeleter
> buffer(static_cast<char*>(
117 base::AlignedAlloc(kBurningBlockSize
, kMemoryAlignment
)));
118 memset(buffer
.get(), 0, kBurningBlockSize
);
120 int bytes_read
= image_file_
.Read(bytes_processed_
, buffer
.get(),
123 if (bytes_read
> 0) {
124 // Always attempt to write a whole block, as writing DASD requires sector-
125 // aligned writes to devices.
126 int bytes_to_write
= bytes_read
+ (kMemoryAlignment
- 1) -
127 (bytes_read
- 1) % kMemoryAlignment
;
128 DCHECK_EQ(0, bytes_to_write
% kMemoryAlignment
);
130 device_file_
.Write(bytes_processed_
, buffer
.get(), bytes_to_write
);
132 if (bytes_written
< bytes_read
) {
133 Error(error::kWriteImage
);
137 bytes_processed_
+= bytes_read
;
138 PostProgress(bytes_processed_
);
140 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
141 } else if (bytes_read
== 0) {
143 device_file_
.Flush();
145 handler_
->SendSucceeded();
147 // Unable to read entire file.
148 Error(error::kReadImage
);
152 void ImageWriter::VerifyChunk() {
157 scoped_ptr
<char[]> image_buffer(new char[kBurningBlockSize
]);
158 // DASD buffers require memory alignment on some systems.
159 scoped_ptr
<char, base::AlignedFreeDeleter
> device_buffer(static_cast<char*>(
160 base::AlignedAlloc(kBurningBlockSize
, kMemoryAlignment
)));
162 int bytes_read
= image_file_
.Read(bytes_processed_
, image_buffer
.get(),
165 if (bytes_read
> 0) {
166 if (device_file_
.Read(bytes_processed_
,
168 kBurningBlockSize
) < bytes_read
) {
169 LOG(ERROR
) << "Failed to read " << bytes_read
<< " bytes of "
170 << "device at offset " << bytes_processed_
;
171 Error(error::kReadDevice
);
175 if (memcmp(image_buffer
.get(), device_buffer
.get(), bytes_read
) != 0) {
176 LOG(ERROR
) << "Write verification failed when comparing " << bytes_read
177 << " bytes at " << bytes_processed_
;
178 Error(error::kVerificationFailed
);
182 bytes_processed_
+= bytes_read
;
183 PostProgress(bytes_processed_
);
185 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
186 } else if (bytes_read
== 0) {
188 handler_
->SendSucceeded();
191 // Unable to read entire file.
192 LOG(ERROR
) << "Failed to read " << kBurningBlockSize
<< " bytes of image "
193 << "at offset " << bytes_processed_
;
194 Error(error::kReadImage
);
198 } // namespace image_writer