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 "base/memory/aligned_memory.h"
6 #include "chrome/utility/image_writer/error_messages.h"
7 #include "chrome/utility/image_writer/image_writer.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; // 1 MB
16 const int kMemoryAlignment = 4096;
18 ImageWriter::ImageWriter(ImageWriterHandler* handler,
19 const base::FilePath& image_path,
20 const base::FilePath& device_path)
21 : image_path_(image_path),
22 device_path_(device_path),
27 ImageWriter::~ImageWriter() {
29 for (std::vector<HANDLE>::const_iterator it = volume_handles_.begin();
30 it != volume_handles_.end();
37 void ImageWriter::Write() {
38 if (!InitializeFiles()) {
43 PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
46 void ImageWriter::Verify() {
47 if (!InitializeFiles()) {
52 PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
55 void ImageWriter::Cancel() {
57 handler_->SendCancelled();
60 bool ImageWriter::IsRunning() const { return running_; }
62 const base::FilePath& ImageWriter::GetImagePath() { return image_path_; }
64 const base::FilePath& ImageWriter::GetDevicePath() { return device_path_; }
66 void ImageWriter::PostTask(const base::Closure& task) {
67 base::MessageLoop::current()->PostTask(FROM_HERE, task);
70 void ImageWriter::PostProgress(int64 progress) {
71 handler_->SendProgress(progress);
74 void ImageWriter::Error(const std::string& message) {
76 handler_->SendFailed(message);
79 bool ImageWriter::InitializeFiles() {
80 if (!image_file_.IsValid()) {
81 image_file_.Initialize(image_path_,
82 base::File::FLAG_OPEN | base::File::FLAG_READ |
83 base::File::FLAG_EXCLUSIVE_READ);
85 if (!image_file_.IsValid()) {
86 DLOG(ERROR) << "Unable to open file for read: " << image_path_.value();
87 Error(error::kOpenImage);
92 if (!device_file_.IsValid()) {
94 // Windows requires that device files be opened with FILE_FLAG_NO_BUFFERING
95 // and FILE_FLAG_WRITE_THROUGH. These two flags are not part of base::File.
97 base::File(CreateFile(device_path_.value().c_str(),
98 GENERIC_READ | GENERIC_WRITE,
99 FILE_SHARE_READ | FILE_SHARE_WRITE,
102 FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
105 device_file_.Initialize(
107 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE |
108 base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_EXCLUSIVE_WRITE);
110 if (!device_file_.IsValid()) {
111 Error(error::kOpenDevice);
116 bytes_processed_ = 0;
122 void ImageWriter::WriteChunk() {
127 // DASD buffers require memory alignment on some systems.
128 scoped_ptr<char, base::AlignedFreeDeleter> buffer(static_cast<char*>(
129 base::AlignedAlloc(kBurningBlockSize, kMemoryAlignment)));
130 memset(buffer.get(), 0, kBurningBlockSize);
132 int bytes_read = image_file_.Read(bytes_processed_, buffer.get(),
135 if (bytes_read > 0) {
136 // Always attempt to write a whole block, as writing DASD requires sector-
137 // aligned writes to devices.
138 int bytes_to_write = bytes_read + (kMemoryAlignment - 1) -
139 (bytes_read - 1) % kMemoryAlignment;
141 device_file_.Write(bytes_processed_, buffer.get(), bytes_to_write);
143 if (bytes_written < bytes_read) {
144 Error(error::kWriteImage);
148 bytes_processed_ += bytes_read;
149 PostProgress(bytes_processed_);
151 PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
152 } else if (bytes_read == 0) {
154 device_file_.Flush();
156 handler_->SendSucceeded();
158 // Unable to read entire file.
159 Error(error::kReadImage);
163 void ImageWriter::VerifyChunk() {
168 scoped_ptr<char[]> image_buffer(new char[kBurningBlockSize]);
169 // DASD buffers require memory alignment on some systems.
170 scoped_ptr<char, base::AlignedFreeDeleter> device_buffer(static_cast<char*>(
171 base::AlignedAlloc(kBurningBlockSize, kMemoryAlignment)));
173 int bytes_read = image_file_.Read(bytes_processed_, image_buffer.get(),
176 if (bytes_read > 0) {
177 if (device_file_.Read(bytes_processed_,
179 kBurningBlockSize) < bytes_read) {
180 LOG(ERROR) << "Failed to read " << bytes_read << " bytes of "
181 << "device at offset " << bytes_processed_;
182 Error(error::kReadDevice);
186 if (memcmp(image_buffer.get(), device_buffer.get(), bytes_read) != 0) {
187 LOG(ERROR) << "Write verification failed when comparing " << bytes_read
188 << " bytes at " << bytes_processed_;
189 Error(error::kVerificationFailed);
193 bytes_processed_ += bytes_read;
194 PostProgress(bytes_processed_);
196 PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
197 } else if (bytes_read == 0) {
199 handler_->SendSucceeded();
202 // Unable to read entire file.
203 LOG(ERROR) << "Failed to read " << kBurningBlockSize << " bytes of image "
204 << "at offset " << bytes_processed_;
205 Error(error::kReadImage);
209 } // namespace image_writer