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/files/file.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/threading/thread_restrictions.h"
18 static_assert(sizeof(double) == 8, "double must be 8 bytes long");
19 const int64 kPmpMaxFilesize
= 50*1024*1024; // Arbitrary maximum of 50 MB.
23 PmpColumnReader::PmpColumnReader()
25 field_type_(PMP_TYPE_INVALID
),
28 PmpColumnReader::~PmpColumnReader() {}
30 bool PmpColumnReader::ReadFile(base::File
* file
,
31 const PmpFieldType expected_type
) {
33 base::ThreadRestrictions::AssertIOAllowed();
38 base::File::Info info
;
39 if (!file
->GetInfo(&info
))
43 if (length_
< kPmpHeaderSize
|| length_
> kPmpMaxFilesize
)
46 data_
.reset(new uint8
[length_
]);
48 char* data_begin
= reinterpret_cast<char*>(data_
.get());
50 DCHECK(length_
< kint32max
); // ReadFile expects an int.
52 bool success
= file
->Read(0, data_begin
, length_
) &&
53 ParseData(expected_type
);
55 // If any of the reading or parsing fails, prevent Read* calls.
62 bool PmpColumnReader::ReadString(const uint32 row
, std::string
* result
) const {
63 DCHECK(data_
.get() != NULL
);
65 if (field_type_
!= PMP_TYPE_STRING
|| row
>= rows_read_
)
68 DCHECK_LT(row
, strings_
.size());
69 *result
= strings_
[row
];
73 bool PmpColumnReader::ReadUInt32(const uint32 row
, uint32
* result
) const {
74 DCHECK(data_
.get() != NULL
);
76 if (field_type_
!= PMP_TYPE_UINT32
|| row
>= rows_read_
)
79 *result
= reinterpret_cast<uint32
*>(data_
.get() + kPmpHeaderSize
)[row
];
83 bool PmpColumnReader::ReadDouble64(const uint32 row
, double* result
) const {
84 DCHECK(data_
.get() != NULL
);
86 if (field_type_
!= PMP_TYPE_DOUBLE64
|| row
>= rows_read_
)
89 *result
= reinterpret_cast<double*>(data_
.get() + kPmpHeaderSize
)[row
];
93 bool PmpColumnReader::ReadUInt8(const uint32 row
, uint8
* result
) const {
94 DCHECK(data_
.get() != NULL
);
96 if (field_type_
!= PMP_TYPE_UINT8
|| row
>= rows_read_
)
99 *result
= reinterpret_cast<uint8
*>(data_
.get() + kPmpHeaderSize
)[row
];
103 bool PmpColumnReader::ReadUInt64(const uint32 row
, uint64
* result
) const {
104 DCHECK(data_
.get() != NULL
);
106 if (field_type_
!= PMP_TYPE_UINT64
|| row
>= rows_read_
)
109 *result
= reinterpret_cast<uint64
*>(data_
.get() + kPmpHeaderSize
)[row
];
113 uint32
PmpColumnReader::rows_read() const {
114 DCHECK(data_
.get() != NULL
);
118 bool PmpColumnReader::ParseData(const PmpFieldType expected_type
) {
119 DCHECK(data_
.get() != NULL
);
120 DCHECK_GE(length_
, kPmpHeaderSize
);
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) {
130 uint16 field_type_data
=
131 *(reinterpret_cast<uint16
*>(&data_
[kPmpFieldType1Offset
]));
133 // Verify if field type matches second declaration
134 if (field_type_data
!=
135 *(reinterpret_cast<uint16
*>(&data_
[kPmpFieldType2Offset
]))) {
139 field_type_
= static_cast<PmpFieldType
>(field_type_data
);
141 if (field_type_
!= expected_type
)
144 rows_read_
= *(reinterpret_cast<uint32
*>(&data_
[kPmpRowCountOffset
]));
146 // Sanity check against malicious row field.
147 if (rows_read_
> (kPmpMaxFilesize
- kPmpHeaderSize
))
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();
157 case PMP_TYPE_UINT32
:
158 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint32
);
160 case PMP_TYPE_DOUBLE64
:
161 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(double);
164 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint8
);
166 case PMP_TYPE_UINT64
:
167 expected_body_length
= static_cast<int64
>(rows_read_
) * sizeof(uint64
);
174 return body_length
== expected_body_length
;
177 int64
PmpColumnReader::IndexStrings() {
178 DCHECK(data_
.get() != NULL
);
179 DCHECK_GE(length_
, kPmpHeaderSize
);
181 strings_
.reserve(rows_read_
);
183 int64 bytes_parsed
= kPmpHeaderSize
;
184 const uint8
* data_cursor
= data_
.get() + kPmpHeaderSize
;
186 while (strings_
.size() < rows_read_
) {
187 const uint8
* string_end
= static_cast<const uint8
*>(
188 memchr(data_cursor
, '\0', length_
- bytes_parsed
));
190 // Fail if cannot find null termination. String runs on past file end.
191 if (string_end
== NULL
)
194 // Length of string. (+1 to include the termination character).
195 ptrdiff_t length_in_bytes
= string_end
- data_cursor
+ 1;
197 strings_
.push_back(reinterpret_cast<const char*>(data_cursor
));
198 data_cursor
+= length_in_bytes
;
199 bytes_parsed
+= length_in_bytes
;
202 return bytes_parsed
- kPmpHeaderSize
;
205 } // namespace picasa