de2caebe2a97a038f5a62c60ed63a323f9e8764b
[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 "chrome/utility/image_writer/image_writer.h"
6
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"
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;
16
17 ImageWriter::ImageWriter(ImageWriterHandler* handler)
18     : bytes_processed_(0),
19       handler_(handler) {}
20
21 ImageWriter::~ImageWriter() {
22   CleanUp();
23 }
24
25 void ImageWriter::Write(const base::FilePath& image_path,
26                         const base::FilePath& device_path) {
27   if (IsRunning()) {
28     handler_->SendFailed(error::kOperationAlreadyInProgress);
29     return;
30   }
31
32   image_path_ = image_path;
33   device_path_ = device_path;
34   bytes_processed_ = 0;
35
36   image_file_.Initialize(image_path_,
37                          base::File::FLAG_OPEN | base::File::FLAG_READ);
38
39   if (!image_file_.IsValid()) {
40     DLOG(ERROR) << "Unable to open file for read: " << image_path_.value();
41     Error(error::kOpenImage);
42     return;
43   }
44
45 #if defined(OS_WIN)
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.
48   device_file_ =
49       base::File(CreateFile(device_path.value().c_str(),
50                             GENERIC_WRITE,
51                             FILE_SHARE_WRITE,
52                             NULL,
53                             OPEN_EXISTING,
54                             FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
55                             NULL));
56
57   if (!device_file_.IsValid()) {
58     Error(error::kOpenDevice);
59     return;
60   }
61 #else
62   device_file_.Initialize(device_path_,
63                           base::File::FLAG_OPEN | base::File::FLAG_WRITE);
64
65   if (!device_file_.IsValid()) {
66     DLOG(ERROR) << "Unable to open file for write(" <<
67                    device_file_.error_details() << "): " <<
68                    device_path_.value();
69     Error(error::kOpenDevice);
70     return;
71   }
72 #endif
73
74   PostProgress(0);
75
76   PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
77 }
78
79 void ImageWriter::Verify(const base::FilePath& image_path,
80                          const base::FilePath& device_path) {
81   if (IsRunning()) {
82     handler_->SendFailed(error::kOperationAlreadyInProgress);
83     return;
84   }
85
86   image_path_ = image_path;
87   device_path_ = device_path;
88   bytes_processed_ = 0;
89
90   image_file_.Initialize(image_path_,
91                          base::File::FLAG_OPEN | base::File::FLAG_READ);
92
93   if (!image_file_.IsValid()) {
94     DLOG(ERROR) << "Unable to open file for read: " << image_path_.value();
95     Error(error::kOpenImage);
96     return;
97   }
98
99   device_file_.Initialize(device_path_,
100                           base::File::FLAG_OPEN | base::File::FLAG_READ);
101
102   if (!device_file_.IsValid()) {
103     DLOG(ERROR) << "Unable to open file for read: " << device_path_.value();
104     Error(error::kOpenDevice);
105     return;
106   }
107
108   PostProgress(0);
109
110   PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
111 }
112
113 void ImageWriter::Cancel() {
114   CleanUp();
115   handler_->SendCancelled();
116 }
117
118 bool ImageWriter::IsRunning() const {
119   return image_file_.IsValid() || device_file_.IsValid();
120 }
121
122 void ImageWriter::PostTask(const base::Closure& task) {
123   base::MessageLoop::current()->PostTask(FROM_HERE, task);
124 }
125
126 void ImageWriter::PostProgress(int64 progress) {
127   handler_->SendProgress(progress);
128 }
129
130 void ImageWriter::Error(const std::string& message) {
131   CleanUp();
132   handler_->SendFailed(message);
133 }
134
135 void ImageWriter::WriteChunk() {
136   if (!IsRunning()) {
137     return;
138   }
139
140   scoped_ptr<char[]> buffer(new char[kBurningBlockSize]);
141   memset(buffer.get(), 0, kBurningBlockSize);
142
143   int bytes_read = image_file_.Read(bytes_processed_, buffer.get(),
144                                     kBurningBlockSize);
145
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(),
150                                            kBurningBlockSize);
151
152     if (bytes_written < bytes_read) {
153       Error(error::kWriteImage);
154       return;
155     }
156
157     bytes_processed_ += bytes_read;
158     PostProgress(bytes_processed_);
159
160     PostTask(base::Bind(&ImageWriter::WriteChunk, AsWeakPtr()));
161   } else if (bytes_read == 0) {
162     // End of file.
163     device_file_.Flush();
164     CleanUp();
165     handler_->SendSucceeded();
166   } else {
167     // Unable to read entire file.
168     Error(error::kReadImage);
169   }
170 }
171
172 void ImageWriter::VerifyChunk() {
173   if (!IsRunning()) {
174     return;
175   }
176
177   scoped_ptr<char[]> image_buffer(new char[kBurningBlockSize]);
178   scoped_ptr<char[]> device_buffer(new char[kBurningBlockSize]);
179
180   int bytes_read = image_file_.Read(bytes_processed_, image_buffer.get(),
181                                     kBurningBlockSize);
182
183   if (bytes_read > 0) {
184     if (device_file_.Read(bytes_processed_,
185                           device_buffer.get(),
186                           kBurningBlockSize) < bytes_read) {
187       LOG(ERROR) << "Failed to read " << kBurningBlockSize << " bytes of "
188                  << "device at offset " << bytes_processed_;
189       Error(error::kReadDevice);
190       return;
191     }
192
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);
197       return;
198     }
199
200     bytes_processed_ += bytes_read;
201     PostProgress(bytes_processed_);
202
203     PostTask(base::Bind(&ImageWriter::VerifyChunk, AsWeakPtr()));
204   } else if (bytes_read == 0) {
205     // End of file.
206     CleanUp();
207     handler_->SendSucceeded();
208   } else {
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);
213   }
214 }
215
216 void ImageWriter::CleanUp() {
217   image_file_.Close();
218   device_file_.Close();
219 }
220
221 }  // namespace image_writer