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.
5 #include "chrome/utility/media_galleries/pmp_column_reader.h"
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/threading/thread_restrictions.h"
17 COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long
);
18 const int64 kPmpMaxFilesize
= 50*1024*1024; // Arbitrary maximum of 50 MB.
22 PmpColumnReader::PmpColumnReader()
24 field_type_(PMP_TYPE_INVALID
),
27 PmpColumnReader::~PmpColumnReader() {}
29 bool PmpColumnReader::ReadFile(base::PlatformFile file
,
30 const PmpFieldType expected_type
) {
32 base::ThreadRestrictions::AssertIOAllowed();
34 if (file
== base::kInvalidPlatformFileValue
)
37 base::PlatformFileInfo info
;
38 if (!base::GetPlatformFileInfo(file
, &info
))
42 if (length_
< kPmpHeaderSize
|| length_
> kPmpMaxFilesize
)
45 data_
.reset(new uint8
[length_
]);
47 char* data_begin
= reinterpret_cast<char*>(data_
.get());
49 DCHECK(length_
< kint32max
); // ReadFile expects an int.
51 bool success
= base::ReadPlatformFile(file
, 0, data_begin
, length_
) &&
52 ParseData(expected_type
);
54 // If any of the reading or parsing fails, prevent Read* calls.
61 bool PmpColumnReader::ReadString(const uint32 row
, std::string
* result
) const {
62 DCHECK(data_
.get() != NULL
);
64 if (field_type_
!= PMP_TYPE_STRING
|| row
>= rows_read_
)
67 DCHECK_LT(row
, strings_
.size());
68 *result
= strings_
[row
];
72 bool PmpColumnReader::ReadUInt32(const uint32 row
, uint32
* result
) const {
73 DCHECK(data_
.get() != NULL
);
75 if (field_type_
!= PMP_TYPE_UINT32
|| row
>= rows_read_
)
78 *result
= reinterpret_cast<uint32
*>(data_
.get() + kPmpHeaderSize
)[row
];
82 bool PmpColumnReader::ReadDouble64(const uint32 row
, double* result
) const {
83 DCHECK(data_
.get() != NULL
);
85 if (field_type_
!= PMP_TYPE_DOUBLE64
|| row
>= rows_read_
)
88 *result
= reinterpret_cast<double*>(data_
.get() + kPmpHeaderSize
)[row
];
92 bool PmpColumnReader::ReadUInt8(const uint32 row
, uint8
* result
) const {
93 DCHECK(data_
.get() != NULL
);
95 if (field_type_
!= PMP_TYPE_UINT8
|| row
>= rows_read_
)
98 *result
= reinterpret_cast<uint8
*>(data_
.get() + kPmpHeaderSize
)[row
];
102 bool PmpColumnReader::ReadUInt64(const uint32 row
, uint64
* result
) const {
103 DCHECK(data_
.get() != NULL
);
105 if (field_type_
!= PMP_TYPE_UINT64
|| row
>= rows_read_
)
108 *result
= reinterpret_cast<uint64
*>(data_
.get() + kPmpHeaderSize
)[row
];
112 uint32
PmpColumnReader::rows_read() const {
113 DCHECK(data_
.get() != NULL
);
117 bool PmpColumnReader::ParseData(const PmpFieldType expected_type
) {
118 DCHECK(data_
.get() != NULL
);
119 DCHECK_GE(length_
, kPmpHeaderSize
);
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) {
129 uint16 field_type_data
=
130 *(reinterpret_cast<uint16
*>(&data_
[kPmpFieldType1Offset
]));
132 // Verify if field type matches second declaration
133 if (field_type_data
!=
134 *(reinterpret_cast<uint16
*>(&data_
[kPmpFieldType2Offset
]))) {
138 field_type_
= static_cast<PmpFieldType
>(field_type_data
);
140 if (field_type_
!= expected_type
)
143 rows_read_
= *(reinterpret_cast<uint32
*>(&data_
[kPmpRowCountOffset
]));
145 // Sanity check against malicious row field.
146 if (rows_read_
> (kPmpMaxFilesize
- kPmpHeaderSize
))
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();
156 case PMP_TYPE_UINT32
:
157 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint32
);
159 case PMP_TYPE_DOUBLE64
:
160 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(double);
163 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint8
);
165 case PMP_TYPE_UINT64
:
166 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint64
);
173 return body_length
== expected_body_length
;
176 int64
PmpColumnReader::IndexStrings() {
177 DCHECK(data_
.get() != NULL
);
178 DCHECK_GE(length_
, kPmpHeaderSize
);
180 strings_
.reserve(rows_read_
);
182 int64 bytes_parsed
= kPmpHeaderSize
;
183 const uint8
* data_cursor
= data_
.get() + kPmpHeaderSize
;
185 while (strings_
.size() < rows_read_
) {
186 const uint8
* string_end
= static_cast<const uint8
*>(
187 memchr(data_cursor
, '\0', length_
- bytes_parsed
));
189 // Fail if cannot find null termination. String runs on past file end.
190 if (string_end
== NULL
)
193 // Length of string. (+1 to include the termination character).
194 ptrdiff_t length_in_bytes
= string_end
- data_cursor
+ 1;
196 strings_
.push_back(reinterpret_cast<const char*>(data_cursor
));
197 data_cursor
+= length_in_bytes
;
198 bytes_parsed
+= length_in_bytes
;
201 return bytes_parsed
- kPmpHeaderSize
;
204 } // namespace picasa