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