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