Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / utility / image_writer / image_writer.cc
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.
4
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"
10
11 namespace image_writer {
12
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;
17
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),
23       bytes_processed_(0),
24       running_(false),
25       handler_(handler) {}
26
27 ImageWriter::~ImageWriter() {
28 #if defined(OS_WIN)
29   for (std::vector<HANDLE>::const_iterator it = volume_handles_.begin();
30        it != volume_handles_.end();
31        ++it) {
32     CloseHandle(*it);
33   }
34 #endif
35 }
36
37 void ImageWriter::Write() {
38   if (!InitializeFiles()) {
39     return;
40   }
41
42   PostProgress(0);
43   PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
44 }
45
46 void ImageWriter::Verify() {
47   if (!InitializeFiles()) {
48     return;
49   }
50
51   PostProgress(0);
52   PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
53 }
54
55 void ImageWriter::Cancel() {
56   running_ = false;
57   handler_->SendCancelled();
58 }
59
60 bool ImageWriter::IsRunning() const { return running_; }
61
62 const base::FilePath& ImageWriter::GetImagePath() { return image_path_; }
63
64 const base::FilePath& ImageWriter::GetDevicePath() { return device_path_; }
65
66 void ImageWriter::PostTask(const base::Closure& task) {
67   base::MessageLoop::current()->PostTask(FROM_HERE, task);
68 }
69
70 void ImageWriter::PostProgress(int64 progress) {
71   handler_->SendProgress(progress);
72 }
73
74 void ImageWriter::Error(const std::string& message) {
75   running_ = false;
76   handler_->SendFailed(message);
77 }
78
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);
84
85     if (!image_file_.IsValid()) {
86       DLOG(ERROR) << "Unable to open file for read: " << image_path_.value();
87       Error(error::kOpenImage);
88       return false;
89     }
90   }
91
92   if (!device_file_.IsValid()) {
93 #if defined(OS_WIN)
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.
96     device_file_ =
97         base::File(CreateFile(device_path_.value().c_str(),
98                               GENERIC_READ | GENERIC_WRITE,
99                               FILE_SHARE_READ | FILE_SHARE_WRITE,
100                               NULL,
101                               OPEN_EXISTING,
102                               FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
103                               NULL));
104 #else
105     device_file_.Initialize(
106         device_path_,
107         base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE |
108             base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_EXCLUSIVE_WRITE);
109 #endif
110     if (!device_file_.IsValid()) {
111       Error(error::kOpenDevice);
112       return false;
113     }
114   }
115
116   bytes_processed_ = 0;
117   running_ = true;
118
119   return true;
120 }
121
122 void ImageWriter::WriteChunk() {
123   if (!IsRunning()) {
124     return;
125   }
126
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);
131
132   int bytes_read = image_file_.Read(bytes_processed_, buffer.get(),
133                                     kBurningBlockSize);
134
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;
140     int bytes_written =
141         device_file_.Write(bytes_processed_, buffer.get(), bytes_to_write);
142
143     if (bytes_written < bytes_read) {
144       Error(error::kWriteImage);
145       return;
146     }
147
148     bytes_processed_ += bytes_read;
149     PostProgress(bytes_processed_);
150
151     PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
152   } else if (bytes_read == 0) {
153     // End of file.
154     device_file_.Flush();
155     running_ = false;
156     handler_->SendSucceeded();
157   } else {
158     // Unable to read entire file.
159     Error(error::kReadImage);
160   }
161 }
162
163 void ImageWriter::VerifyChunk() {
164   if (!IsRunning()) {
165     return;
166   }
167
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)));
172
173   int bytes_read = image_file_.Read(bytes_processed_, image_buffer.get(),
174                                     kBurningBlockSize);
175
176   if (bytes_read > 0) {
177     if (device_file_.Read(bytes_processed_,
178                           device_buffer.get(),
179                           kBurningBlockSize) < bytes_read) {
180       LOG(ERROR) << "Failed to read " << bytes_read << " bytes of "
181                  << "device at offset " << bytes_processed_;
182       Error(error::kReadDevice);
183       return;
184     }
185
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);
190       return;
191     }
192
193     bytes_processed_ += bytes_read;
194     PostProgress(bytes_processed_);
195
196     PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
197   } else if (bytes_read == 0) {
198     // End of file.
199     handler_->SendSucceeded();
200     running_ = false;
201   } else {
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);
206   }
207 }
208
209 }  // namespace image_writer