Coverage for /Syzygy/msf/msf_writer_impl.h

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
81.2%1601970.C++source

Line-by-line coverage:

   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_

Coverage information generated Fri Jul 29 11:00:21 2016.