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 13namespace picasa { 14 15namespace { 16 17COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long); 18const int64 kPmpMaxFilesize = 50*1024*1024; // Arbitrary maximum of 50 MB. 19 20} // namespace 21 22PmpColumnReader::PmpColumnReader() 23 : length_(0), 24 field_type_(PMP_TYPE_INVALID), 25 rows_read_(0) {} 26 27PmpColumnReader::~PmpColumnReader() {} 28 29bool 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 61bool 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 72bool 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 82bool 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 92bool 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 102bool 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 112uint32 PmpColumnReader::rows_read() const { 113 DCHECK(data_.get() != NULL); 114 return rows_read_; 115} 116 117bool 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 176int64 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