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 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 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 page_index, bool free) {
50 E : DCHECK_LT(page_index, page_count_);
51 :
52 E : uint32 byte = page_index / 8;
53 E : uint32 bit = page_index % 8;
54 E : uint8 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 page_index) { SetBit(page_index, true); }
65 : void SetUsed(uint32 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 bits_left = 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 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>& data() const { return data_; }
82 :
83 : private:
84 : std::vector<uint8> data_;
85 : uint32 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 : virtual bool ReadBytes(void* dest,
97 : size_t count,
98 E : size_t* bytes_read) override {
99 E : DCHECK(dest != NULL);
100 E : DCHECK(bytes_read != NULL);
101 :
102 E : bool result = true;
103 E : size_t bytes_to_read = count;
104 E : size_t bytes_left = length() - pos();
105 E : if (bytes_left < bytes_to_read) {
106 i : bytes_to_read = bytes_left;
107 i : result = false;
108 : }
109 :
110 : ::memcpy(dest, reinterpret_cast<const uint8*>(data_) + pos(),
111 E : bytes_to_read);
112 E : Seek(pos() + bytes_to_read);
113 :
114 E : *bytes_read = bytes_to_read;
115 E : return result;
116 E : }
117 :
118 : private:
119 : const void* data_;
120 : };
121 :
122 : // Appends a page to the provided file, adding the written page ID to the vector
123 : // of @p pages_written, and incrementing the total @P page_count. This will
124 : // occasionally cause more than one single page to be written to the output,
125 : // thus advancing @p page_count by more than one (when reserving pages for the
126 : // free page map). It is expected that @data be kMsfPageSize in length.
127 : // @pre the file is expected to be positioned at @p *page_count * kMsfPageSize
128 : // when entering this routine
129 : // @post the file will be positioned at @p *page_count * kMsfPageSiz when
130 : // exiting this routine.
131 : bool AppendPage(const void* data,
132 : std::vector<uint32>* pages_written,
133 : uint32* page_count,
134 E : FILE* file) {
135 E : DCHECK(data != NULL);
136 E : DCHECK(pages_written != NULL);
137 E : DCHECK(page_count != NULL);
138 E : DCHECK(file != NULL);
139 :
140 E : uint32 local_page_count = *page_count;
141 :
142 : // The file is written sequentially, so it will already be pointing to
143 : // the appropriate spot.
144 : DCHECK_EQ(local_page_count * kMsfPageSize,
145 E : static_cast<uint32>(::ftell(file)));
146 :
147 : // If we're due to allocate pages for the free page map, then do so.
148 E : if (((*page_count) % kMsfPageSize) == 1) {
149 : if (::fwrite(kZeroBuffer, 1, kMsfPageSize, file) != kMsfPageSize ||
150 E : ::fwrite(kZeroBuffer, 1, kMsfPageSize, file) != kMsfPageSize) {
151 i : LOG(ERROR) << "Failed to allocate free page map pages.";
152 i : return false;
153 : }
154 E : local_page_count += 2;
155 : }
156 :
157 : // Write the page itself.
158 E : if (::fwrite(data, 1, kMsfPageSize, file) != kMsfPageSize) {
159 i : LOG(ERROR) << "Failed to write page " << *page_count << ".";
160 i : return false;
161 : }
162 E : pages_written->push_back(local_page_count);
163 E : ++local_page_count;
164 :
165 : DCHECK_EQ(local_page_count * kMsfPageSize,
166 E : static_cast<uint32>(::ftell(file)));
167 :
168 E : *page_count = local_page_count;
169 E : return true;
170 E : }
171 :
172 E : bool WriteFreePageBitMap(const FreePageBitMap& free, FILE* file) {
173 E : DCHECK(file != NULL);
174 :
175 E : const uint8* data = free.data().data();
176 E : size_t bytes_left = free.data().size();
177 E : size_t page_index = 1;
178 E : size_t bytes_to_write = kMsfPageSize;
179 E : while (true) {
180 E : if (::fseek(file, page_index * kMsfPageSize, SEEK_SET) != 0) {
181 i : LOG(ERROR) << "Failed to seek to page " << page_index << ".";
182 i : return false;
183 : }
184 :
185 E : bytes_to_write = kMsfPageSize;
186 E : if (bytes_left < bytes_to_write)
187 E : bytes_to_write = bytes_left;
188 :
189 E : if (::fwrite(data, 1, bytes_to_write, file) != bytes_to_write) {
190 i : LOG(ERROR) << "Failed to write page " << page_index
191 : << " of free page map.";
192 i : return false;
193 : }
194 :
195 E : bytes_left -= bytes_to_write;
196 E : if (bytes_left == 0)
197 E : break;
198 :
199 i : data += bytes_to_write;
200 i : page_index += kMsfPageSize;
201 i : }
202 :
203 : // Was the last write partial? If so, we need to flush out the rest of the
204 : // free page map with ones (0xFF bytes).
205 E : if (bytes_to_write < kMsfPageSize) {
206 : // Create a vector of bytes with all the bits set.
207 E : std::vector<uint8> ones(kMsfPageSize - bytes_to_write, 0xFF);
208 E : if (::fwrite(ones.data(), 1, ones.size(), file) != ones.size()) {
209 i : LOG(ERROR) << "Failed to pad page " << page_index << " of free page map.";
210 i : return false;
211 : }
212 E : }
213 :
214 E : return true;
215 E : }
216 :
217 : } // namespace
218 :
219 : template <MsfFileType T>
220 E : MsfWriterImpl<T>::MsfWriterImpl() {
221 E : }
222 :
223 : template <MsfFileType T>
224 E : MsfWriterImpl<T>::~MsfWriterImpl() {
225 E : }
226 :
227 : template <MsfFileType T>
228 : bool MsfWriterImpl<T>::Write(const base::FilePath& msf_path,
229 E : const MsfFileImpl<T>& msf_file) {
230 E : file_.reset(base::OpenFile(msf_path, "wb"));
231 E : if (!file_.get()) {
232 i : LOG(ERROR) << "Failed to create '" << msf_path.value() << "'.";
233 i : return false;
234 : }
235 :
236 : // Initialize the directory with stream count and lengths.
237 E : std::vector<uint32> directory;
238 E : directory.push_back(msf_file.StreamCount());
239 E : for (size_t i = 0; i < msf_file.StreamCount(); ++i) {
240 : // Null streams have an implicit zero length.
241 E : MsfStreamImpl<T>* stream = msf_file.GetStream(i).get();
242 E : if (stream == NULL)
243 E : directory.push_back(0);
244 E : else
245 E : directory.push_back(stream->length());
246 E : }
247 :
248 : // Reserve space for the header page, the two free page map pages, and a
249 : // fourth empty page. The fourth empty page doesn't appear to be strictly
250 : // necessary but MSF files produced by MS tools always contain it.
251 E : uint32 page_count = 4;
252 E : for (uint32 i = 0; i < page_count; ++i) {
253 E : if (::fwrite(kZeroBuffer, 1, kMsfPageSize, file_.get()) != kMsfPageSize) {
254 i : LOG(ERROR) << "Failed to allocate preamble page.";
255 i : return false;
256 : }
257 E : }
258 :
259 : // Append all the streams after the preamble and build the directory while
260 : // we're at it. We keep track of which pages host stream 0 for some free page
261 : // map bookkeeping later on.
262 E : size_t stream0_start = directory.size();
263 E : size_t stream0_end = 0;
264 E : for (size_t i = 0; i < msf_file.StreamCount(); ++i) {
265 E : if (i == 1)
266 E : stream0_end = directory.size();
267 :
268 : // Null streams are treated as empty streams.
269 E : MsfStreamImpl<T>* stream = msf_file.GetStream(i).get();
270 E : if (stream == NULL || stream->length() == 0)
271 E : continue;
272 :
273 : // Write the stream, updating the directory and page index. This routine
274 : // takes care of making room for the free page map pages.
275 E : if (!AppendStream(stream, &directory, &page_count)) {
276 i : LOG(ERROR) << "Failed to write stream " << i << ".";
277 i : return false;
278 : }
279 E : }
280 E : DCHECK_LE(stream0_start, stream0_end);
281 :
282 : // Write the directory, and keep track of the pages it is written to.
283 E : std::vector<uint32> directory_pages;
284 : scoped_refptr<MsfStreamImpl<T>> directory_stream(new ReadOnlyMsfStream<T>(
285 E : directory.data(), sizeof(directory[0]) * directory.size()));
286 E : if (!AppendStream(directory_stream.get(), &directory_pages, &page_count)) {
287 i : LOG(ERROR) << "Failed to write directory.";
288 i : return false;
289 : }
290 :
291 : // Write the root directory, and keep track of the pages it is written to.
292 : // These will in turn go into the header root directory pointers.
293 E : std::vector<uint32> root_directory_pages;
294 : scoped_refptr<MsfStreamImpl<T>> root_directory_stream(
295 : new ReadOnlyMsfStream<T>(
296 : directory_pages.data(),
297 E : sizeof(directory_pages[0]) * directory_pages.size()));
298 : if (!AppendStream(root_directory_stream.get(), &root_directory_pages,
299 E : &page_count)) {
300 i : LOG(ERROR) << "Failed to write root directory.";
301 i : return false;
302 : }
303 :
304 : // Write the header.
305 : if (!WriteHeader(root_directory_pages,
306 E : sizeof(directory[0]) * directory.size(), page_count)) {
307 i : LOG(ERROR) << "Failed to write MSF header.";
308 i : return false;
309 : }
310 :
311 : // Initialize the free page bit map. The pages corresponding to stream 0 are
312 : // always marked as free, as well as page 3 which we allocated in the
313 : // preamble.
314 E : FreePageBitMap free_page;
315 E : free_page.SetPageCount(page_count);
316 E : free_page.SetFree(3);
317 E : for (size_t i = stream0_start; i < stream0_end; ++i)
318 E : free_page.SetFree(directory[i]);
319 E : free_page.Finalize();
320 :
321 E : if (!WriteFreePageBitMap(free_page, file_.get())) {
322 i : LOG(ERROR) << "Failed to write free page bitmap.";
323 i : return false;
324 : }
325 :
326 : // On success we want the file to be closed right away.
327 E : file_.reset();
328 :
329 E : return true;
330 E : }
331 :
332 : template <MsfFileType T>
333 : bool MsfWriterImpl<T>::AppendStream(MsfStreamImpl<T>* stream,
334 : std::vector<uint32>* pages_written,
335 E : uint32* page_count) {
336 E : DCHECK(stream != NULL);
337 E : DCHECK(pages_written != NULL);
338 E : DCHECK(page_count != NULL);
339 :
340 : #ifndef NDEBUG
341 E : size_t old_pages_written_count = pages_written->size();
342 : #endif
343 :
344 : // Write the stream page by page.
345 E : stream->Seek(0);
346 E : uint8 buffer[kMsfPageSize] = {0};
347 E : size_t bytes_left = stream->length();
348 E : while (bytes_left) {
349 E : size_t bytes_to_read = sizeof(buffer);
350 E : if (bytes_to_read > bytes_left) {
351 E : bytes_to_read = bytes_left;
352 :
353 : // If we're only reading a partial buffer then pad the end of it with
354 : // zeros.
355 E : ::memset(buffer + bytes_to_read, 0, sizeof(buffer) - bytes_to_read);
356 : }
357 :
358 : // Read the buffer from the stream.
359 E : size_t bytes_read = 0;
360 : if (!stream->ReadBytes(buffer, bytes_to_read, &bytes_read) ||
361 E : bytes_read != bytes_to_read) {
362 i : size_t offset = stream->length() - bytes_left;
363 i : LOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset "
364 : << offset << " of MSF stream.";
365 i : return false;
366 : }
367 :
368 E : if (!AppendPage(buffer, pages_written, page_count, file_.get()))
369 i : return false;
370 :
371 E : bytes_left -= bytes_read;
372 E : }
373 E : DCHECK_EQ(0u, bytes_left);
374 :
375 : #ifndef NDEBUG
376 : size_t expected_pages_written =
377 E : (stream->length() + kMsfPageSize - 1) / kMsfPageSize;
378 E : DCHECK_EQ(old_pages_written_count + expected_pages_written,
379 : pages_written->size());
380 : // We can't say anything about |page_count| as AppendPage occasionally snags
381 : // extra pages for the free page map.
382 : #endif
383 :
384 E : return true;
385 E : }
386 :
387 : template <MsfFileType T>
388 : bool MsfWriterImpl<T>::WriteHeader(
389 : const std::vector<uint32>& root_directory_pages,
390 : size_t directory_size,
391 E : uint32 page_count) {
392 E : VLOG(1) << "Writing MSF Header ...";
393 :
394 E : MsfHeader header = {0};
395 :
396 : // Make sure the root directory pointers won't overflow.
397 E : if (root_directory_pages.size() > arraysize(header.root_pages)) {
398 E : LOG(ERROR) << "Too many root directory pages for header ("
399 : << root_directory_pages.size() << " > "
400 : << arraysize(header.root_pages) << ").";
401 E : return false;
402 : }
403 :
404 : // Seek to the beginning of the file so we can stamp in the header.
405 E : if (::fseek(file_.get(), 0, SEEK_SET) != 0) {
406 i : LOG(ERROR) << "Seek failed while writing header.";
407 i : return false;
408 : }
409 :
410 : ::memcpy(header.magic_string, kMsfHeaderMagicString,
411 E : sizeof(kMsfHeaderMagicString));
412 E : header.page_size = kMsfPageSize;
413 E : header.free_page_map = 1;
414 E : header.num_pages = page_count;
415 E : header.directory_size = directory_size;
416 E : header.reserved = 0;
417 : ::memcpy(header.root_pages, root_directory_pages.data(),
418 E : sizeof(root_directory_pages[0]) * root_directory_pages.size());
419 :
420 E : if (::fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
421 i : LOG(ERROR) << "Failed to write header.";
422 i : return false;
423 : }
424 :
425 E : return true;
426 E : }
427 :
428 : } // namespace detail
429 : } // namespace msf
430 :
431 : #endif // SYZYGY_MSF_MSF_WRITER_IMPL_H_
|