1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #ifndef SYZYGY_MSF_MSF_WRITER_IMPL_H_
16 : #define SYZYGY_MSF_MSF_WRITER_IMPL_H_
17 :
18 : #include <cstdio>
19 : #include <cstring>
20 : #include <vector>
21 :
22 : #include "base/logging.h"
23 : #include "syzygy/msf/msf_constants.h"
24 : #include "syzygy/msf/msf_data.h"
25 :
26 : namespace msf {
27 : namespace detail {
28 :
29 : namespace {
30 :
31 : const uint32_t kZeroBuffer[kMsfPageSize] = {0};
32 :
33 : // A byte-based bitmap for keeping track of free pages in an MSF file.
34 : // TODO(chrisha): Promote this to its own file and unittest it when we make
35 : // a library for MSF-specific stuff.
36 : class FreePageBitMap {
37 : public:
38 E : FreePageBitMap() : page_count_(0) {}
39 :
40 E : void SetPageCount(uint32_t page_count) {
41 E : page_count_ = page_count;
42 E : data_.resize((page_count + 7) / 8);
43 :
44 : // Double check our invariant.
45 E : DCHECK_LE(page_count, data_.size() * 8);
46 E : DCHECK_LE(page_count / 8, data_.size());
47 E : }
48 :
49 E : void SetBit(uint32_t page_index, bool free) {
50 E : DCHECK_LT(page_index, page_count_);
51 :
52 E : uint32_t byte = page_index / 8;
53 E : uint32_t bit = page_index % 8;
54 E : uint8_t bitmask = 1 << bit;
55 E : DCHECK_LT(byte, data_.size());
56 :
57 E : if (free) {
58 E : data_[byte] |= bitmask;
59 E : } else {
60 i : data_[byte] &= ~bitmask;
61 : }
62 E : }
63 :
64 E : void SetFree(uint32_t page_index) { SetBit(page_index, true); }
65 : void SetUsed(uint32_t page_index) { SetBit(page_index, false); }
66 :
67 : // TODO(chrisha): Make this an invariant of the class and move the logic
68 : // to SetPageCount. This involves both clearing and setting bits in that
69 : // case.
70 E : void Finalize() {
71 E : uint32_t bits_left = static_cast<uint32_t>(data_.size()) * 8 - page_count_;
72 E : DCHECK_LE(bits_left, 7u);
73 :
74 : // This leaves the top |bits_left| bits set.
75 E : uint8_t bitmask = ~(0xFF >> bits_left);
76 :
77 : // Mark any bits as free beyond those specifically allocated.
78 E : data_.back() |= bitmask;
79 E : }
80 :
81 E : const std::vector<uint8_t>& data() const { return data_; }
82 :
83 : private:
84 : std::vector<uint8_t> data_;
85 : uint32_t page_count_;
86 : };
87 :
88 : // A light-weight wrapper that allows a previously allocated buffer to be read
89 : // as an MsfStreamImpl.
90 : template <MsfFileType T>
91 : class ReadOnlyMsfStream : public MsfStreamImpl<T> {
92 : public:
93 : ReadOnlyMsfStream(const void* data, size_t bytes)
94 E : : MsfStreamImpl(bytes), data_(data) {}
95 :
96 E : bool ReadBytesAt(size_t pos, size_t count, void* dest) override {
97 E : DCHECK(dest != NULL);
98 :
99 E : if (count > length() - pos)
100 i : return false;
101 :
102 E : ::memcpy(dest, reinterpret_cast<const uint8_t*>(data_) + pos, count);
103 :
104 E : return true;
105 E : }
106 :
107 : private:
108 : const void* data_;
109 : };
110 :
111 : // Appends a page to the provided file, adding the written page ID to the vector
112 : // of @p pages_written, and incrementing the total @P page_count. This will
113 : // occasionally cause more than one single page to be written to the output,
114 : // thus advancing @p page_count by more than one (when reserving pages for the
115 : // free page map). It is expected that @data be kMsfPageSize in length.
116 : // @pre the file is expected to be positioned at @p *page_count * kMsfPageSize
117 : // when entering this routine
118 : // @post the file will be positioned at @p *page_count * kMsfPageSiz when
119 : // exiting this routine.
120 : bool AppendPage(const void* data,
121 : std::vector<uint32_t>* pages_written,
122 : uint32_t* page_count,
123 E : FILE* file) {
124 E : DCHECK(data != NULL);
125 E : DCHECK(pages_written != NULL);
126 E : DCHECK(page_count != NULL);
127 E : DCHECK(file != NULL);
128 :
129 E : uint32_t local_page_count = *page_count;
130 :
131 : // The file is written sequentially, so it will already be pointing to
132 : // the appropriate spot.
133 E : DCHECK_EQ(local_page_count * kMsfPageSize,
134 E : static_cast<uint32_t>(::ftell(file)));
135 :
136 : // If we're due to allocate pages for the free page map, then do so.
137 E : if (((*page_count) % kMsfPageSize) == 1) {
138 E : if (::fwrite(kZeroBuffer, 1, kMsfPageSize, file) != kMsfPageSize ||
139 : ::fwrite(kZeroBuffer, 1, kMsfPageSize, file) != kMsfPageSize) {
140 i : LOG(ERROR) << "Failed to allocate free page map pages.";
141 i : return false;
142 : }
143 E : local_page_count += 2;
144 : }
145 :
146 : // Write the page itself.
147 E : if (::fwrite(data, 1, kMsfPageSize, file) != kMsfPageSize) {
148 i : LOG(ERROR) << "Failed to write page " << *page_count << ".";
149 i : return false;
150 : }
151 E : pages_written->push_back(local_page_count);
152 E : ++local_page_count;
153 :
154 E : DCHECK_EQ(local_page_count * kMsfPageSize,
155 E : static_cast<uint32_t>(::ftell(file)));
156 :
157 E : *page_count = local_page_count;
158 E : return true;
159 E : }
160 :
161 E : bool WriteFreePageBitMap(const FreePageBitMap& free, FILE* file) {
162 E : DCHECK(file != NULL);
163 :
164 E : const uint8_t* data = free.data().data();
165 E : size_t bytes_left = free.data().size();
166 E : size_t page_index = 1;
167 E : size_t bytes_to_write = kMsfPageSize;
168 E : while (true) {
169 : if (::fseek(file,
170 : static_cast<long>(page_index * kMsfPageSize),
171 E : SEEK_SET) != 0) {
172 i : LOG(ERROR) << "Failed to seek to page " << page_index << ".";
173 i : return false;
174 : }
175 :
176 E : bytes_to_write = kMsfPageSize;
177 E : if (bytes_left < bytes_to_write)
178 E : bytes_to_write = bytes_left;
179 :
180 E : if (::fwrite(data, 1, bytes_to_write, file) != bytes_to_write) {
181 i : LOG(ERROR) << "Failed to write page " << page_index
182 : << " of free page map.";
183 i : return false;
184 : }
185 :
186 E : bytes_left -= bytes_to_write;
187 E : if (bytes_left == 0)
188 E : break;
189 :
190 i : data += bytes_to_write;
191 i : page_index += kMsfPageSize;
192 i : }
193 :
194 : // Was the last write partial? If so, we need to flush out the rest of the
195 : // free page map with ones (0xFF bytes).
196 E : if (bytes_to_write < kMsfPageSize) {
197 : // Create a vector of bytes with all the bits set.
198 E : std::vector<uint8_t> ones(kMsfPageSize - bytes_to_write, 0xFF);
199 E : if (::fwrite(ones.data(), 1, ones.size(), file) != ones.size()) {
200 i : LOG(ERROR) << "Failed to pad page " << page_index << " of free page map.";
201 i : return false;
202 : }
203 E : }
204 :
205 E : return true;
206 E : }
207 :
208 : } // namespace
209 :
210 : template <MsfFileType T>
211 E : MsfWriterImpl<T>::MsfWriterImpl() {
212 E : }
213 :
214 : template <MsfFileType T>
215 E : MsfWriterImpl<T>::~MsfWriterImpl() {
216 E : }
217 :
218 : template <MsfFileType T>
219 : bool MsfWriterImpl<T>::Write(const base::FilePath& msf_path,
220 E : const MsfFileImpl<T>& msf_file) {
221 E : file_.reset(base::OpenFile(msf_path, "wb"));
222 E : if (!file_.get()) {
223 i : LOG(ERROR) << "Failed to create '" << msf_path.value() << "'.";
224 i : return false;
225 : }
226 :
227 : // Initialize the directory with stream count and lengths.
228 E : std::vector<uint32_t> directory;
229 E : directory.push_back(static_cast<uint32_t>(msf_file.StreamCount()));
230 E : for (uint32_t i = 0; i < msf_file.StreamCount(); ++i) {
231 : // Null streams have an implicit zero length.
232 E : MsfStreamImpl<T>* stream = msf_file.GetStream(i).get();
233 E : if (stream == NULL)
234 E : directory.push_back(0);
235 E : else
236 E : directory.push_back(static_cast<uint32_t>(stream->length()));
237 E : }
238 :
239 : // Reserve space for the header page, the two free page map pages, and a
240 : // fourth empty page. The fourth empty page doesn't appear to be strictly
241 : // necessary but MSF files produced by MS tools always contain it.
242 E : uint32_t page_count = 4;
243 E : for (uint32_t i = 0; i < page_count; ++i) {
244 E : if (::fwrite(kZeroBuffer, 1, kMsfPageSize, file_.get()) != kMsfPageSize) {
245 i : LOG(ERROR) << "Failed to allocate preamble page.";
246 i : return false;
247 : }
248 E : }
249 :
250 : // Append all the streams after the preamble and build the directory while
251 : // we're at it. We keep track of which pages host stream 0 for some free page
252 : // map bookkeeping later on.
253 E : size_t stream0_start = directory.size();
254 E : size_t stream0_end = 0;
255 E : for (uint32_t i = 0; i < msf_file.StreamCount(); ++i) {
256 E : if (i == 1)
257 E : stream0_end = directory.size();
258 :
259 : // Null streams are treated as empty streams.
260 E : MsfStreamImpl<T>* stream = msf_file.GetStream(i).get();
261 E : if (stream == NULL || stream->length() == 0)
262 E : continue;
263 :
264 : // Write the stream, updating the directory and page index. This routine
265 : // takes care of making room for the free page map pages.
266 E : if (!AppendStream(stream, &directory, &page_count)) {
267 i : LOG(ERROR) << "Failed to write stream " << i << ".";
268 i : return false;
269 : }
270 E : }
271 E : DCHECK_LE(stream0_start, stream0_end);
272 :
273 : // Write the directory, and keep track of the pages it is written to.
274 E : std::vector<uint32_t> directory_pages;
275 E : scoped_refptr<MsfStreamImpl<T>> directory_stream(new ReadOnlyMsfStream<T>(
276 : directory.data(), sizeof(directory[0]) * directory.size()));
277 E : if (!AppendStream(directory_stream.get(), &directory_pages, &page_count)) {
278 i : LOG(ERROR) << "Failed to write directory.";
279 i : return false;
280 : }
281 :
282 : // Write the root directory, and keep track of the pages it is written to.
283 : // These will in turn go into the header root directory pointers.
284 E : std::vector<uint32_t> root_directory_pages;
285 E : scoped_refptr<MsfStreamImpl<T>> root_directory_stream(
286 : new ReadOnlyMsfStream<T>(
287 : directory_pages.data(),
288 : sizeof(directory_pages[0]) * directory_pages.size()));
289 E : if (!AppendStream(root_directory_stream.get(), &root_directory_pages,
290 : &page_count)) {
291 i : LOG(ERROR) << "Failed to write root directory.";
292 i : return false;
293 : }
294 :
295 : // Write the header.
296 E : if (!WriteHeader(root_directory_pages,
297 : static_cast<uint32_t>(
298 : sizeof(directory[0]) * directory.size()),
299 : page_count)) {
300 i : LOG(ERROR) << "Failed to write MSF header.";
301 i : return false;
302 : }
303 :
304 : // Initialize the free page bit map. The pages corresponding to stream 0 are
305 : // always marked as free, as well as page 3 which we allocated in the
306 : // preamble.
307 E : FreePageBitMap free_page;
308 E : free_page.SetPageCount(page_count);
309 E : free_page.SetFree(3);
310 E : for (size_t i = stream0_start; i < stream0_end; ++i)
311 E : free_page.SetFree(directory[i]);
312 E : free_page.Finalize();
313 :
314 E : if (!WriteFreePageBitMap(free_page, file_.get())) {
315 i : LOG(ERROR) << "Failed to write free page bitmap.";
316 i : return false;
317 : }
318 :
319 : // On success we want the file to be closed right away.
320 E : file_.reset();
321 :
322 E : return true;
323 E : }
324 :
325 : template <MsfFileType T>
326 : bool MsfWriterImpl<T>::AppendStream(MsfStreamImpl<T>* stream,
327 : std::vector<uint32_t>* pages_written,
328 E : uint32_t* page_count) {
329 E : DCHECK(stream != NULL);
330 E : DCHECK(pages_written != NULL);
331 E : DCHECK(page_count != NULL);
332 :
333 : #ifndef NDEBUG
334 E : size_t old_pages_written_count = pages_written->size();
335 : #endif
336 :
337 : // Write the stream page by page.
338 E : uint8_t buffer[kMsfPageSize] = {0};
339 E : size_t bytes_left = stream->length();
340 E : size_t bytes_read = 0;
341 E : while (bytes_left) {
342 E : size_t bytes_to_read = sizeof(buffer);
343 E : if (bytes_to_read > bytes_left) {
344 E : bytes_to_read = bytes_left;
345 :
346 : // If we're only reading a partial buffer then pad the end of it with
347 : // zeros.
348 E : ::memset(buffer + bytes_to_read, 0, sizeof(buffer) - bytes_to_read);
349 : }
350 :
351 : // Read the buffer from the stream.
352 E : if (!stream->ReadBytesAt(bytes_read, bytes_to_read, buffer)) {
353 i : size_t offset = stream->length() - bytes_left;
354 i : LOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset "
355 : << offset << " of MSF stream.";
356 i : return false;
357 : }
358 E : if (!AppendPage(buffer, pages_written, page_count, file_.get()))
359 i : return false;
360 :
361 E : bytes_read += bytes_to_read;
362 E : bytes_left -= bytes_to_read;
363 E : }
364 E : DCHECK_EQ(0u, bytes_left);
365 :
366 : #ifndef NDEBUG
367 : size_t expected_pages_written =
368 E : (stream->length() + kMsfPageSize - 1) / kMsfPageSize;
369 E : DCHECK_EQ(old_pages_written_count + expected_pages_written,
370 : pages_written->size());
371 : // We can't say anything about |page_count| as AppendPage occasionally snags
372 : // extra pages for the free page map.
373 : #endif
374 :
375 E : return true;
376 E : }
377 :
378 : template <MsfFileType T>
379 : bool MsfWriterImpl<T>::WriteHeader(
380 : const std::vector<uint32_t>& root_directory_pages,
381 : uint32_t directory_size,
382 E : uint32_t page_count) {
383 E : VLOG(1) << "Writing MSF Header ...";
384 :
385 E : MsfHeader header = {0};
386 :
387 : // Make sure the root directory pointers won't overflow.
388 E : if (root_directory_pages.size() > arraysize(header.root_pages)) {
389 E : LOG(ERROR) << "Too many root directory pages for header ("
390 : << root_directory_pages.size() << " > "
391 : << arraysize(header.root_pages) << ").";
392 E : return false;
393 : }
394 :
395 : // Seek to the beginning of the file so we can stamp in the header.
396 E : if (::fseek(file_.get(), 0, SEEK_SET) != 0) {
397 i : LOG(ERROR) << "Seek failed while writing header.";
398 i : return false;
399 : }
400 :
401 E : ::memcpy(header.magic_string, kMsfHeaderMagicString,
402 : sizeof(kMsfHeaderMagicString));
403 E : header.page_size = kMsfPageSize;
404 E : header.free_page_map = 1;
405 E : header.num_pages = page_count;
406 E : header.directory_size = directory_size;
407 E : header.reserved = 0;
408 E : ::memcpy(header.root_pages, root_directory_pages.data(),
409 : sizeof(root_directory_pages[0]) * root_directory_pages.size());
410 :
411 E : if (::fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
412 i : LOG(ERROR) << "Failed to write header.";
413 i : return false;
414 : }
415 :
416 E : return true;
417 E : }
418 :
419 : } // namespace detail
420 : } // namespace msf
421 :
422 : #endif // SYZYGY_MSF_MSF_WRITER_IMPL_H_
|