Coverage for /Syzygy/msf/msf_writer_impl.h

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
81.2%1642020.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 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_

Coverage information generated Thu Jan 14 17:40:38 2016.