Home | History | Annotate | Download | only in media_galleries
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/utility/media_galleries/pmp_column_reader.h"
      6 
      7 #include <cstring>
      8 
      9 #include "base/file_util.h"
     10 #include "base/logging.h"
     11 #include "base/threading/thread_restrictions.h"
     12 
     13 namespace picasa {
     14 
     15 namespace {
     16 
     17 COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long);
     18 const int64 kPmpMaxFilesize = 50*1024*1024;  // Arbitrary maximum of 50 MB.
     19 
     20 }  // namespace
     21 
     22 PmpColumnReader::PmpColumnReader()
     23     : length_(0),
     24       field_type_(PMP_TYPE_INVALID),
     25       rows_read_(0) {}
     26 
     27 PmpColumnReader::~PmpColumnReader() {}
     28 
     29 bool PmpColumnReader::ReadFile(base::PlatformFile file,
     30                                const PmpFieldType expected_type) {
     31   DCHECK(!data_.get());
     32   base::ThreadRestrictions::AssertIOAllowed();
     33 
     34   if (file == base::kInvalidPlatformFileValue)
     35     return false;
     36 
     37   base::PlatformFileInfo info;
     38   if (!base::GetPlatformFileInfo(file, &info))
     39     return false;
     40   length_ = info.size;
     41 
     42   if (length_ < kPmpHeaderSize || length_ > kPmpMaxFilesize)
     43     return false;
     44 
     45   data_.reset(new uint8[length_]);
     46 
     47   char* data_begin = reinterpret_cast<char*>(data_.get());
     48 
     49   DCHECK(length_ < kint32max);  // ReadFile expects an int.
     50 
     51   bool success = base::ReadPlatformFile(file, 0, data_begin, length_) &&
     52                  ParseData(expected_type);
     53 
     54   // If any of the reading or parsing fails, prevent Read* calls.
     55   if (!success)
     56     rows_read_ = 0;
     57 
     58   return success;
     59 }
     60 
     61 bool PmpColumnReader::ReadString(const uint32 row, std::string* result) const {
     62   DCHECK(data_.get() != NULL);
     63 
     64   if (field_type_ != PMP_TYPE_STRING || row >= rows_read_)
     65     return false;
     66 
     67   DCHECK_LT(row, strings_.size());
     68   *result = strings_[row];
     69   return true;
     70 }
     71 
     72 bool PmpColumnReader::ReadUInt32(const uint32 row, uint32* result) const {
     73   DCHECK(data_.get() != NULL);
     74 
     75   if (field_type_ != PMP_TYPE_UINT32 || row >= rows_read_)
     76     return false;
     77 
     78   *result = reinterpret_cast<uint32*>(data_.get() + kPmpHeaderSize)[row];
     79   return true;
     80 }
     81 
     82 bool PmpColumnReader::ReadDouble64(const uint32 row, double* result) const {
     83   DCHECK(data_.get() != NULL);
     84 
     85   if (field_type_ != PMP_TYPE_DOUBLE64 || row >= rows_read_)
     86     return false;
     87 
     88   *result = reinterpret_cast<double*>(data_.get() + kPmpHeaderSize)[row];
     89   return true;
     90 }
     91 
     92 bool PmpColumnReader::ReadUInt8(const uint32 row, uint8* result) const {
     93   DCHECK(data_.get() != NULL);
     94 
     95   if (field_type_ != PMP_TYPE_UINT8 || row >= rows_read_)
     96     return false;
     97 
     98   *result = reinterpret_cast<uint8*>(data_.get() + kPmpHeaderSize)[row];
     99   return true;
    100 }
    101 
    102 bool PmpColumnReader::ReadUInt64(const uint32 row, uint64* result) const {
    103   DCHECK(data_.get() != NULL);
    104 
    105   if (field_type_ != PMP_TYPE_UINT64 || row >= rows_read_)
    106     return false;
    107 
    108   *result = reinterpret_cast<uint64*>(data_.get() + kPmpHeaderSize)[row];
    109   return true;
    110 }
    111 
    112 uint32 PmpColumnReader::rows_read() const {
    113   DCHECK(data_.get() != NULL);
    114   return rows_read_;
    115 }
    116 
    117 bool PmpColumnReader::ParseData(const PmpFieldType expected_type) {
    118   DCHECK(data_.get() != NULL);
    119   DCHECK_GE(length_, kPmpHeaderSize);
    120 
    121   // Check all magic bytes.
    122   if (memcmp(&kPmpMagic1, &data_[kPmpMagic1Offset], sizeof(kPmpMagic1)) != 0 ||
    123       memcmp(&kPmpMagic2, &data_[kPmpMagic2Offset], sizeof(kPmpMagic2)) != 0 ||
    124       memcmp(&kPmpMagic3, &data_[kPmpMagic3Offset], sizeof(kPmpMagic3)) != 0 ||
    125       memcmp(&kPmpMagic4, &data_[kPmpMagic4Offset], sizeof(kPmpMagic4)) != 0) {
    126     return false;
    127   }
    128 
    129   uint16 field_type_data =
    130       *(reinterpret_cast<uint16*>(&data_[kPmpFieldType1Offset]));
    131 
    132   // Verify if field type matches second declaration
    133   if (field_type_data !=
    134       *(reinterpret_cast<uint16*>(&data_[kPmpFieldType2Offset]))) {
    135     return false;
    136   }
    137 
    138   field_type_ = static_cast<PmpFieldType>(field_type_data);
    139 
    140   if (field_type_ != expected_type)
    141     return false;
    142 
    143   rows_read_ = *(reinterpret_cast<uint32*>(&data_[kPmpRowCountOffset]));
    144 
    145   // Sanity check against malicious row field.
    146   if (rows_read_ > (kPmpMaxFilesize - kPmpHeaderSize))
    147     return false;
    148 
    149   DCHECK_GE(length_, kPmpHeaderSize);
    150   int64 body_length = length_ - kPmpHeaderSize;
    151   int64 expected_body_length = 0;
    152   switch (field_type_) {
    153     case PMP_TYPE_STRING:
    154       expected_body_length = IndexStrings();
    155       break;
    156     case PMP_TYPE_UINT32:
    157       expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint32);
    158       break;
    159     case PMP_TYPE_DOUBLE64:
    160       expected_body_length = static_cast<int64>(rows_read_) * sizeof(double);
    161       break;
    162     case PMP_TYPE_UINT8:
    163       expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint8);
    164       break;
    165     case PMP_TYPE_UINT64:
    166       expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint64);
    167       break;
    168     default:
    169       return false;
    170       break;
    171   }
    172 
    173   return body_length == expected_body_length;
    174 }
    175 
    176 int64 PmpColumnReader::IndexStrings() {
    177   DCHECK(data_.get() != NULL);
    178   DCHECK_GE(length_, kPmpHeaderSize);
    179 
    180   strings_.reserve(rows_read_);
    181 
    182   int64 bytes_parsed = kPmpHeaderSize;
    183   const uint8* data_cursor = data_.get() + kPmpHeaderSize;
    184 
    185   while (strings_.size() < rows_read_) {
    186     const uint8* string_end = static_cast<const uint8*>(
    187         memchr(data_cursor, '\0', length_ - bytes_parsed));
    188 
    189     // Fail if cannot find null termination. String runs on past file end.
    190     if (string_end == NULL)
    191       return -1;
    192 
    193     // Length of string. (+1 to include the termination character).
    194     ptrdiff_t length_in_bytes = string_end - data_cursor + 1;
    195 
    196     strings_.push_back(reinterpret_cast<const char*>(data_cursor));
    197     data_cursor += length_in_bytes;
    198     bytes_parsed += length_in_bytes;
    199   }
    200 
    201   return bytes_parsed - kPmpHeaderSize;
    202 }
    203 
    204 }  // namespace picasa
    205