root/chrome/utility/media_galleries/pmp_column_reader.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rows_read_
  2. ReadFile
  3. ReadString
  4. ReadUInt32
  5. ReadDouble64
  6. ReadUInt8
  7. ReadUInt64
  8. rows_read
  9. ParseData
  10. IndexStrings

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/utility/media_galleries/pmp_column_reader.h"

#include <cstring>

#include "base/file_util.h"
#include "base/files/file.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"

namespace picasa {

namespace {

COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long);
const int64 kPmpMaxFilesize = 50*1024*1024;  // Arbitrary maximum of 50 MB.

}  // namespace

PmpColumnReader::PmpColumnReader()
    : length_(0),
      field_type_(PMP_TYPE_INVALID),
      rows_read_(0) {}

PmpColumnReader::~PmpColumnReader() {}

bool PmpColumnReader::ReadFile(base::File* file,
                               const PmpFieldType expected_type) {
  DCHECK(!data_.get());
  base::ThreadRestrictions::AssertIOAllowed();

  if (!file->IsValid())
    return false;

  base::File::Info info;
  if (!file->GetInfo(&info))
    return false;
  length_ = info.size;

  if (length_ < kPmpHeaderSize || length_ > kPmpMaxFilesize)
    return false;

  data_.reset(new uint8[length_]);

  char* data_begin = reinterpret_cast<char*>(data_.get());

  DCHECK(length_ < kint32max);  // ReadFile expects an int.

  bool success = file->Read(0, data_begin, length_) &&
                 ParseData(expected_type);

  // If any of the reading or parsing fails, prevent Read* calls.
  if (!success)
    rows_read_ = 0;

  return success;
}

bool PmpColumnReader::ReadString(const uint32 row, std::string* result) const {
  DCHECK(data_.get() != NULL);

  if (field_type_ != PMP_TYPE_STRING || row >= rows_read_)
    return false;

  DCHECK_LT(row, strings_.size());
  *result = strings_[row];
  return true;
}

bool PmpColumnReader::ReadUInt32(const uint32 row, uint32* result) const {
  DCHECK(data_.get() != NULL);

  if (field_type_ != PMP_TYPE_UINT32 || row >= rows_read_)
    return false;

  *result = reinterpret_cast<uint32*>(data_.get() + kPmpHeaderSize)[row];
  return true;
}

bool PmpColumnReader::ReadDouble64(const uint32 row, double* result) const {
  DCHECK(data_.get() != NULL);

  if (field_type_ != PMP_TYPE_DOUBLE64 || row >= rows_read_)
    return false;

  *result = reinterpret_cast<double*>(data_.get() + kPmpHeaderSize)[row];
  return true;
}

bool PmpColumnReader::ReadUInt8(const uint32 row, uint8* result) const {
  DCHECK(data_.get() != NULL);

  if (field_type_ != PMP_TYPE_UINT8 || row >= rows_read_)
    return false;

  *result = reinterpret_cast<uint8*>(data_.get() + kPmpHeaderSize)[row];
  return true;
}

bool PmpColumnReader::ReadUInt64(const uint32 row, uint64* result) const {
  DCHECK(data_.get() != NULL);

  if (field_type_ != PMP_TYPE_UINT64 || row >= rows_read_)
    return false;

  *result = reinterpret_cast<uint64*>(data_.get() + kPmpHeaderSize)[row];
  return true;
}

uint32 PmpColumnReader::rows_read() const {
  DCHECK(data_.get() != NULL);
  return rows_read_;
}

bool PmpColumnReader::ParseData(const PmpFieldType expected_type) {
  DCHECK(data_.get() != NULL);
  DCHECK_GE(length_, kPmpHeaderSize);

  // Check all magic bytes.
  if (memcmp(&kPmpMagic1, &data_[kPmpMagic1Offset], sizeof(kPmpMagic1)) != 0 ||
      memcmp(&kPmpMagic2, &data_[kPmpMagic2Offset], sizeof(kPmpMagic2)) != 0 ||
      memcmp(&kPmpMagic3, &data_[kPmpMagic3Offset], sizeof(kPmpMagic3)) != 0 ||
      memcmp(&kPmpMagic4, &data_[kPmpMagic4Offset], sizeof(kPmpMagic4)) != 0) {
    return false;
  }

  uint16 field_type_data =
      *(reinterpret_cast<uint16*>(&data_[kPmpFieldType1Offset]));

  // Verify if field type matches second declaration
  if (field_type_data !=
      *(reinterpret_cast<uint16*>(&data_[kPmpFieldType2Offset]))) {
    return false;
  }

  field_type_ = static_cast<PmpFieldType>(field_type_data);

  if (field_type_ != expected_type)
    return false;

  rows_read_ = *(reinterpret_cast<uint32*>(&data_[kPmpRowCountOffset]));

  // Sanity check against malicious row field.
  if (rows_read_ > (kPmpMaxFilesize - kPmpHeaderSize))
    return false;

  DCHECK_GE(length_, kPmpHeaderSize);
  int64 body_length = length_ - kPmpHeaderSize;
  int64 expected_body_length = 0;
  switch (field_type_) {
    case PMP_TYPE_STRING:
      expected_body_length = IndexStrings();
      break;
    case PMP_TYPE_UINT32:
      expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint32);
      break;
    case PMP_TYPE_DOUBLE64:
      expected_body_length = static_cast<int64>(rows_read_) * sizeof(double);
      break;
    case PMP_TYPE_UINT8:
      expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint8);
      break;
    case PMP_TYPE_UINT64:
      expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint64);
      break;
    default:
      return false;
      break;
  }

  return body_length == expected_body_length;
}

int64 PmpColumnReader::IndexStrings() {
  DCHECK(data_.get() != NULL);
  DCHECK_GE(length_, kPmpHeaderSize);

  strings_.reserve(rows_read_);

  int64 bytes_parsed = kPmpHeaderSize;
  const uint8* data_cursor = data_.get() + kPmpHeaderSize;

  while (strings_.size() < rows_read_) {
    const uint8* string_end = static_cast<const uint8*>(
        memchr(data_cursor, '\0', length_ - bytes_parsed));

    // Fail if cannot find null termination. String runs on past file end.
    if (string_end == NULL)
      return -1;

    // Length of string. (+1 to include the termination character).
    ptrdiff_t length_in_bytes = string_end - data_cursor + 1;

    strings_.push_back(reinterpret_cast<const char*>(data_cursor));
    data_cursor += length_in_bytes;
    bytes_parsed += length_in_bytes;
  }

  return bytes_parsed - kPmpHeaderSize;
}

}  // namespace picasa

/* [<][>][^][v][top][bottom][index][help] */