forked from Pissandshittium/pissandshittium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbtree.cc
253 lines (214 loc) · 9.18 KB
/
btree.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sql/recover_module/btree.h"
#include <algorithm>
#include <limits>
#include <ostream>
#include <type_traits>
#include "base/check_op.h"
#include "sql/recover_module/integers.h"
#include "sql/recover_module/pager.h"
#include "third_party/sqlite/sqlite3.h"
namespace sql {
namespace recover {
namespace {
// The SQLite database format is documented at the following URLs.
// https://www.sqlite.org/fileformat.html
// https://www.sqlite.org/fileformat2.html
constexpr uint8_t kInnerTablePageType = 0x05;
constexpr uint8_t kLeafTablePageType = 0x0D;
// Offset from the page header to the page type byte.
constexpr int kPageTypePageOffset = 0;
// Offset from the page header to the 2-byte cell count.
constexpr int kCellCountPageOffset = 3;
// Offset from an inner page header to the 4-byte last child page ID.
constexpr int kLastChildIdInnerPageOffset = 8;
// Offset from an inner page header to the cell pointer array.
constexpr int kFirstCellOfsetInnerPageOffset = 12;
// Offset from a leaf page header to the cell pointer array.
constexpr int kFirstCellOfsetLeafPageOffset = 8;
} // namespace
#if !DCHECK_IS_ON()
// In DCHECKed builds, the decoder contains a sequence checker, which has a
// non-trivial destructor.
static_assert(std::is_trivially_destructible<InnerPageDecoder>::value,
"Move the destructor to the .cc file if it's non-trival");
#endif // !DCHECK_IS_ON()
InnerPageDecoder::InnerPageDecoder(DatabasePageReader* db_reader) noexcept
: page_id_(db_reader->page_id()),
db_reader_(db_reader),
cell_count_(ComputeCellCount(db_reader)),
next_read_index_(0) {
DCHECK(IsOnValidPage(db_reader));
DCHECK(DatabasePageReader::IsValidPageId(page_id_));
}
int InnerPageDecoder::TryAdvance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(CanAdvance());
const int sqlite_status = db_reader_->ReadPage(page_id_);
if (sqlite_status != SQLITE_OK) {
// TODO(pwnall): UMA the error code.
next_read_index_ = cell_count_ + 1; // End the reading process.
return DatabasePageReader::kInvalidPageId;
}
const uint8_t* const page_data = db_reader_->page_data();
const int read_index = next_read_index_;
next_read_index_ += 1;
if (read_index == cell_count_)
return LoadBigEndianInt32(page_data + kLastChildIdInnerPageOffset);
const int cell_pointer_offset =
kFirstCellOfsetInnerPageOffset + (read_index << 1);
DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size())
<< "ComputeCellCount() used an incorrect upper bound";
const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset);
static_assert(std::numeric_limits<uint16_t>::max() + 4 <
std::numeric_limits<int>::max(),
"The addition below may overflow");
if (cell_pointer + 4 >= db_reader_->page_size()) {
// Each cell needs 1 byte for the rowid varint, in addition to the 4 bytes
// for the child page number that will be read below. Skip cells that
// obviously go over the page end.
return DatabasePageReader::kInvalidPageId;
}
if (cell_pointer < kFirstCellOfsetInnerPageOffset) {
// The pointer points into the cell's header.
return DatabasePageReader::kInvalidPageId;
}
return LoadBigEndianInt32(page_data + cell_pointer);
}
// static
bool InnerPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) {
static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize,
"The check below may perform an out-of-bounds memory access");
return db_reader->page_data()[kPageTypePageOffset] == kInnerTablePageType;
}
// static
int InnerPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) {
// The B-tree page header stores the cell count.
int header_count =
LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset);
static_assert(
kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize,
"The read above may be out of bounds");
// However, the data may be corrupted. So, use an upper bound based on the
// fact that the cell pointer array should never extend past the end of the
// page.
//
// The page size is always even, because it is either a power of two, for
// most pages, or a power of two minus 100, for the first database page. The
// cell pointer array starts at offset 12. So, each cell pointer must be
// separated from the page buffer's end by an even number of bytes.
DCHECK((db_reader->page_size() - kFirstCellOfsetInnerPageOffset) % 2 == 0);
int upper_bound =
(db_reader->page_size() - kFirstCellOfsetInnerPageOffset) >> 1;
static_assert(
kFirstCellOfsetInnerPageOffset <= DatabasePageReader::kMinUsablePageSize,
"The |upper_bound| computation above may overflow");
return std::min(header_count, upper_bound);
}
#if !DCHECK_IS_ON()
// In DCHECKed builds, the decoder contains a sequence checker, which has a
// non-trivial destructor.
static_assert(std::is_trivially_destructible<LeafPageDecoder>::value,
"Move the destructor to the .cc file if it's non-trival");
#endif // !DCHECK_IS_ON()
LeafPageDecoder::LeafPageDecoder(DatabasePageReader* db_reader) noexcept
: page_id_(db_reader->page_id()),
db_reader_(db_reader),
cell_count_(ComputeCellCount(db_reader)),
next_read_index_(0),
last_record_size_(0) {
DCHECK(IsOnValidPage(db_reader));
DCHECK(DatabasePageReader::IsValidPageId(page_id_));
}
bool LeafPageDecoder::TryAdvance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(CanAdvance());
#if DCHECK_IS_ON()
// DCHECKs use last_record_size == 0 to check for incorrect access to the
// decoder's state.
last_record_size_ = 0;
#endif // DCHECK_IS_ON()
const int sqlite_status = db_reader_->ReadPage(page_id_);
if (sqlite_status != SQLITE_OK) {
// TODO(pwnall): UMA the error code.
next_read_index_ = cell_count_; // End the reading process.
return false;
}
const uint8_t* page_data = db_reader_->page_data();
const int read_index = next_read_index_;
next_read_index_ += 1;
const int cell_pointer_offset =
kFirstCellOfsetLeafPageOffset + (read_index << 1);
DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size())
<< "ComputeCellCount() used an incorrect upper bound";
const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset);
static_assert(std::numeric_limits<uint16_t>::max() + 3 <
std::numeric_limits<int>::max(),
"The addition below may overflow");
if (cell_pointer + 3 >= db_reader_->page_size()) {
// Each cell needs at least 1 byte for page type varint, 1 byte for the
// rowid varint, and 1 byte for the record header size varint. Skip cells
// that obviously go over the page end.
return false;
}
if (cell_pointer < kFirstCellOfsetLeafPageOffset) {
// The pointer points into the cell's header.
return false;
}
const uint8_t* const cell_start = page_data + cell_pointer;
const uint8_t* const page_end = page_data + db_reader_->page_size();
DCHECK_LT(cell_start, page_end) << "Failed to skip over empty cells";
const uint8_t* rowid_start;
std::tie(last_record_size_, rowid_start) = ParseVarint(cell_start, page_end);
if (rowid_start == page_end) {
// The value size varint extended to the end of the page, so the rowid
// varint starts past the page end.
return false;
}
if (last_record_size_ <= 0) {
// Each payload needs at least one varint. Skip empty payloads.
#if DCHECK_IS_ON()
// DCHECKs use last_record_size == 0 to check for incorrect access to the
// decoder's state.
last_record_size_ = 0;
#endif // DCHECK_IS_ON()
return false;
}
const uint8_t* record_start;
std::tie(last_record_rowid_, record_start) =
ParseVarint(rowid_start, page_end);
if (record_start == page_end) {
// The rowid varint extended to the end of the page, so the record starts
// past the page end. Records need at least 1 byte for their header size
// varint, so this suggests corruption.
last_record_size_ = 0;
return false;
}
last_record_offset_ = record_start - page_data;
return true;
}
// static
bool LeafPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) {
static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize,
"The check below may perform an out-of-bounds memory access");
return db_reader->page_data()[kPageTypePageOffset] == kLeafTablePageType;
}
// static
int LeafPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) {
// See InnerPageDecoder::ComputeCellCount() for the reasoning behind the code.
int header_count =
LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset);
static_assert(
kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize,
"The read above may be out of bounds");
int upper_bound =
(db_reader->page_size() - kFirstCellOfsetLeafPageOffset) >> 1;
static_assert(
kFirstCellOfsetLeafPageOffset <= DatabasePageReader::kMinUsablePageSize,
"The |upper_bound| computation above may overflow");
return std::min(header_count, upper_bound);
}
} // namespace recover
} // namespace sql