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 "chrome/utility/image_writer/error_messages.h"
8 #include "chrome/utility/image_writer/image_writer_handler.h"
9 #include "content/public/utility/utility_thread.h"
11 namespace image_writer
{
13 // Since block devices like large sequential access and IPC is expensive we're
14 // doing work in 1MB chunks.
15 const int kBurningBlockSize
= 1 << 20;
17 ImageWriter::ImageWriter(ImageWriterHandler
* handler
)
18 : bytes_processed_(0),
21 ImageWriter::~ImageWriter() {
25 void ImageWriter::Write(const base::FilePath
& image_path
,
26 const base::FilePath
& device_path
) {
28 handler_
->SendFailed(error::kOperationAlreadyInProgress
);
32 image_path_
= image_path
;
33 device_path_
= device_path
;
36 image_file_
.Initialize(image_path_
,
37 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
39 if (!image_file_
.IsValid()) {
40 DLOG(ERROR
) << "Unable to open file for read: " << image_path_
.value();
41 Error(error::kOpenImage
);
46 // Windows requires that device files be opened with FILE_FLAG_NO_BUFFERING
47 // and FILE_FLAG_WRITE_THROUGH. These two flags are not part of base::File.
49 base::File(CreateFile(device_path
.value().c_str(),
54 FILE_FLAG_NO_BUFFERING
| FILE_FLAG_WRITE_THROUGH
,
57 if (!device_file_
.IsValid()) {
58 Error(error::kOpenDevice
);
62 device_file_
.Initialize(device_path_
,
63 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
65 if (!device_file_
.IsValid()) {
66 DLOG(ERROR
) << "Unable to open file for write(" <<
67 device_file_
.error_details() << "): " <<
69 Error(error::kOpenDevice
);
76 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
79 void ImageWriter::Verify(const base::FilePath
& image_path
,
80 const base::FilePath
& device_path
) {
82 handler_
->SendFailed(error::kOperationAlreadyInProgress
);
86 image_path_
= image_path
;
87 device_path_
= device_path
;
90 image_file_
.Initialize(image_path_
,
91 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
93 if (!image_file_
.IsValid()) {
94 DLOG(ERROR
) << "Unable to open file for read: " << image_path_
.value();
95 Error(error::kOpenImage
);
99 device_file_
.Initialize(device_path_
,
100 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
102 if (!device_file_
.IsValid()) {
103 DLOG(ERROR
) << "Unable to open file for read: " << device_path_
.value();
104 Error(error::kOpenDevice
);
110 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
113 void ImageWriter::Cancel() {
115 handler_
->SendCancelled();
118 bool ImageWriter::IsRunning() const {
119 return image_file_
.IsValid() || device_file_
.IsValid();
122 void ImageWriter::PostTask(const base::Closure
& task
) {
123 base::MessageLoop::current()->PostTask(FROM_HERE
, task
);
126 void ImageWriter::PostProgress(int64 progress
) {
127 handler_
->SendProgress(progress
);
130 void ImageWriter::Error(const std::string
& message
) {
132 handler_
->SendFailed(message
);
135 void ImageWriter::WriteChunk() {
140 scoped_ptr
<char[]> buffer(new char[kBurningBlockSize
]);
141 memset(buffer
.get(), 0, kBurningBlockSize
);
143 int bytes_read
= image_file_
.Read(bytes_processed_
, buffer
.get(),
146 if (bytes_read
> 0) {
147 // Always attempt to write a whole block, as Windows requires 512-byte
148 // aligned writes to devices.
149 int bytes_written
= device_file_
.Write(bytes_processed_
, buffer
.get(),
152 if (bytes_written
< bytes_read
) {
153 Error(error::kWriteImage
);
157 bytes_processed_
+= bytes_read
;
158 PostProgress(bytes_processed_
);
160 PostTask(base::Bind(&ImageWriter::WriteChunk
, AsWeakPtr()));
161 } else if (bytes_read
== 0) {
163 device_file_
.Flush();
165 handler_
->SendSucceeded();
167 // Unable to read entire file.
168 Error(error::kReadImage
);
172 void ImageWriter::VerifyChunk() {
177 scoped_ptr
<char[]> image_buffer(new char[kBurningBlockSize
]);
178 scoped_ptr
<char[]> device_buffer(new char[kBurningBlockSize
]);
180 int bytes_read
= image_file_
.Read(bytes_processed_
, image_buffer
.get(),
183 if (bytes_read
> 0) {
184 if (device_file_
.Read(bytes_processed_
,
186 kBurningBlockSize
) < bytes_read
) {
187 LOG(ERROR
) << "Failed to read " << kBurningBlockSize
<< " bytes of "
188 << "device at offset " << bytes_processed_
;
189 Error(error::kReadDevice
);
193 if (memcmp(image_buffer
.get(), device_buffer
.get(), bytes_read
) != 0) {
194 LOG(ERROR
) << "Write verification failed when comparing " << bytes_read
195 << " bytes at " << bytes_processed_
;
196 Error(error::kVerificationFailed
);
200 bytes_processed_
+= bytes_read
;
201 PostProgress(bytes_processed_
);
203 PostTask(base::Bind(&ImageWriter::VerifyChunk
, AsWeakPtr()));
204 } else if (bytes_read
== 0) {
207 handler_
->SendSucceeded();
209 // Unable to read entire file.
210 LOG(ERROR
) << "Failed to read " << kBurningBlockSize
<< " bytes of image "
211 << "at offset " << bytes_processed_
;
212 Error(error::kReadImage
);
216 void ImageWriter::CleanUp() {
218 device_file_
.Close();
221 } // namespace image_writer