dfindexeddb 20241105__py3-none-any.whl → 20260205__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dfindexeddb/indexeddb/chromium/blink.py +116 -74
- dfindexeddb/indexeddb/chromium/definitions.py +240 -125
- dfindexeddb/indexeddb/chromium/record.py +651 -346
- dfindexeddb/indexeddb/chromium/sqlite.py +362 -0
- dfindexeddb/indexeddb/chromium/v8.py +100 -78
- dfindexeddb/indexeddb/cli.py +282 -121
- dfindexeddb/indexeddb/firefox/definitions.py +7 -4
- dfindexeddb/indexeddb/firefox/gecko.py +98 -74
- dfindexeddb/indexeddb/firefox/record.py +78 -26
- dfindexeddb/indexeddb/safari/definitions.py +5 -3
- dfindexeddb/indexeddb/safari/record.py +86 -53
- dfindexeddb/indexeddb/safari/webkit.py +85 -71
- dfindexeddb/indexeddb/types.py +4 -1
- dfindexeddb/leveldb/cli.py +146 -138
- dfindexeddb/leveldb/definitions.py +6 -2
- dfindexeddb/leveldb/descriptor.py +70 -56
- dfindexeddb/leveldb/ldb.py +39 -33
- dfindexeddb/leveldb/log.py +41 -30
- dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
- dfindexeddb/leveldb/plugins/interface.py +5 -6
- dfindexeddb/leveldb/plugins/manager.py +10 -9
- dfindexeddb/leveldb/record.py +71 -62
- dfindexeddb/leveldb/utils.py +105 -13
- dfindexeddb/utils.py +36 -31
- dfindexeddb/version.py +2 -2
- dfindexeddb-20260205.dist-info/METADATA +171 -0
- dfindexeddb-20260205.dist-info/RECORD +41 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/WHEEL +1 -1
- dfindexeddb-20241105.dist-info/AUTHORS +0 -12
- dfindexeddb-20241105.dist-info/METADATA +0 -424
- dfindexeddb-20241105.dist-info/RECORD +0 -41
- {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/entry_points.txt +0 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info/licenses}/LICENSE +0 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/top_level.txt +0 -0
|
@@ -15,28 +15,32 @@
|
|
|
15
15
|
"""Parses Chromium IndexedDb structures."""
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
-
from dataclasses import dataclass, field
|
|
19
|
-
from datetime import datetime
|
|
20
18
|
import io
|
|
21
19
|
import pathlib
|
|
22
20
|
import sys
|
|
23
21
|
import traceback
|
|
24
|
-
from
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from typing import (
|
|
25
|
+
Any,
|
|
26
|
+
BinaryIO,
|
|
27
|
+
ClassVar,
|
|
28
|
+
Generator,
|
|
29
|
+
Optional,
|
|
30
|
+
Tuple,
|
|
31
|
+
Type,
|
|
32
|
+
TypeVar,
|
|
33
|
+
Union,
|
|
34
|
+
)
|
|
28
35
|
|
|
29
36
|
from dfindexeddb import errors
|
|
30
|
-
from dfindexeddb.indexeddb.chromium import blink
|
|
31
|
-
from dfindexeddb.
|
|
32
|
-
from dfindexeddb.leveldb import record
|
|
33
|
-
from dfindexeddb.leveldb import utils
|
|
34
|
-
|
|
37
|
+
from dfindexeddb.indexeddb.chromium import blink, definitions
|
|
38
|
+
from dfindexeddb.leveldb import record, utils
|
|
35
39
|
|
|
36
|
-
T = TypeVar(
|
|
40
|
+
T = TypeVar("T", bound="BaseIndexedDBKey")
|
|
37
41
|
|
|
38
42
|
|
|
39
|
-
@dataclass
|
|
43
|
+
@dataclass(frozen=True)
|
|
40
44
|
class KeyPrefix(utils.FromDecoderMixin):
|
|
41
45
|
"""The IndexedDB key prefix.
|
|
42
46
|
|
|
@@ -46,6 +50,7 @@ class KeyPrefix(utils.FromDecoderMixin):
|
|
|
46
50
|
object_store_id: the object store ID.
|
|
47
51
|
index_id: the index ID.
|
|
48
52
|
"""
|
|
53
|
+
|
|
49
54
|
offset: int = field(compare=False)
|
|
50
55
|
database_id: int
|
|
51
56
|
object_store_id: int
|
|
@@ -53,9 +58,7 @@ class KeyPrefix(utils.FromDecoderMixin):
|
|
|
53
58
|
|
|
54
59
|
@classmethod
|
|
55
60
|
def FromDecoder(
|
|
56
|
-
cls,
|
|
57
|
-
decoder: utils.LevelDBDecoder,
|
|
58
|
-
base_offset: int = 0
|
|
61
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
59
62
|
) -> KeyPrefix:
|
|
60
63
|
"""Decodes a KeyPrefix from the current position of a LevelDBDecoder.
|
|
61
64
|
|
|
@@ -72,28 +75,27 @@ class KeyPrefix(utils.FromDecoderMixin):
|
|
|
72
75
|
"""
|
|
73
76
|
offset, raw_prefix = decoder.ReadBytes(1)
|
|
74
77
|
|
|
75
|
-
database_id_length = (raw_prefix[0] & 0xE0 >> 5) + 1
|
|
76
|
-
object_store_id_length = (raw_prefix[0] & 0x1C >> 2) + 1
|
|
78
|
+
database_id_length = ((raw_prefix[0] & 0xE0) >> 5) + 1
|
|
79
|
+
object_store_id_length = ((raw_prefix[0] & 0x1C) >> 2) + 1
|
|
77
80
|
index_id_length = (raw_prefix[0] & 0x03) + 1
|
|
78
|
-
|
|
79
81
|
if database_id_length < 1 or database_id_length > 8:
|
|
80
|
-
raise errors.ParserError(
|
|
82
|
+
raise errors.ParserError("Invalid database ID length")
|
|
81
83
|
|
|
82
84
|
if object_store_id_length < 1 or object_store_id_length > 8:
|
|
83
|
-
raise errors.ParserError(
|
|
85
|
+
raise errors.ParserError("Invalid object store ID length")
|
|
84
86
|
|
|
85
87
|
if index_id_length < 1 or index_id_length > 4:
|
|
86
|
-
raise errors.ParserError(
|
|
87
|
-
|
|
88
|
-
_, database_id = decoder.DecodeInt(database_id_length)
|
|
89
|
-
_, object_store_id = decoder.DecodeInt(object_store_id_length)
|
|
90
|
-
_, index_id = decoder.DecodeInt(index_id_length)
|
|
88
|
+
raise errors.ParserError("Invalid index ID length")
|
|
91
89
|
|
|
90
|
+
_, database_id = decoder.DecodeInt(database_id_length, signed=False)
|
|
91
|
+
_, object_store_id = decoder.DecodeInt(object_store_id_length, signed=False)
|
|
92
|
+
_, index_id = decoder.DecodeInt(index_id_length, signed=False)
|
|
92
93
|
return cls(
|
|
93
94
|
offset=base_offset + offset,
|
|
94
95
|
database_id=database_id,
|
|
95
96
|
object_store_id=object_store_id,
|
|
96
|
-
index_id=index_id
|
|
97
|
+
index_id=index_id,
|
|
98
|
+
)
|
|
97
99
|
|
|
98
100
|
def GetKeyPrefixType(self) -> definitions.KeyPrefixType:
|
|
99
101
|
"""Returns the KeyPrefixType.
|
|
@@ -116,7 +118,8 @@ class KeyPrefix(utils.FromDecoderMixin):
|
|
|
116
118
|
if self.index_id >= 30:
|
|
117
119
|
return definitions.KeyPrefixType.INDEX_DATA
|
|
118
120
|
raise errors.ParserError(
|
|
119
|
-
f
|
|
121
|
+
f"Unknown KeyPrefixType (index_id={self.index_id})"
|
|
122
|
+
)
|
|
120
123
|
|
|
121
124
|
|
|
122
125
|
@dataclass
|
|
@@ -128,9 +131,10 @@ class IDBKey(utils.FromDecoderMixin):
|
|
|
128
131
|
type: the type of the IDBKey.
|
|
129
132
|
value: the value of the IDBKey.
|
|
130
133
|
"""
|
|
134
|
+
|
|
131
135
|
offset: int = field(compare=False)
|
|
132
136
|
type: definitions.IDBKeyType
|
|
133
|
-
value: Union[list, bytes, str, float, datetime, None]
|
|
137
|
+
value: Union[list[Any], bytes, str, float, datetime, None]
|
|
134
138
|
|
|
135
139
|
_MAXIMUM_DEPTH = 2000
|
|
136
140
|
|
|
@@ -138,7 +142,7 @@ class IDBKey(utils.FromDecoderMixin):
|
|
|
138
142
|
def FromDecoder(
|
|
139
143
|
cls,
|
|
140
144
|
decoder: utils.LevelDBDecoder,
|
|
141
|
-
base_offset: int = 0 #pylint: disable=unused-argument
|
|
145
|
+
base_offset: int = 0, # pylint: disable=unused-argument
|
|
142
146
|
) -> IDBKey:
|
|
143
147
|
"""Decodes an IDBKey from the current position of a LevelDBDecoder.
|
|
144
148
|
|
|
@@ -155,10 +159,13 @@ class IDBKey(utils.FromDecoderMixin):
|
|
|
155
159
|
RecursionError: if maximum depth encountered during parsing.
|
|
156
160
|
"""
|
|
157
161
|
|
|
158
|
-
def RecursiveParse(
|
|
159
|
-
int,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
def RecursiveParse(
|
|
163
|
+
depth: int,
|
|
164
|
+
) -> Tuple[
|
|
165
|
+
int,
|
|
166
|
+
definitions.IDBKeyType,
|
|
167
|
+
Union[list[Any], bytes, str, float, datetime, None],
|
|
168
|
+
]:
|
|
162
169
|
"""Recursively parses IDBKeys.
|
|
163
170
|
|
|
164
171
|
Args:
|
|
@@ -174,16 +181,17 @@ class IDBKey(utils.FromDecoderMixin):
|
|
|
174
181
|
RecursionError: if maximum depth encountered during parsing.
|
|
175
182
|
"""
|
|
176
183
|
if depth == cls._MAXIMUM_DEPTH:
|
|
177
|
-
raise RecursionError(
|
|
184
|
+
raise RecursionError("Maximum recursion depth encountered during parse")
|
|
178
185
|
offset, key_type_value = decoder.DecodeInt(1)
|
|
179
186
|
key_type = definitions.IDBKeyType(key_type_value)
|
|
187
|
+
value: Any = None
|
|
180
188
|
|
|
181
189
|
if key_type == definitions.IDBKeyType.NULL:
|
|
182
190
|
value = None
|
|
183
191
|
elif key_type == definitions.IDBKeyType.ARRAY:
|
|
184
192
|
_, length = decoder.DecodeVarint()
|
|
185
193
|
if length < 0:
|
|
186
|
-
raise errors.ParserError(
|
|
194
|
+
raise errors.ParserError("Invalid length encountered")
|
|
187
195
|
value = []
|
|
188
196
|
while length:
|
|
189
197
|
entry = RecursiveParse(depth + 1)
|
|
@@ -195,19 +203,108 @@ class IDBKey(utils.FromDecoderMixin):
|
|
|
195
203
|
_, value = decoder.DecodeStringWithLength()
|
|
196
204
|
elif key_type == definitions.IDBKeyType.DATE:
|
|
197
205
|
_, raw_value = decoder.DecodeDouble()
|
|
198
|
-
value = datetime.utcfromtimestamp(raw_value/1000.0)
|
|
206
|
+
value = datetime.utcfromtimestamp(raw_value / 1000.0)
|
|
199
207
|
elif key_type == definitions.IDBKeyType.NUMBER:
|
|
200
208
|
_, value = decoder.DecodeDouble()
|
|
201
209
|
elif key_type == definitions.IDBKeyType.MIN_KEY:
|
|
202
210
|
value = None
|
|
203
211
|
else:
|
|
204
|
-
raise errors.ParserError(
|
|
212
|
+
raise errors.ParserError("Invalid IndexedDbKeyType")
|
|
205
213
|
return offset, key_type, value
|
|
206
214
|
|
|
207
215
|
offset, key_type, value = RecursiveParse(0)
|
|
208
216
|
return cls(base_offset + offset, key_type, value)
|
|
209
217
|
|
|
210
218
|
|
|
219
|
+
@dataclass(frozen=True)
|
|
220
|
+
class SortableIDBKey(utils.FromDecoderMixin):
|
|
221
|
+
"""A sortable IDBKey.
|
|
222
|
+
|
|
223
|
+
Attributes:
|
|
224
|
+
offset: the offset of the IDBKey.
|
|
225
|
+
type: the type of the IDBKey.
|
|
226
|
+
value: the value of the IDBKey.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
offset: int = field(compare=False)
|
|
230
|
+
type: definitions.IDBKeyType
|
|
231
|
+
value: Union[list[Any], bytes, str, float, datetime, None]
|
|
232
|
+
|
|
233
|
+
_MAXIMUM_DEPTH = 2000
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def FromDecoder(
|
|
237
|
+
cls,
|
|
238
|
+
decoder: utils.LevelDBDecoder,
|
|
239
|
+
base_offset: int = 0,
|
|
240
|
+
) -> SortableIDBKey:
|
|
241
|
+
"""Decodes a sortable IDBKey from the current position of a LevelDBDecoder.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
decoder: the LevelDBDecoder.
|
|
245
|
+
base_offset: the base offset.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
The decoded SortableIDBKey.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
ParserError: on invalid key type or truncated data.
|
|
252
|
+
RecursionError: if maximum depth encountered.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def RecursiveParse(depth: int) -> Tuple[int, definitions.IDBKeyType, Any]:
|
|
256
|
+
"""Recursively parses sortable IDBKeys.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
depth: the current recursion depth.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
A tuple of the offset, the key type and the key value (where the value
|
|
263
|
+
can be bytes, str, float, datetime or a list of these types).
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
ParserError: on invalid IDBKeyType or invalid array length during
|
|
267
|
+
parsing.
|
|
268
|
+
RecursionError: if maximum depth encountered during parsing.
|
|
269
|
+
"""
|
|
270
|
+
if depth == cls._MAXIMUM_DEPTH:
|
|
271
|
+
raise RecursionError("Maximum recursion depth encountered")
|
|
272
|
+
|
|
273
|
+
value: Any = None
|
|
274
|
+
offset, ordered_type = decoder.DecodeUint8()
|
|
275
|
+
if ordered_type == definitions.OrderedIDBKeyType.NUMBER:
|
|
276
|
+
_, value = decoder.DecodeSortableDouble()
|
|
277
|
+
return offset, definitions.IDBKeyType.NUMBER, value
|
|
278
|
+
if ordered_type == definitions.OrderedIDBKeyType.DATE:
|
|
279
|
+
_, raw_date = decoder.DecodeSortableDouble()
|
|
280
|
+
return (
|
|
281
|
+
offset,
|
|
282
|
+
definitions.IDBKeyType.DATE,
|
|
283
|
+
datetime.utcfromtimestamp(raw_date / 1000.0),
|
|
284
|
+
)
|
|
285
|
+
if ordered_type == definitions.OrderedIDBKeyType.STRING:
|
|
286
|
+
_, value = decoder.DecodeSortableString()
|
|
287
|
+
return offset, definitions.IDBKeyType.STRING, value
|
|
288
|
+
if ordered_type == definitions.OrderedIDBKeyType.BINARY:
|
|
289
|
+
_, value = decoder.DecodeSortableBinary()
|
|
290
|
+
return offset, definitions.IDBKeyType.BINARY, value
|
|
291
|
+
if ordered_type == definitions.OrderedIDBKeyType.ARRAY:
|
|
292
|
+
value = []
|
|
293
|
+
while True:
|
|
294
|
+
_, next_byte = decoder.PeekBytes(1)
|
|
295
|
+
if next_byte[0] == definitions.SENTINEL:
|
|
296
|
+
decoder.ReadBytes(1)
|
|
297
|
+
break
|
|
298
|
+
_, _, item = RecursiveParse(depth + 1)
|
|
299
|
+
value.append(item)
|
|
300
|
+
return offset, definitions.IDBKeyType.ARRAY, value
|
|
301
|
+
|
|
302
|
+
raise errors.ParserError(f"Unknown ordered key type {ordered_type}")
|
|
303
|
+
|
|
304
|
+
offset, key_type, value = RecursiveParse(0)
|
|
305
|
+
return cls(base_offset + offset, key_type, value)
|
|
306
|
+
|
|
307
|
+
|
|
211
308
|
@dataclass
|
|
212
309
|
class IDBKeyPath(utils.FromDecoderMixin):
|
|
213
310
|
"""An IDBKeyPath.
|
|
@@ -217,15 +314,14 @@ class IDBKeyPath(utils.FromDecoderMixin):
|
|
|
217
314
|
type: the IDBKeyPath type.
|
|
218
315
|
value: the IDBKeyPath value.
|
|
219
316
|
"""
|
|
317
|
+
|
|
220
318
|
offset: int = field(compare=False)
|
|
221
319
|
type: definitions.IDBKeyPathType
|
|
222
320
|
value: Union[str, list[str], None]
|
|
223
321
|
|
|
224
322
|
@classmethod
|
|
225
323
|
def FromDecoder(
|
|
226
|
-
cls,
|
|
227
|
-
decoder: utils.LevelDBDecoder,
|
|
228
|
-
base_offset: int = 0
|
|
324
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
229
325
|
) -> IDBKeyPath:
|
|
230
326
|
"""Decodes an IDBKeyPath from the current position of a LevelDBDecoder.
|
|
231
327
|
|
|
@@ -240,11 +336,13 @@ class IDBKeyPath(utils.FromDecoderMixin):
|
|
|
240
336
|
ParserError: on insufficient bytes or invalid array length during
|
|
241
337
|
parsing or unsupported key path type.
|
|
242
338
|
"""
|
|
243
|
-
buffer = decoder.stream.getvalue() #
|
|
339
|
+
buffer = decoder.stream.getvalue() # type: ignore[attr-defined]
|
|
244
340
|
if len(buffer) < 3:
|
|
245
|
-
raise errors.ParserError(
|
|
341
|
+
raise errors.ParserError("Insufficient bytes to parse.")
|
|
342
|
+
|
|
343
|
+
value: str | list[str] | None = None
|
|
246
344
|
|
|
247
|
-
if buffer[0:2] != b
|
|
345
|
+
if buffer[0:2] != b"\x00\x00":
|
|
248
346
|
offset, value = decoder.DecodeString()
|
|
249
347
|
return IDBKeyPath(offset, definitions.IDBKeyPathType.STRING, value)
|
|
250
348
|
|
|
@@ -261,12 +359,12 @@ class IDBKeyPath(utils.FromDecoderMixin):
|
|
|
261
359
|
value = []
|
|
262
360
|
offset, count = decoder.DecodeVarint()
|
|
263
361
|
if count < 0:
|
|
264
|
-
raise errors.ParserError(f
|
|
362
|
+
raise errors.ParserError(f"Invalid array length {count}")
|
|
265
363
|
for _ in range(count):
|
|
266
364
|
_, entry = decoder.DecodeStringWithLength()
|
|
267
365
|
value.append(entry)
|
|
268
366
|
else:
|
|
269
|
-
raise errors.ParserError(f
|
|
367
|
+
raise errors.ParserError(f"Unsupported key_path_type {key_path_type}.")
|
|
270
368
|
return IDBKeyPath(base_offset + offset, key_path_type, value)
|
|
271
369
|
|
|
272
370
|
|
|
@@ -279,15 +377,14 @@ class BlobJournalEntry(utils.FromDecoderMixin):
|
|
|
279
377
|
database_id (int): the database ID.
|
|
280
378
|
blob_number (int): the blob number.
|
|
281
379
|
"""
|
|
380
|
+
|
|
282
381
|
offset: int = field(compare=False)
|
|
283
382
|
database_id: int
|
|
284
383
|
blob_number: int
|
|
285
384
|
|
|
286
385
|
@classmethod
|
|
287
386
|
def FromDecoder(
|
|
288
|
-
cls,
|
|
289
|
-
decoder: utils.LevelDBDecoder,
|
|
290
|
-
base_offset: int = 0
|
|
387
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
291
388
|
) -> BlobJournalEntry:
|
|
292
389
|
"""Decodes a BlobJournalEntry from the current position of a LevelDBDecoder.
|
|
293
390
|
|
|
@@ -300,8 +397,11 @@ class BlobJournalEntry(utils.FromDecoderMixin):
|
|
|
300
397
|
"""
|
|
301
398
|
offset, database_id = decoder.DecodeUint64Varint()
|
|
302
399
|
_, blob_number = decoder.DecodeUint64Varint()
|
|
303
|
-
return cls(
|
|
304
|
-
|
|
400
|
+
return cls(
|
|
401
|
+
offset=base_offset + offset,
|
|
402
|
+
database_id=database_id,
|
|
403
|
+
blob_number=blob_number,
|
|
404
|
+
)
|
|
305
405
|
|
|
306
406
|
|
|
307
407
|
@dataclass
|
|
@@ -312,14 +412,13 @@ class BlobJournal(utils.FromDecoderMixin):
|
|
|
312
412
|
offset (int): the offset.
|
|
313
413
|
entries (list[BlobJournalEntry]): the list of blob journal entries.
|
|
314
414
|
"""
|
|
415
|
+
|
|
315
416
|
offset: int = field(compare=False)
|
|
316
417
|
entries: list[BlobJournalEntry]
|
|
317
418
|
|
|
318
419
|
@classmethod
|
|
319
420
|
def FromDecoder(
|
|
320
|
-
cls,
|
|
321
|
-
decoder: utils.LevelDBDecoder,
|
|
322
|
-
base_offset: int = 0
|
|
421
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
323
422
|
) -> BlobJournal:
|
|
324
423
|
"""Decodes a BlobJournal from the current position of a LevelDBDecoder.
|
|
325
424
|
|
|
@@ -360,6 +459,7 @@ class BaseIndexedDBKey:
|
|
|
360
459
|
offset: the offset of the key (after the key_prefix).
|
|
361
460
|
key_prefix: the key prefix.
|
|
362
461
|
"""
|
|
462
|
+
|
|
363
463
|
offset: int
|
|
364
464
|
key_prefix: KeyPrefix
|
|
365
465
|
|
|
@@ -371,16 +471,19 @@ class BaseIndexedDBKey:
|
|
|
371
471
|
Args:
|
|
372
472
|
decoder: the stream decoder
|
|
373
473
|
|
|
474
|
+
Returns:
|
|
475
|
+
The decoded value.
|
|
476
|
+
|
|
374
477
|
Raises:
|
|
375
478
|
NotImplementedError.
|
|
376
479
|
"""
|
|
377
|
-
raise NotImplementedError(f
|
|
480
|
+
raise NotImplementedError(f"{self.__class__.__name__}.decode_value")
|
|
378
481
|
|
|
379
482
|
def ParseValue(self, value_data: bytes) -> Any:
|
|
380
483
|
"""Parses the value from raw bytes.
|
|
381
484
|
|
|
382
485
|
Args:
|
|
383
|
-
value_data: the raw value
|
|
486
|
+
value_data: the raw value data.
|
|
384
487
|
|
|
385
488
|
Returns:
|
|
386
489
|
The parsed value.
|
|
@@ -392,17 +495,19 @@ class BaseIndexedDBKey:
|
|
|
392
495
|
|
|
393
496
|
@classmethod
|
|
394
497
|
def FromDecoder(
|
|
395
|
-
cls: T,
|
|
498
|
+
cls: Type[T],
|
|
396
499
|
decoder: utils.LevelDBDecoder,
|
|
397
500
|
key_prefix: KeyPrefix,
|
|
398
|
-
base_offset: int = 0
|
|
399
|
-
) -> T:
|
|
400
|
-
"""
|
|
501
|
+
base_offset: int = 0,
|
|
502
|
+
) -> T:
|
|
503
|
+
"""Parses the key from the current position of the LevelDBDecoder.
|
|
504
|
+
|
|
505
|
+
To be implemented by subclasses.
|
|
401
506
|
|
|
402
507
|
Args:
|
|
403
508
|
decoder: the stream decoder.
|
|
404
|
-
key_prefix: the
|
|
405
|
-
base_offset: the base offset.
|
|
509
|
+
key_prefix: the key prefix.
|
|
510
|
+
base_offset: the base offset of the key.
|
|
406
511
|
|
|
407
512
|
Returns:
|
|
408
513
|
The decoded key.
|
|
@@ -410,7 +515,7 @@ class BaseIndexedDBKey:
|
|
|
410
515
|
Raises:
|
|
411
516
|
NotImplementedError.
|
|
412
517
|
"""
|
|
413
|
-
raise NotImplementedError(f
|
|
518
|
+
raise NotImplementedError(f"{cls.__class__.__name__}.decode_key")
|
|
414
519
|
|
|
415
520
|
@classmethod
|
|
416
521
|
def FromStream(cls: Type[T], stream: BinaryIO, base_offset: int = 0) -> T:
|
|
@@ -426,7 +531,8 @@ class BaseIndexedDBKey:
|
|
|
426
531
|
decoder = utils.LevelDBDecoder(stream)
|
|
427
532
|
key_prefix = KeyPrefix.FromDecoder(decoder, base_offset=base_offset)
|
|
428
533
|
return cls.FromDecoder(
|
|
429
|
-
decoder=decoder, key_prefix=key_prefix, base_offset=base_offset
|
|
534
|
+
decoder=decoder, key_prefix=key_prefix, base_offset=base_offset
|
|
535
|
+
)
|
|
430
536
|
|
|
431
537
|
@classmethod
|
|
432
538
|
def FromBytes(cls: Type[T], raw_data: bytes, base_offset: int = 0) -> T:
|
|
@@ -456,12 +562,12 @@ class SchemaVersionKey(BaseIndexedDBKey):
|
|
|
456
562
|
cls,
|
|
457
563
|
decoder: utils.LevelDBDecoder,
|
|
458
564
|
key_prefix: KeyPrefix,
|
|
459
|
-
base_offset: int = 0
|
|
565
|
+
base_offset: int = 0,
|
|
460
566
|
) -> SchemaVersionKey:
|
|
461
567
|
"""Decodes the schema version key."""
|
|
462
568
|
offset, key_type = decoder.DecodeUint8()
|
|
463
569
|
if key_type != definitions.GlobalMetadataKeyType.SCHEMA_VERSION:
|
|
464
|
-
raise errors.ParserError(
|
|
570
|
+
raise errors.ParserError("Not a SchemaVersionKey")
|
|
465
571
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
466
572
|
|
|
467
573
|
|
|
@@ -478,12 +584,12 @@ class MaxDatabaseIdKey(BaseIndexedDBKey):
|
|
|
478
584
|
cls,
|
|
479
585
|
decoder: utils.LevelDBDecoder,
|
|
480
586
|
key_prefix: KeyPrefix,
|
|
481
|
-
base_offset: int = 0
|
|
587
|
+
base_offset: int = 0,
|
|
482
588
|
) -> MaxDatabaseIdKey:
|
|
483
589
|
"""Decodes the maximum database key."""
|
|
484
590
|
offset, key_type = decoder.DecodeUint8()
|
|
485
591
|
if key_type != definitions.GlobalMetadataKeyType.MAX_DATABASE_ID:
|
|
486
|
-
raise errors.ParserError(
|
|
592
|
+
raise errors.ParserError("Not a MaxDatabaseIdKey")
|
|
487
593
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
488
594
|
|
|
489
595
|
|
|
@@ -500,12 +606,12 @@ class DataVersionKey(BaseIndexedDBKey):
|
|
|
500
606
|
cls,
|
|
501
607
|
decoder: utils.LevelDBDecoder,
|
|
502
608
|
key_prefix: KeyPrefix,
|
|
503
|
-
base_offset: int = 0
|
|
609
|
+
base_offset: int = 0,
|
|
504
610
|
) -> DataVersionKey:
|
|
505
611
|
"""Decodes the data version key."""
|
|
506
612
|
offset, key_type = decoder.DecodeUint8()
|
|
507
613
|
if key_type != definitions.GlobalMetadataKeyType.DATA_VERSION:
|
|
508
|
-
raise errors.ParserError(
|
|
614
|
+
raise errors.ParserError("Not a DataVersionKey")
|
|
509
615
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
510
616
|
|
|
511
617
|
|
|
@@ -522,12 +628,12 @@ class RecoveryBlobJournalKey(BaseIndexedDBKey):
|
|
|
522
628
|
cls,
|
|
523
629
|
decoder: utils.LevelDBDecoder,
|
|
524
630
|
key_prefix: KeyPrefix,
|
|
525
|
-
base_offset: int = 0
|
|
631
|
+
base_offset: int = 0,
|
|
526
632
|
) -> RecoveryBlobJournalKey:
|
|
527
633
|
"""Decodes the recovery blob journal key."""
|
|
528
634
|
offset, key_type = decoder.DecodeUint8()
|
|
529
635
|
if key_type != definitions.GlobalMetadataKeyType.RECOVERY_BLOB_JOURNAL:
|
|
530
|
-
raise errors.ParserError(
|
|
636
|
+
raise errors.ParserError("Not a RecoveryBlobJournalKey")
|
|
531
637
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
532
638
|
|
|
533
639
|
|
|
@@ -544,12 +650,12 @@ class ActiveBlobJournalKey(BaseIndexedDBKey):
|
|
|
544
650
|
cls,
|
|
545
651
|
decoder: utils.LevelDBDecoder,
|
|
546
652
|
key_prefix: KeyPrefix,
|
|
547
|
-
base_offset: int = 0
|
|
653
|
+
base_offset: int = 0,
|
|
548
654
|
) -> ActiveBlobJournalKey:
|
|
549
655
|
"""Decodes the active blob journal value."""
|
|
550
656
|
offset, key_type = decoder.DecodeUint8()
|
|
551
657
|
if key_type != definitions.GlobalMetadataKeyType.ACTIVE_BLOB_JOURNAL:
|
|
552
|
-
raise errors.ParserError(
|
|
658
|
+
raise errors.ParserError("Not a ActiveBlobJournalKey")
|
|
553
659
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
554
660
|
|
|
555
661
|
|
|
@@ -560,19 +666,19 @@ class EarliestSweepKey(BaseIndexedDBKey):
|
|
|
560
666
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
561
667
|
"""Decodes the earliest sweep value."""
|
|
562
668
|
_, data = decoder.ReadBytes()
|
|
563
|
-
return int.from_bytes(data, byteorder=
|
|
669
|
+
return int.from_bytes(data, byteorder="little", signed=False)
|
|
564
670
|
|
|
565
671
|
@classmethod
|
|
566
672
|
def FromDecoder(
|
|
567
673
|
cls,
|
|
568
674
|
decoder: utils.LevelDBDecoder,
|
|
569
675
|
key_prefix: KeyPrefix,
|
|
570
|
-
base_offset: int = 0
|
|
676
|
+
base_offset: int = 0,
|
|
571
677
|
) -> EarliestSweepKey:
|
|
572
678
|
"""Decodes the earliest sweep value."""
|
|
573
679
|
offset, key_type = decoder.DecodeUint8()
|
|
574
680
|
if key_type != definitions.GlobalMetadataKeyType.EARLIEST_SWEEP:
|
|
575
|
-
raise errors.ParserError(
|
|
681
|
+
raise errors.ParserError("Not a EarliestSweepKey")
|
|
576
682
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
577
683
|
|
|
578
684
|
|
|
@@ -583,19 +689,19 @@ class EarliestCompactionTimeKey(BaseIndexedDBKey):
|
|
|
583
689
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
584
690
|
"""Decodes the earliest compaction time value."""
|
|
585
691
|
_, data = decoder.ReadBytes()
|
|
586
|
-
return int.from_bytes(data, byteorder=
|
|
692
|
+
return int.from_bytes(data, byteorder="little", signed=False)
|
|
587
693
|
|
|
588
694
|
@classmethod
|
|
589
695
|
def FromDecoder(
|
|
590
696
|
cls,
|
|
591
697
|
decoder: utils.LevelDBDecoder,
|
|
592
698
|
key_prefix: KeyPrefix,
|
|
593
|
-
base_offset: int = 0
|
|
699
|
+
base_offset: int = 0,
|
|
594
700
|
) -> EarliestCompactionTimeKey:
|
|
595
701
|
"""Decodes the earliest compaction time key."""
|
|
596
702
|
offset, key_type = decoder.DecodeUint8()
|
|
597
703
|
if key_type != definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME:
|
|
598
|
-
raise errors.ParserError(
|
|
704
|
+
raise errors.ParserError("Not a EarliestCompactionTimeKey")
|
|
599
705
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
600
706
|
|
|
601
707
|
|
|
@@ -605,7 +711,7 @@ class ScopesPrefixKey(BaseIndexedDBKey):
|
|
|
605
711
|
|
|
606
712
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Optional[bytes]:
|
|
607
713
|
"""Decodes the scopes prefix value."""
|
|
608
|
-
if decoder.NumRemainingBytes:
|
|
714
|
+
if decoder.NumRemainingBytes():
|
|
609
715
|
return decoder.ReadBytes()[1]
|
|
610
716
|
return None
|
|
611
717
|
|
|
@@ -614,12 +720,12 @@ class ScopesPrefixKey(BaseIndexedDBKey):
|
|
|
614
720
|
cls,
|
|
615
721
|
decoder: utils.LevelDBDecoder,
|
|
616
722
|
key_prefix: KeyPrefix,
|
|
617
|
-
base_offset: int = 0
|
|
723
|
+
base_offset: int = 0,
|
|
618
724
|
) -> ScopesPrefixKey:
|
|
619
725
|
"""Decodes the scopes prefix key."""
|
|
620
726
|
offset, key_type = decoder.DecodeUint8()
|
|
621
727
|
if key_type != definitions.GlobalMetadataKeyType.SCOPES_PREFIX:
|
|
622
|
-
raise errors.ParserError(
|
|
728
|
+
raise errors.ParserError("Not a ScopesPrefixKey")
|
|
623
729
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
624
730
|
|
|
625
731
|
|
|
@@ -630,27 +736,30 @@ class DatabaseFreeListKey(BaseIndexedDBKey):
|
|
|
630
736
|
Attributes:
|
|
631
737
|
database_id: the database ID.
|
|
632
738
|
"""
|
|
739
|
+
|
|
633
740
|
database_id: int
|
|
634
741
|
|
|
635
|
-
def DecodeValue(self, decoder: utils.LevelDBDecoder):
|
|
742
|
+
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
|
|
636
743
|
"""Decodes the database free list value."""
|
|
637
|
-
raise NotImplementedError(
|
|
744
|
+
raise NotImplementedError("DatabaseFreeListKey.decode_value")
|
|
638
745
|
|
|
639
746
|
@classmethod
|
|
640
747
|
def FromDecoder(
|
|
641
748
|
cls,
|
|
642
749
|
decoder: utils.LevelDBDecoder,
|
|
643
750
|
key_prefix: KeyPrefix,
|
|
644
|
-
base_offset: int = 0
|
|
751
|
+
base_offset: int = 0,
|
|
645
752
|
) -> DatabaseFreeListKey:
|
|
646
753
|
"""Decodes the database free list key."""
|
|
647
754
|
offset, key_type = decoder.DecodeUint8()
|
|
648
755
|
if key_type != definitions.GlobalMetadataKeyType.DATABASE_FREE_LIST:
|
|
649
|
-
raise errors.ParserError(
|
|
756
|
+
raise errors.ParserError("Not a DatabaseFreeListKey")
|
|
650
757
|
_, database_id = decoder.DecodeVarint()
|
|
651
758
|
return cls(
|
|
652
|
-
offset=base_offset + offset,
|
|
653
|
-
|
|
759
|
+
offset=base_offset + offset,
|
|
760
|
+
key_prefix=key_prefix,
|
|
761
|
+
database_id=database_id,
|
|
762
|
+
)
|
|
654
763
|
|
|
655
764
|
|
|
656
765
|
@dataclass
|
|
@@ -661,6 +770,7 @@ class DatabaseNameKey(BaseIndexedDBKey):
|
|
|
661
770
|
origin: the origin of the database.
|
|
662
771
|
database_name: the database name.
|
|
663
772
|
"""
|
|
773
|
+
|
|
664
774
|
origin: str
|
|
665
775
|
database_name: str
|
|
666
776
|
|
|
@@ -676,45 +786,44 @@ class DatabaseNameKey(BaseIndexedDBKey):
|
|
|
676
786
|
cls,
|
|
677
787
|
decoder: utils.LevelDBDecoder,
|
|
678
788
|
key_prefix: KeyPrefix,
|
|
679
|
-
base_offset: int = 0
|
|
789
|
+
base_offset: int = 0,
|
|
680
790
|
) -> DatabaseNameKey:
|
|
681
791
|
"""Decodes the database name key."""
|
|
682
792
|
offset, key_type = decoder.DecodeUint8()
|
|
683
793
|
if key_type != definitions.GlobalMetadataKeyType.DATABASE_NAME:
|
|
684
|
-
raise errors.ParserError(
|
|
794
|
+
raise errors.ParserError("Not a DatabaseNameKey")
|
|
685
795
|
_, origin = decoder.DecodeStringWithLength()
|
|
686
796
|
_, database_name = decoder.DecodeStringWithLength()
|
|
687
797
|
return cls(
|
|
688
|
-
offset=base_offset + offset,
|
|
689
|
-
|
|
798
|
+
offset=base_offset + offset,
|
|
799
|
+
key_prefix=key_prefix,
|
|
800
|
+
origin=origin,
|
|
801
|
+
database_name=database_name,
|
|
802
|
+
)
|
|
690
803
|
|
|
691
804
|
|
|
692
805
|
@dataclass
|
|
693
806
|
class GlobalMetaDataKey(BaseIndexedDBKey):
|
|
694
807
|
"""A GlobalMetaDataKey parser."""
|
|
695
808
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
definitions.GlobalMetadataKeyType
|
|
704
|
-
|
|
705
|
-
definitions.GlobalMetadataKeyType
|
|
706
|
-
|
|
707
|
-
definitions.GlobalMetadataKeyType
|
|
708
|
-
|
|
709
|
-
definitions.GlobalMetadataKeyType
|
|
710
|
-
|
|
711
|
-
definitions.GlobalMetadataKeyType
|
|
712
|
-
.RECOVERY_BLOB_JOURNAL: RecoveryBlobJournalKey,
|
|
713
|
-
definitions.GlobalMetadataKeyType
|
|
714
|
-
.SCHEMA_VERSION: SchemaVersionKey,
|
|
715
|
-
definitions.GlobalMetadataKeyType
|
|
716
|
-
.SCOPES_PREFIX: ScopesPrefixKey,
|
|
809
|
+
# pylint: disable=line-too-long
|
|
810
|
+
METADATA_TYPE_TO_CLASS: ClassVar[
|
|
811
|
+
dict[ # pylint: disable=invalid-name
|
|
812
|
+
definitions.GlobalMetadataKeyType, type[BaseIndexedDBKey]
|
|
813
|
+
]
|
|
814
|
+
] = {
|
|
815
|
+
definitions.GlobalMetadataKeyType.ACTIVE_BLOB_JOURNAL: ActiveBlobJournalKey,
|
|
816
|
+
definitions.GlobalMetadataKeyType.DATA_VERSION: DataVersionKey,
|
|
817
|
+
definitions.GlobalMetadataKeyType.DATABASE_FREE_LIST: DatabaseFreeListKey,
|
|
818
|
+
definitions.GlobalMetadataKeyType.DATABASE_NAME: DatabaseNameKey,
|
|
819
|
+
definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME: EarliestCompactionTimeKey,
|
|
820
|
+
definitions.GlobalMetadataKeyType.EARLIEST_SWEEP: EarliestSweepKey,
|
|
821
|
+
definitions.GlobalMetadataKeyType.MAX_DATABASE_ID: MaxDatabaseIdKey,
|
|
822
|
+
definitions.GlobalMetadataKeyType.RECOVERY_BLOB_JOURNAL: RecoveryBlobJournalKey,
|
|
823
|
+
definitions.GlobalMetadataKeyType.SCHEMA_VERSION: SchemaVersionKey,
|
|
824
|
+
definitions.GlobalMetadataKeyType.SCOPES_PREFIX: ScopesPrefixKey,
|
|
717
825
|
}
|
|
826
|
+
# pylint: enable=line-too-long
|
|
718
827
|
|
|
719
828
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Any:
|
|
720
829
|
"""Decodes the value from the current position of the LevelDBDecoder.
|
|
@@ -728,17 +837,8 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
|
|
|
728
837
|
cls,
|
|
729
838
|
decoder: utils.LevelDBDecoder,
|
|
730
839
|
key_prefix: KeyPrefix,
|
|
731
|
-
base_offset: int = 0
|
|
732
|
-
) ->
|
|
733
|
-
DataVersionKey,
|
|
734
|
-
DatabaseFreeListKey,
|
|
735
|
-
DatabaseNameKey,
|
|
736
|
-
EarliestSweepKey,
|
|
737
|
-
EarliestCompactionTimeKey,
|
|
738
|
-
MaxDatabaseIdKey,
|
|
739
|
-
RecoveryBlobJournalKey,
|
|
740
|
-
SchemaVersionKey,
|
|
741
|
-
ScopesPrefixKey]:
|
|
840
|
+
base_offset: int = 0,
|
|
841
|
+
) -> BaseIndexedDBKey:
|
|
742
842
|
"""Decodes the global metadata key.
|
|
743
843
|
|
|
744
844
|
Raises:
|
|
@@ -749,9 +849,8 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
|
|
|
749
849
|
|
|
750
850
|
key_class = cls.METADATA_TYPE_TO_CLASS.get(metadata_type)
|
|
751
851
|
if not key_class:
|
|
752
|
-
raise errors.ParserError(
|
|
753
|
-
return key_class.FromDecoder(
|
|
754
|
-
decoder, key_prefix, base_offset)
|
|
852
|
+
raise errors.ParserError("Unknown metadata key type")
|
|
853
|
+
return key_class.FromDecoder(decoder, key_prefix, base_offset)
|
|
755
854
|
|
|
756
855
|
|
|
757
856
|
@dataclass
|
|
@@ -761,27 +860,30 @@ class ObjectStoreFreeListKey(BaseIndexedDBKey):
|
|
|
761
860
|
Attributes:
|
|
762
861
|
object_store_id: the ID of the object store containing the free list.
|
|
763
862
|
"""
|
|
863
|
+
|
|
764
864
|
object_store_id: int
|
|
765
865
|
|
|
766
|
-
def DecodeValue(self, decoder: utils.LevelDBDecoder):
|
|
866
|
+
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
|
|
767
867
|
"""Decodes the object store free list value."""
|
|
768
|
-
raise NotImplementedError(
|
|
868
|
+
raise NotImplementedError("ObjectStoreFreeListKey.decode_value")
|
|
769
869
|
|
|
770
870
|
@classmethod
|
|
771
871
|
def FromDecoder(
|
|
772
872
|
cls,
|
|
773
873
|
decoder: utils.LevelDBDecoder,
|
|
774
874
|
key_prefix: KeyPrefix,
|
|
775
|
-
base_offset: int = 0
|
|
875
|
+
base_offset: int = 0,
|
|
776
876
|
) -> ObjectStoreFreeListKey:
|
|
777
877
|
"""Decodes the object store free list key."""
|
|
778
878
|
offset, key_type = decoder.DecodeUint8()
|
|
779
879
|
if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST:
|
|
780
|
-
raise errors.ParserError(
|
|
880
|
+
raise errors.ParserError("Not am ObjectStoreFreeListKey")
|
|
781
881
|
_, object_store_id = decoder.DecodeVarint()
|
|
782
882
|
return cls(
|
|
783
|
-
offset=base_offset + offset,
|
|
784
|
-
|
|
883
|
+
offset=base_offset + offset,
|
|
884
|
+
key_prefix=key_prefix,
|
|
885
|
+
object_store_id=object_store_id,
|
|
886
|
+
)
|
|
785
887
|
|
|
786
888
|
|
|
787
889
|
@dataclass
|
|
@@ -791,27 +893,30 @@ class IndexFreeListKey(BaseIndexedDBKey):
|
|
|
791
893
|
Attributes:
|
|
792
894
|
object_store_id: the ID of the object store containing the free list.
|
|
793
895
|
"""
|
|
896
|
+
|
|
794
897
|
object_store_id: int
|
|
795
898
|
|
|
796
|
-
def DecodeValue(self, decoder: utils.LevelDBDecoder):
|
|
899
|
+
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
|
|
797
900
|
"""Decodes the index free list value."""
|
|
798
|
-
raise NotImplementedError(
|
|
901
|
+
raise NotImplementedError("IndexFreeListKey.decode_value")
|
|
799
902
|
|
|
800
903
|
@classmethod
|
|
801
904
|
def FromDecoder(
|
|
802
905
|
cls,
|
|
803
906
|
decoder: utils.LevelDBDecoder,
|
|
804
907
|
key_prefix: KeyPrefix,
|
|
805
|
-
base_offset: int = 0
|
|
806
|
-
|
|
908
|
+
base_offset: int = 0,
|
|
909
|
+
) -> IndexFreeListKey:
|
|
807
910
|
"""Decodes the index free list key."""
|
|
808
911
|
offset, key_type = decoder.DecodeUint8()
|
|
809
912
|
if key_type != definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
|
|
810
|
-
raise errors.ParserError(
|
|
913
|
+
raise errors.ParserError("Not am IndexFreeListKey")
|
|
811
914
|
_, object_store_id = decoder.DecodeVarint()
|
|
812
915
|
return cls(
|
|
813
|
-
offset=base_offset + offset,
|
|
814
|
-
|
|
916
|
+
offset=base_offset + offset,
|
|
917
|
+
key_prefix=key_prefix,
|
|
918
|
+
object_store_id=object_store_id,
|
|
919
|
+
)
|
|
815
920
|
|
|
816
921
|
|
|
817
922
|
@dataclass
|
|
@@ -821,6 +926,7 @@ class ObjectStoreNamesKey(BaseIndexedDBKey):
|
|
|
821
926
|
Attributes:
|
|
822
927
|
object_store_name: the name of the object store.
|
|
823
928
|
"""
|
|
929
|
+
|
|
824
930
|
object_store_name: str
|
|
825
931
|
|
|
826
932
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
@@ -829,17 +935,21 @@ class ObjectStoreNamesKey(BaseIndexedDBKey):
|
|
|
829
935
|
|
|
830
936
|
@classmethod
|
|
831
937
|
def FromDecoder(
|
|
832
|
-
cls,
|
|
938
|
+
cls,
|
|
939
|
+
decoder: utils.LevelDBDecoder,
|
|
833
940
|
key_prefix: KeyPrefix,
|
|
834
|
-
base_offset: int = 0
|
|
941
|
+
base_offset: int = 0,
|
|
835
942
|
) -> ObjectStoreNamesKey:
|
|
836
943
|
"""Decodes the object store names key."""
|
|
837
944
|
offset, key_type = decoder.DecodeUint8()
|
|
838
945
|
if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES:
|
|
839
|
-
raise errors.ParserError(
|
|
946
|
+
raise errors.ParserError("Not am ObjectStoreNamesKey")
|
|
840
947
|
_, object_store_name = decoder.DecodeStringWithLength()
|
|
841
|
-
return cls(
|
|
842
|
-
|
|
948
|
+
return cls(
|
|
949
|
+
key_prefix=key_prefix,
|
|
950
|
+
offset=base_offset + offset,
|
|
951
|
+
object_store_name=object_store_name,
|
|
952
|
+
)
|
|
843
953
|
|
|
844
954
|
|
|
845
955
|
@dataclass
|
|
@@ -849,6 +959,7 @@ class IndexNamesKey(BaseIndexedDBKey):
|
|
|
849
959
|
Attributes:
|
|
850
960
|
index_name: the name of the index.
|
|
851
961
|
"""
|
|
962
|
+
|
|
852
963
|
index_name: str
|
|
853
964
|
|
|
854
965
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
@@ -860,15 +971,18 @@ class IndexNamesKey(BaseIndexedDBKey):
|
|
|
860
971
|
cls,
|
|
861
972
|
decoder: utils.LevelDBDecoder,
|
|
862
973
|
key_prefix: KeyPrefix,
|
|
863
|
-
base_offset: int = 0
|
|
974
|
+
base_offset: int = 0,
|
|
864
975
|
) -> IndexNamesKey:
|
|
865
976
|
"""Decodes the index names key."""
|
|
866
977
|
offset, key_type = decoder.DecodeUint8()
|
|
867
978
|
if key_type != definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
|
|
868
|
-
raise errors.ParserError(
|
|
979
|
+
raise errors.ParserError("Not am IndexNamesKey")
|
|
869
980
|
_, index_name = decoder.DecodeStringWithLength()
|
|
870
|
-
return cls(
|
|
871
|
-
|
|
981
|
+
return cls(
|
|
982
|
+
key_prefix=key_prefix,
|
|
983
|
+
offset=base_offset + offset,
|
|
984
|
+
index_name=index_name,
|
|
985
|
+
)
|
|
872
986
|
|
|
873
987
|
|
|
874
988
|
@dataclass
|
|
@@ -878,39 +992,45 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
|
|
|
878
992
|
Attributes:
|
|
879
993
|
metadata_type: the type of metadata that the key value contains.
|
|
880
994
|
"""
|
|
995
|
+
|
|
881
996
|
metadata_type: definitions.DatabaseMetaDataKeyType
|
|
882
997
|
|
|
883
|
-
def DecodeValue(
|
|
884
|
-
self, decoder: utils.LevelDBDecoder) -> Union[str, int]:
|
|
998
|
+
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Union[str, int]:
|
|
885
999
|
"""Decodes the database metadata value."""
|
|
886
|
-
if
|
|
887
|
-
definitions.DatabaseMetaDataKeyType.ORIGIN_NAME):
|
|
1000
|
+
if self.metadata_type == definitions.DatabaseMetaDataKeyType.ORIGIN_NAME:
|
|
888
1001
|
return decoder.DecodeString()[1]
|
|
889
|
-
if
|
|
890
|
-
definitions.DatabaseMetaDataKeyType.DATABASE_NAME):
|
|
1002
|
+
if self.metadata_type == definitions.DatabaseMetaDataKeyType.DATABASE_NAME:
|
|
891
1003
|
return decoder.DecodeString()[1]
|
|
892
|
-
if (
|
|
893
|
-
|
|
1004
|
+
if (
|
|
1005
|
+
self.metadata_type
|
|
1006
|
+
== definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA
|
|
1007
|
+
):
|
|
894
1008
|
return decoder.DecodeString()[1]
|
|
895
|
-
if (
|
|
896
|
-
|
|
1009
|
+
if (
|
|
1010
|
+
self.metadata_type
|
|
1011
|
+
== definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID
|
|
1012
|
+
):
|
|
897
1013
|
return decoder.DecodeInt()[1]
|
|
898
|
-
if (
|
|
899
|
-
|
|
1014
|
+
if (
|
|
1015
|
+
self.metadata_type
|
|
1016
|
+
== definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION
|
|
1017
|
+
):
|
|
900
1018
|
return decoder.DecodeVarint()[1]
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
1019
|
+
if (
|
|
1020
|
+
self.metadata_type
|
|
1021
|
+
== definitions.DatabaseMetaDataKeyType.BLOB_NUMBER_GENERATOR_CURRENT_NUMBER # pylint: disable=line-too-long
|
|
1022
|
+
):
|
|
904
1023
|
return decoder.DecodeVarint()[1]
|
|
905
1024
|
raise errors.ParserError(
|
|
906
|
-
f
|
|
1025
|
+
f"Unknown database metadata type {self.metadata_type}"
|
|
1026
|
+
)
|
|
907
1027
|
|
|
908
1028
|
@classmethod
|
|
909
1029
|
def FromDecoder(
|
|
910
1030
|
cls,
|
|
911
1031
|
decoder: utils.LevelDBDecoder,
|
|
912
1032
|
key_prefix: KeyPrefix,
|
|
913
|
-
base_offset: int = 0
|
|
1033
|
+
base_offset: int = 0,
|
|
914
1034
|
) -> Union[
|
|
915
1035
|
DatabaseMetaDataKey,
|
|
916
1036
|
IndexFreeListKey,
|
|
@@ -918,7 +1038,8 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
|
|
|
918
1038
|
IndexNamesKey,
|
|
919
1039
|
ObjectStoreFreeListKey,
|
|
920
1040
|
ObjectStoreMetaDataKey,
|
|
921
|
-
ObjectStoreNamesKey
|
|
1041
|
+
ObjectStoreNamesKey,
|
|
1042
|
+
]:
|
|
922
1043
|
"""Decodes the database metadata key."""
|
|
923
1044
|
offset, metadata_value = decoder.PeekBytes(1)
|
|
924
1045
|
metadata_type = definitions.DatabaseMetaDataKeyType(metadata_value[0])
|
|
@@ -929,32 +1050,36 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
|
|
|
929
1050
|
definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA,
|
|
930
1051
|
definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID,
|
|
931
1052
|
definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION,
|
|
932
|
-
definitions.DatabaseMetaDataKeyType
|
|
933
|
-
|
|
934
|
-
return cls(
|
|
935
|
-
|
|
1053
|
+
definitions.DatabaseMetaDataKeyType.BLOB_NUMBER_GENERATOR_CURRENT_NUMBER, # pylint: disable=line-too-long
|
|
1054
|
+
):
|
|
1055
|
+
return cls(
|
|
1056
|
+
key_prefix=key_prefix,
|
|
1057
|
+
offset=base_offset + offset,
|
|
1058
|
+
metadata_type=metadata_type,
|
|
1059
|
+
)
|
|
936
1060
|
if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
|
|
937
|
-
return IndexMetaDataKey.FromDecoder(
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA
|
|
1061
|
+
return IndexMetaDataKey.FromDecoder(decoder, key_prefix, base_offset)
|
|
1062
|
+
if (
|
|
1063
|
+
metadata_type
|
|
1064
|
+
== definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA
|
|
1065
|
+
):
|
|
941
1066
|
return ObjectStoreMetaDataKey.FromDecoder(
|
|
942
|
-
decoder, key_prefix, base_offset
|
|
943
|
-
|
|
944
|
-
|
|
1067
|
+
decoder, key_prefix, base_offset
|
|
1068
|
+
)
|
|
1069
|
+
if (
|
|
1070
|
+
metadata_type
|
|
1071
|
+
== definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST
|
|
1072
|
+
):
|
|
945
1073
|
return ObjectStoreFreeListKey.FromDecoder(
|
|
946
|
-
decoder, key_prefix, base_offset
|
|
1074
|
+
decoder, key_prefix, base_offset
|
|
1075
|
+
)
|
|
947
1076
|
if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
|
|
948
|
-
return IndexFreeListKey.FromDecoder(
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES):
|
|
952
|
-
return ObjectStoreNamesKey.FromDecoder(
|
|
953
|
-
decoder, key_prefix, base_offset)
|
|
1077
|
+
return IndexFreeListKey.FromDecoder(decoder, key_prefix, base_offset)
|
|
1078
|
+
if metadata_type == definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES:
|
|
1079
|
+
return ObjectStoreNamesKey.FromDecoder(decoder, key_prefix, base_offset)
|
|
954
1080
|
if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
|
|
955
|
-
return IndexNamesKey.FromDecoder(
|
|
956
|
-
|
|
957
|
-
raise errors.ParserError(f'unknown database metadata type {metadata_type}.')
|
|
1081
|
+
return IndexNamesKey.FromDecoder(decoder, key_prefix, base_offset)
|
|
1082
|
+
raise errors.ParserError(f"unknown database metadata type {metadata_type}.")
|
|
958
1083
|
|
|
959
1084
|
|
|
960
1085
|
@dataclass
|
|
@@ -965,58 +1090,78 @@ class ObjectStoreMetaDataKey(BaseIndexedDBKey):
|
|
|
965
1090
|
object_store_id: the ID of the object store.
|
|
966
1091
|
metadata_type: the object store metadata type.
|
|
967
1092
|
"""
|
|
1093
|
+
|
|
968
1094
|
object_store_id: int
|
|
969
1095
|
metadata_type: definitions.ObjectStoreMetaDataKeyType
|
|
970
1096
|
|
|
971
1097
|
def DecodeValue(
|
|
972
|
-
self,
|
|
973
|
-
decoder: utils.LevelDBDecoder
|
|
1098
|
+
self, decoder: utils.LevelDBDecoder
|
|
974
1099
|
) -> Union[IDBKeyPath, str, bool, int]:
|
|
975
1100
|
"""Decodes the object store metadata value."""
|
|
976
|
-
if (
|
|
977
|
-
|
|
1101
|
+
if (
|
|
1102
|
+
self.metadata_type
|
|
1103
|
+
== definitions.ObjectStoreMetaDataKeyType.OBJECT_STORE_NAME
|
|
1104
|
+
):
|
|
978
1105
|
return decoder.DecodeString()[1]
|
|
979
1106
|
if self.metadata_type == definitions.ObjectStoreMetaDataKeyType.KEY_PATH:
|
|
980
1107
|
return IDBKeyPath.FromDecoder(decoder)
|
|
981
|
-
if (
|
|
982
|
-
|
|
1108
|
+
if (
|
|
1109
|
+
self.metadata_type
|
|
1110
|
+
== definitions.ObjectStoreMetaDataKeyType.AUTO_INCREMENT_FLAG
|
|
1111
|
+
):
|
|
983
1112
|
return decoder.DecodeBool()[1]
|
|
984
|
-
if (
|
|
985
|
-
|
|
1113
|
+
if (
|
|
1114
|
+
self.metadata_type
|
|
1115
|
+
== definitions.ObjectStoreMetaDataKeyType.IS_EVICTABLE
|
|
1116
|
+
):
|
|
986
1117
|
return decoder.DecodeBool()[1]
|
|
987
|
-
if (
|
|
988
|
-
|
|
1118
|
+
if (
|
|
1119
|
+
self.metadata_type
|
|
1120
|
+
== definitions.ObjectStoreMetaDataKeyType.LAST_VERSION_NUMBER
|
|
1121
|
+
):
|
|
989
1122
|
return decoder.DecodeInt(signed=False)[1]
|
|
990
|
-
if (
|
|
991
|
-
|
|
1123
|
+
if (
|
|
1124
|
+
self.metadata_type
|
|
1125
|
+
== definitions.ObjectStoreMetaDataKeyType.MAXIMUM_ALLOCATED_INDEX_ID
|
|
1126
|
+
):
|
|
992
1127
|
return decoder.DecodeInt()[1]
|
|
993
|
-
if (
|
|
994
|
-
|
|
1128
|
+
if (
|
|
1129
|
+
self.metadata_type
|
|
1130
|
+
== definitions.ObjectStoreMetaDataKeyType.HAS_KEY_PATH
|
|
1131
|
+
):
|
|
995
1132
|
return decoder.DecodeBool()[1]
|
|
996
|
-
if (
|
|
997
|
-
|
|
1133
|
+
if (
|
|
1134
|
+
self.metadata_type
|
|
1135
|
+
== definitions.ObjectStoreMetaDataKeyType.KEY_GENERATOR_CURRENT_NUMBER
|
|
1136
|
+
):
|
|
998
1137
|
return decoder.DecodeInt()[1]
|
|
999
|
-
raise errors.ParserError(f
|
|
1138
|
+
raise errors.ParserError(f"Unknown metadata type {self.metadata_type}")
|
|
1000
1139
|
|
|
1001
1140
|
@classmethod
|
|
1002
1141
|
def FromDecoder(
|
|
1003
1142
|
cls,
|
|
1004
1143
|
decoder: utils.LevelDBDecoder,
|
|
1005
1144
|
key_prefix: KeyPrefix,
|
|
1006
|
-
base_offset: int = 0
|
|
1145
|
+
base_offset: int = 0,
|
|
1007
1146
|
) -> ObjectStoreMetaDataKey:
|
|
1008
1147
|
"""Decodes the object store metadata key."""
|
|
1009
1148
|
offset, metadata_value = decoder.DecodeUint8()
|
|
1010
|
-
if (
|
|
1011
|
-
|
|
1012
|
-
|
|
1149
|
+
if (
|
|
1150
|
+
metadata_value
|
|
1151
|
+
!= definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA
|
|
1152
|
+
):
|
|
1153
|
+
raise errors.ParserError("Not a ObjectStoreMetaDataKey")
|
|
1013
1154
|
|
|
1014
1155
|
_, object_store_id = decoder.DecodeVarint()
|
|
1015
1156
|
_, metadata_value = decoder.DecodeUint8()
|
|
1016
1157
|
metadata_type = definitions.ObjectStoreMetaDataKeyType(metadata_value)
|
|
1017
1158
|
return cls(
|
|
1018
|
-
offset=base_offset + offset,
|
|
1019
|
-
|
|
1159
|
+
offset=base_offset + offset,
|
|
1160
|
+
key_prefix=key_prefix,
|
|
1161
|
+
object_store_id=object_store_id,
|
|
1162
|
+
metadata_type=metadata_type,
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1020
1165
|
|
|
1021
1166
|
@dataclass
|
|
1022
1167
|
class ObjectStoreDataValue:
|
|
@@ -1024,13 +1169,12 @@ class ObjectStoreDataValue:
|
|
|
1024
1169
|
|
|
1025
1170
|
Attributes:
|
|
1026
1171
|
version: the version prefix.
|
|
1027
|
-
is_wrapped: True if the value was wrapped.
|
|
1028
1172
|
blob_size: the blob size, only valid if wrapped.
|
|
1029
1173
|
blob_offset: the blob offset, only valid if wrapped.
|
|
1030
1174
|
value: the blink serialized value, only valid if not wrapped.
|
|
1031
1175
|
"""
|
|
1176
|
+
|
|
1032
1177
|
version: int
|
|
1033
|
-
is_wrapped: bool
|
|
1034
1178
|
blob_size: Optional[int]
|
|
1035
1179
|
blob_offset: Optional[int]
|
|
1036
1180
|
value: Any
|
|
@@ -1043,68 +1187,59 @@ class ObjectStoreDataKey(BaseIndexedDBKey):
|
|
|
1043
1187
|
Attributes:
|
|
1044
1188
|
encoded_user_key: the encoded user key.
|
|
1045
1189
|
"""
|
|
1190
|
+
|
|
1046
1191
|
encoded_user_key: IDBKey
|
|
1047
1192
|
|
|
1048
|
-
def DecodeValue(
|
|
1049
|
-
self,
|
|
1050
|
-
decoder: utils.LevelDBDecoder
|
|
1051
|
-
) -> ObjectStoreDataValue:
|
|
1193
|
+
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> ObjectStoreDataValue:
|
|
1052
1194
|
"""Decodes the object store data value."""
|
|
1053
1195
|
_, version = decoder.DecodeVarint()
|
|
1054
1196
|
|
|
1055
1197
|
_, wrapped_header_bytes = decoder.PeekBytes(3)
|
|
1056
1198
|
if len(wrapped_header_bytes) != 3:
|
|
1057
|
-
raise errors.DecoderError(
|
|
1199
|
+
raise errors.DecoderError("Insufficient bytes")
|
|
1058
1200
|
|
|
1059
|
-
if (
|
|
1060
|
-
|
|
1061
|
-
wrapped_header_bytes[1]
|
|
1062
|
-
|
|
1063
|
-
wrapped_header_bytes[2] == definitions.REPLACE_WITH_BLOB
|
|
1201
|
+
if (
|
|
1202
|
+
wrapped_header_bytes[0] == definitions.BlinkSerializationTag.VERSION
|
|
1203
|
+
and wrapped_header_bytes[1]
|
|
1204
|
+
== definitions.REQUIRES_PROCESSING_SSV_PSEUDO_VERSION
|
|
1205
|
+
and wrapped_header_bytes[2] == definitions.REPLACE_WITH_BLOB
|
|
1206
|
+
):
|
|
1064
1207
|
_ = decoder.ReadBytes(3)
|
|
1065
1208
|
_, blob_size = decoder.DecodeVarint()
|
|
1066
1209
|
_, blob_offset = decoder.DecodeVarint()
|
|
1067
1210
|
return ObjectStoreDataValue(
|
|
1068
1211
|
version=version,
|
|
1069
|
-
is_wrapped=True,
|
|
1070
1212
|
blob_size=blob_size,
|
|
1071
1213
|
blob_offset=blob_offset,
|
|
1072
|
-
value=None
|
|
1214
|
+
value=None,
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1073
1217
|
_, blink_bytes = decoder.ReadBytes()
|
|
1074
|
-
is_wrapped = False
|
|
1075
|
-
if (
|
|
1076
|
-
wrapped_header_bytes[0] ==
|
|
1077
|
-
definitions.BlinkSerializationTag.VERSION and
|
|
1078
|
-
wrapped_header_bytes[1] ==
|
|
1079
|
-
definitions.REQUIRES_PROCESSING_SSV_PSEUDO_VERSION and
|
|
1080
|
-
wrapped_header_bytes[2] == definitions.COMPRESSED_WITH_SNAPPY):
|
|
1081
|
-
is_wrapped = True
|
|
1082
|
-
# ignore the wrapped header bytes when decompressing
|
|
1083
|
-
blink_bytes = snappy.decompress(blink_bytes[3:])
|
|
1084
1218
|
blink_value = blink.V8ScriptValueDecoder.FromBytes(blink_bytes)
|
|
1085
1219
|
return ObjectStoreDataValue(
|
|
1086
|
-
version=version,
|
|
1087
|
-
|
|
1088
|
-
blob_size=None,
|
|
1089
|
-
blob_offset=None,
|
|
1090
|
-
value=blink_value)
|
|
1220
|
+
version=version, blob_size=None, blob_offset=None, value=blink_value
|
|
1221
|
+
)
|
|
1091
1222
|
|
|
1092
1223
|
@classmethod
|
|
1093
1224
|
def FromDecoder(
|
|
1094
1225
|
cls,
|
|
1095
1226
|
decoder: utils.LevelDBDecoder,
|
|
1096
1227
|
key_prefix: KeyPrefix,
|
|
1097
|
-
base_offset: int = 0
|
|
1228
|
+
base_offset: int = 0,
|
|
1098
1229
|
) -> ObjectStoreDataKey:
|
|
1099
1230
|
"""Decodes the object store data key."""
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1102
|
-
|
|
1231
|
+
if (
|
|
1232
|
+
key_prefix.GetKeyPrefixType()
|
|
1233
|
+
!= definitions.KeyPrefixType.OBJECT_STORE_DATA
|
|
1234
|
+
):
|
|
1235
|
+
raise errors.ParserError("Invalid KeyPrefix for ObjectStoreDataKey")
|
|
1103
1236
|
offset = decoder.stream.tell()
|
|
1104
1237
|
encoded_user_key = IDBKey.FromDecoder(decoder, offset)
|
|
1105
1238
|
return cls(
|
|
1106
1239
|
offset=base_offset + offset,
|
|
1107
|
-
key_prefix=key_prefix,
|
|
1240
|
+
key_prefix=key_prefix,
|
|
1241
|
+
encoded_user_key=encoded_user_key,
|
|
1242
|
+
)
|
|
1108
1243
|
|
|
1109
1244
|
|
|
1110
1245
|
@dataclass
|
|
@@ -1114,26 +1249,29 @@ class ExistsEntryKey(BaseIndexedDBKey):
|
|
|
1114
1249
|
Attributes:
|
|
1115
1250
|
encoded_user_key: the encoded user key.
|
|
1116
1251
|
"""
|
|
1252
|
+
|
|
1117
1253
|
encoded_user_key: IDBKey
|
|
1118
1254
|
|
|
1119
1255
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
1120
1256
|
"""Decodes the exists entry value."""
|
|
1121
1257
|
_, data = decoder.ReadBytes()
|
|
1122
|
-
return int.from_bytes(data, byteorder=
|
|
1258
|
+
return int.from_bytes(data, byteorder="little", signed=False)
|
|
1123
1259
|
|
|
1124
1260
|
@classmethod
|
|
1125
1261
|
def FromDecoder(
|
|
1126
1262
|
cls,
|
|
1127
1263
|
decoder: utils.LevelDBDecoder,
|
|
1128
1264
|
key_prefix: KeyPrefix,
|
|
1129
|
-
base_offset: int = 0
|
|
1265
|
+
base_offset: int = 0,
|
|
1130
1266
|
) -> ExistsEntryKey:
|
|
1131
1267
|
"""Decodes the exists entry key."""
|
|
1132
1268
|
offset = decoder.stream.tell()
|
|
1133
1269
|
encoded_user_key = IDBKey.FromDecoder(decoder, offset)
|
|
1134
1270
|
return cls(
|
|
1135
1271
|
offset=base_offset + offset,
|
|
1136
|
-
key_prefix=key_prefix,
|
|
1272
|
+
key_prefix=key_prefix,
|
|
1273
|
+
encoded_user_key=encoded_user_key,
|
|
1274
|
+
)
|
|
1137
1275
|
|
|
1138
1276
|
|
|
1139
1277
|
@dataclass
|
|
@@ -1145,6 +1283,7 @@ class IndexDataKey(BaseIndexedDBKey):
|
|
|
1145
1283
|
sequence_number: the sequence number of the data key.
|
|
1146
1284
|
encoded_primary_key: the encoded primary key.
|
|
1147
1285
|
"""
|
|
1286
|
+
|
|
1148
1287
|
encoded_user_key: IDBKey
|
|
1149
1288
|
sequence_number: Optional[int]
|
|
1150
1289
|
encoded_primary_key: Optional[IDBKey]
|
|
@@ -1160,7 +1299,7 @@ class IndexDataKey(BaseIndexedDBKey):
|
|
|
1160
1299
|
cls,
|
|
1161
1300
|
decoder: utils.LevelDBDecoder,
|
|
1162
1301
|
key_prefix: KeyPrefix,
|
|
1163
|
-
base_offset: int = 0
|
|
1302
|
+
base_offset: int = 0,
|
|
1164
1303
|
) -> IndexDataKey:
|
|
1165
1304
|
"""Decodes the index data key."""
|
|
1166
1305
|
offset = decoder.stream.tell()
|
|
@@ -1174,7 +1313,8 @@ class IndexDataKey(BaseIndexedDBKey):
|
|
|
1174
1313
|
if decoder.NumRemainingBytes() > 0:
|
|
1175
1314
|
encoded_primary_key_offset = decoder.stream.tell()
|
|
1176
1315
|
encoded_primary_key = IDBKey.FromDecoder(
|
|
1177
|
-
decoder, encoded_primary_key_offset
|
|
1316
|
+
decoder, encoded_primary_key_offset
|
|
1317
|
+
)
|
|
1178
1318
|
else:
|
|
1179
1319
|
encoded_primary_key = None
|
|
1180
1320
|
|
|
@@ -1183,7 +1323,8 @@ class IndexDataKey(BaseIndexedDBKey):
|
|
|
1183
1323
|
encoded_user_key=encoded_user_key,
|
|
1184
1324
|
sequence_number=sequence_number,
|
|
1185
1325
|
encoded_primary_key=encoded_primary_key,
|
|
1186
|
-
offset=base_offset + offset
|
|
1326
|
+
offset=base_offset + offset,
|
|
1327
|
+
)
|
|
1187
1328
|
|
|
1188
1329
|
|
|
1189
1330
|
@dataclass
|
|
@@ -1193,11 +1334,11 @@ class BlobEntryKey(BaseIndexedDBKey):
|
|
|
1193
1334
|
Attributes:
|
|
1194
1335
|
user_key: the user/primary key.
|
|
1195
1336
|
"""
|
|
1337
|
+
|
|
1196
1338
|
user_key: IDBKey
|
|
1197
1339
|
|
|
1198
1340
|
def DecodeValue(
|
|
1199
|
-
self,
|
|
1200
|
-
decoder: utils.LevelDBDecoder
|
|
1341
|
+
self, decoder: utils.LevelDBDecoder
|
|
1201
1342
|
) -> IndexedDBExternalObject:
|
|
1202
1343
|
"""Decodes the blob entry value."""
|
|
1203
1344
|
return IndexedDBExternalObject.FromDecoder(decoder)
|
|
@@ -1207,14 +1348,15 @@ class BlobEntryKey(BaseIndexedDBKey):
|
|
|
1207
1348
|
cls,
|
|
1208
1349
|
decoder: utils.LevelDBDecoder,
|
|
1209
1350
|
key_prefix: KeyPrefix,
|
|
1210
|
-
base_offset: int = 0
|
|
1351
|
+
base_offset: int = 0,
|
|
1211
1352
|
) -> BlobEntryKey:
|
|
1212
1353
|
"""Decodes the blob entry key."""
|
|
1213
1354
|
offset = decoder.stream.tell()
|
|
1214
1355
|
user_key = IDBKey.FromDecoder(decoder, offset)
|
|
1215
1356
|
|
|
1216
|
-
return cls(
|
|
1217
|
-
|
|
1357
|
+
return cls(
|
|
1358
|
+
key_prefix=key_prefix, user_key=user_key, offset=base_offset + offset
|
|
1359
|
+
)
|
|
1218
1360
|
|
|
1219
1361
|
|
|
1220
1362
|
@dataclass
|
|
@@ -1224,7 +1366,11 @@ class IndexedDbKey(BaseIndexedDBKey):
|
|
|
1224
1366
|
A factory class for parsing IndexedDB keys.
|
|
1225
1367
|
"""
|
|
1226
1368
|
|
|
1227
|
-
METADATA_TYPE_TO_CLASS
|
|
1369
|
+
METADATA_TYPE_TO_CLASS: ClassVar[
|
|
1370
|
+
dict[ # pylint: disable=invalid-name
|
|
1371
|
+
definitions.KeyPrefixType, Optional[type[BaseIndexedDBKey]]
|
|
1372
|
+
]
|
|
1373
|
+
] = {
|
|
1228
1374
|
definitions.KeyPrefixType.BLOB_ENTRY: BlobEntryKey,
|
|
1229
1375
|
definitions.KeyPrefixType.DATABASE_METADATA: DatabaseMetaDataKey,
|
|
1230
1376
|
definitions.KeyPrefixType.EXISTS_ENTRY: ExistsEntryKey,
|
|
@@ -1246,23 +1392,18 @@ class IndexedDbKey(BaseIndexedDBKey):
|
|
|
1246
1392
|
cls,
|
|
1247
1393
|
decoder: utils.LevelDBDecoder,
|
|
1248
1394
|
key_prefix: KeyPrefix,
|
|
1249
|
-
base_offset: int = 0
|
|
1250
|
-
) ->
|
|
1251
|
-
BlobEntryKey,
|
|
1252
|
-
DatabaseMetaDataKey,
|
|
1253
|
-
ExistsEntryKey,
|
|
1254
|
-
GlobalMetaDataKey,
|
|
1255
|
-
IndexDataKey,
|
|
1256
|
-
ObjectStoreDataKey]:
|
|
1395
|
+
base_offset: int = 0,
|
|
1396
|
+
) -> BaseIndexedDBKey:
|
|
1257
1397
|
"""Decodes the IndexedDB key."""
|
|
1258
1398
|
key_type = key_prefix.GetKeyPrefixType()
|
|
1259
1399
|
key_class = cls.METADATA_TYPE_TO_CLASS.get(key_type)
|
|
1260
1400
|
if not key_class:
|
|
1261
|
-
raise errors.ParserError(
|
|
1401
|
+
raise errors.ParserError("Unknown KeyPrefixType")
|
|
1262
1402
|
return key_class.FromDecoder(
|
|
1263
1403
|
decoder=decoder,
|
|
1264
|
-
key_prefix=key_prefix,
|
|
1265
|
-
base_offset=base_offset
|
|
1404
|
+
key_prefix=key_prefix,
|
|
1405
|
+
base_offset=base_offset,
|
|
1406
|
+
)
|
|
1266
1407
|
|
|
1267
1408
|
|
|
1268
1409
|
@dataclass
|
|
@@ -1274,45 +1415,49 @@ class IndexMetaDataKey(BaseIndexedDBKey):
|
|
|
1274
1415
|
index_id: the index ID.
|
|
1275
1416
|
metadata_type: the metadata key type.
|
|
1276
1417
|
"""
|
|
1418
|
+
|
|
1277
1419
|
object_store_id: int
|
|
1278
1420
|
index_id: int
|
|
1279
1421
|
metadata_type: definitions.IndexMetaDataKeyType
|
|
1280
1422
|
|
|
1281
1423
|
def DecodeValue(
|
|
1282
|
-
self,
|
|
1283
|
-
decoder: utils.LevelDBDecoder
|
|
1424
|
+
self, decoder: utils.LevelDBDecoder
|
|
1284
1425
|
) -> Union[bool, IDBKeyPath, str]:
|
|
1285
1426
|
"""Decodes the index metadata value."""
|
|
1286
1427
|
if self.metadata_type == definitions.IndexMetaDataKeyType.INDEX_NAME:
|
|
1287
1428
|
return decoder.DecodeString()[1]
|
|
1288
1429
|
if self.metadata_type == definitions.IndexMetaDataKeyType.KEY_PATH:
|
|
1289
1430
|
return IDBKeyPath.FromDecoder(decoder)
|
|
1290
|
-
if
|
|
1291
|
-
definitions.IndexMetaDataKeyType.MULTI_ENTRY_FLAG):
|
|
1431
|
+
if self.metadata_type == definitions.IndexMetaDataKeyType.MULTI_ENTRY_FLAG:
|
|
1292
1432
|
return decoder.DecodeBool()[1]
|
|
1293
1433
|
if self.metadata_type == definitions.IndexMetaDataKeyType.UNIQUE_FLAG:
|
|
1294
1434
|
return decoder.DecodeBool()[1]
|
|
1295
1435
|
raise errors.ParserError(
|
|
1296
|
-
f
|
|
1436
|
+
f"Unknown index metadata type {self.metadata_type}"
|
|
1437
|
+
)
|
|
1297
1438
|
|
|
1298
1439
|
@classmethod
|
|
1299
1440
|
def FromDecoder(
|
|
1300
1441
|
cls,
|
|
1301
1442
|
decoder: utils.LevelDBDecoder,
|
|
1302
1443
|
key_prefix: KeyPrefix,
|
|
1303
|
-
base_offset: int = 0
|
|
1444
|
+
base_offset: int = 0,
|
|
1304
1445
|
) -> IndexMetaDataKey:
|
|
1305
1446
|
"""Decodes the index metadata key."""
|
|
1306
1447
|
offset, key_type = decoder.DecodeUint8()
|
|
1307
1448
|
if key_type != definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
|
|
1308
|
-
raise errors.ParserError(
|
|
1449
|
+
raise errors.ParserError("Not an IndexMetaDataKey.")
|
|
1309
1450
|
_, object_store_id = decoder.DecodeVarint()
|
|
1310
1451
|
_, index_id = decoder.DecodeVarint()
|
|
1311
1452
|
_, metadata_bytes = decoder.ReadBytes(1)
|
|
1312
1453
|
metadata_type = definitions.IndexMetaDataKeyType(metadata_bytes[0])
|
|
1313
|
-
return cls(
|
|
1314
|
-
|
|
1315
|
-
|
|
1454
|
+
return cls(
|
|
1455
|
+
offset=base_offset + offset,
|
|
1456
|
+
key_prefix=key_prefix,
|
|
1457
|
+
object_store_id=object_store_id,
|
|
1458
|
+
index_id=index_id,
|
|
1459
|
+
metadata_type=metadata_type,
|
|
1460
|
+
)
|
|
1316
1461
|
|
|
1317
1462
|
|
|
1318
1463
|
@dataclass
|
|
@@ -1331,6 +1476,7 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
|
|
|
1331
1476
|
last_modified: the last modified time if a blob or file, None otherwise.
|
|
1332
1477
|
token: the token if a filesystem access handle, None otherwise.
|
|
1333
1478
|
"""
|
|
1479
|
+
|
|
1334
1480
|
offset: int = field(compare=False)
|
|
1335
1481
|
object_type: definitions.ExternalObjectType
|
|
1336
1482
|
blob_number: Optional[int]
|
|
@@ -1342,16 +1488,15 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
|
|
|
1342
1488
|
|
|
1343
1489
|
@classmethod
|
|
1344
1490
|
def FromDecoder(
|
|
1345
|
-
cls,
|
|
1346
|
-
decoder: utils.LevelDBDecoder,
|
|
1347
|
-
base_offset: int = 0
|
|
1491
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
1348
1492
|
) -> ExternalObjectEntry:
|
|
1349
1493
|
"""Decodes the external object entry."""
|
|
1350
1494
|
offset, object_type_value = decoder.DecodeUint8()
|
|
1351
1495
|
object_type = definitions.ExternalObjectType(object_type_value)
|
|
1352
|
-
if
|
|
1353
|
-
|
|
1354
|
-
|
|
1496
|
+
if object_type in (
|
|
1497
|
+
definitions.ExternalObjectType.BLOB,
|
|
1498
|
+
definitions.ExternalObjectType.FILE,
|
|
1499
|
+
):
|
|
1355
1500
|
_, blob_number = decoder.DecodeVarint()
|
|
1356
1501
|
_, mime_type = decoder.DecodeStringWithLength()
|
|
1357
1502
|
_, size = decoder.DecodeVarint()
|
|
@@ -1363,8 +1508,10 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
|
|
|
1363
1508
|
last_modified = None
|
|
1364
1509
|
token = None
|
|
1365
1510
|
else:
|
|
1366
|
-
if (
|
|
1367
|
-
|
|
1511
|
+
if (
|
|
1512
|
+
object_type
|
|
1513
|
+
== definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE
|
|
1514
|
+
):
|
|
1368
1515
|
_, token = decoder.DecodeBlobWithLength()
|
|
1369
1516
|
else:
|
|
1370
1517
|
token = None
|
|
@@ -1374,9 +1521,16 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
|
|
|
1374
1521
|
filename = None
|
|
1375
1522
|
last_modified = None
|
|
1376
1523
|
|
|
1377
|
-
return cls(
|
|
1378
|
-
|
|
1379
|
-
|
|
1524
|
+
return cls(
|
|
1525
|
+
offset=base_offset + offset,
|
|
1526
|
+
object_type=object_type,
|
|
1527
|
+
blob_number=blob_number,
|
|
1528
|
+
mime_type=mime_type,
|
|
1529
|
+
size=size,
|
|
1530
|
+
filename=filename,
|
|
1531
|
+
last_modified=last_modified,
|
|
1532
|
+
token=token,
|
|
1533
|
+
)
|
|
1380
1534
|
|
|
1381
1535
|
|
|
1382
1536
|
@dataclass
|
|
@@ -1387,14 +1541,13 @@ class IndexedDBExternalObject(utils.FromDecoderMixin):
|
|
|
1387
1541
|
offset: the offset of the IndexedDBExternalObject.
|
|
1388
1542
|
entries: a list of external objects.
|
|
1389
1543
|
"""
|
|
1544
|
+
|
|
1390
1545
|
offset: int = field(compare=False)
|
|
1391
1546
|
entries: list[ExternalObjectEntry]
|
|
1392
1547
|
|
|
1393
1548
|
@classmethod
|
|
1394
1549
|
def FromDecoder(
|
|
1395
|
-
cls,
|
|
1396
|
-
decoder: utils.LevelDBDecoder,
|
|
1397
|
-
base_offset: int = 0
|
|
1550
|
+
cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
|
|
1398
1551
|
) -> IndexedDBExternalObject:
|
|
1399
1552
|
"""Decodes the external object."""
|
|
1400
1553
|
entries = []
|
|
@@ -1407,22 +1560,8 @@ class IndexedDBExternalObject(utils.FromDecoderMixin):
|
|
|
1407
1560
|
|
|
1408
1561
|
|
|
1409
1562
|
@dataclass
|
|
1410
|
-
class
|
|
1411
|
-
"""An IndexedDB
|
|
1412
|
-
|
|
1413
|
-
Attributes:
|
|
1414
|
-
id: the object store ID.
|
|
1415
|
-
name: the object store name.
|
|
1416
|
-
records: the records in the object store.
|
|
1417
|
-
"""
|
|
1418
|
-
id: int
|
|
1419
|
-
name: str
|
|
1420
|
-
records: list = field(default_factory=list, repr=False)
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
@dataclass
|
|
1424
|
-
class IndexedDBRecord:
|
|
1425
|
-
"""An IndexedDB Record.
|
|
1563
|
+
class ChromiumIndexedDBRecord:
|
|
1564
|
+
"""An IndexedDB Record parsed from LevelDB.
|
|
1426
1565
|
|
|
1427
1566
|
Attributes:
|
|
1428
1567
|
path: the source file path
|
|
@@ -1434,7 +1573,15 @@ class IndexedDBRecord:
|
|
|
1434
1573
|
level: the leveldb level, if applicable, None can indicate the record
|
|
1435
1574
|
originated from a log file or the level could not be determined.
|
|
1436
1575
|
recovered: True if the record is a recovered record.
|
|
1576
|
+
database_id: the database ID.
|
|
1577
|
+
object_store_id: the object store ID.
|
|
1578
|
+
database_name: the name of the database, if available.
|
|
1579
|
+
object_store_name: the name of the object store, if available.
|
|
1580
|
+
blobs: the list of blob paths and contents or error message, if available.
|
|
1581
|
+
raw_key: the raw key, if available.
|
|
1582
|
+
raw_value: the raw value, if available.
|
|
1437
1583
|
"""
|
|
1584
|
+
|
|
1438
1585
|
path: str
|
|
1439
1586
|
offset: int
|
|
1440
1587
|
key: Any
|
|
@@ -1443,46 +1590,181 @@ class IndexedDBRecord:
|
|
|
1443
1590
|
type: int
|
|
1444
1591
|
level: Optional[int]
|
|
1445
1592
|
recovered: Optional[bool]
|
|
1593
|
+
database_id: int
|
|
1594
|
+
object_store_id: int
|
|
1595
|
+
database_name: Optional[str] = None
|
|
1596
|
+
object_store_name: Optional[str] = None
|
|
1597
|
+
blobs: Optional[list[tuple[str, Optional[Any]]]] = None
|
|
1598
|
+
raw_key: Optional[bytes] = None
|
|
1599
|
+
raw_value: Optional[bytes] = None
|
|
1446
1600
|
|
|
1447
1601
|
@classmethod
|
|
1448
1602
|
def FromLevelDBRecord(
|
|
1449
1603
|
cls,
|
|
1450
|
-
db_record: record.LevelDBRecord
|
|
1451
|
-
|
|
1452
|
-
|
|
1604
|
+
db_record: record.LevelDBRecord,
|
|
1605
|
+
parse_value: bool = True,
|
|
1606
|
+
include_raw_data: bool = False,
|
|
1607
|
+
blob_folder_reader: Optional[BlobFolderReader] = None,
|
|
1608
|
+
) -> ChromiumIndexedDBRecord:
|
|
1609
|
+
"""Returns an ChromiumIndexedDBRecord from a ParsedInternalKey."""
|
|
1453
1610
|
idb_key = IndexedDbKey.FromBytes(
|
|
1454
|
-
db_record.record.key, base_offset=db_record.record.offset
|
|
1455
|
-
|
|
1611
|
+
db_record.record.key, base_offset=db_record.record.offset
|
|
1612
|
+
)
|
|
1613
|
+
|
|
1614
|
+
if parse_value:
|
|
1615
|
+
idb_value = idb_key.ParseValue(db_record.record.value)
|
|
1616
|
+
else:
|
|
1617
|
+
idb_value = None
|
|
1618
|
+
|
|
1619
|
+
blobs = []
|
|
1620
|
+
if isinstance(idb_value, IndexedDBExternalObject) and blob_folder_reader:
|
|
1621
|
+
for (
|
|
1622
|
+
blob_path_or_error,
|
|
1623
|
+
blob_data,
|
|
1624
|
+
) in blob_folder_reader.ReadBlobsFromExternalObjectEntries(
|
|
1625
|
+
idb_key.key_prefix.database_id, idb_value.entries
|
|
1626
|
+
):
|
|
1627
|
+
if blob_data:
|
|
1628
|
+
blob = blink.V8ScriptValueDecoder.FromBytes(blob_data)
|
|
1629
|
+
else:
|
|
1630
|
+
blob = None
|
|
1631
|
+
blobs.append((blob_path_or_error, blob))
|
|
1632
|
+
|
|
1456
1633
|
return cls(
|
|
1457
1634
|
path=db_record.path,
|
|
1458
1635
|
offset=db_record.record.offset,
|
|
1459
1636
|
key=idb_key,
|
|
1460
1637
|
value=idb_value,
|
|
1461
|
-
sequence_number=
|
|
1462
|
-
db_record.record
|
|
1638
|
+
sequence_number=(
|
|
1639
|
+
db_record.record.sequence_number
|
|
1640
|
+
if hasattr(db_record.record, "sequence_number")
|
|
1641
|
+
else None
|
|
1642
|
+
),
|
|
1463
1643
|
type=db_record.record.record_type,
|
|
1464
1644
|
level=db_record.level,
|
|
1465
|
-
recovered=db_record.recovered
|
|
1645
|
+
recovered=db_record.recovered,
|
|
1646
|
+
database_id=idb_key.key_prefix.database_id,
|
|
1647
|
+
object_store_id=idb_key.key_prefix.object_store_id,
|
|
1648
|
+
database_name=None,
|
|
1649
|
+
object_store_name=None,
|
|
1650
|
+
blobs=blobs,
|
|
1651
|
+
raw_key=db_record.record.key if include_raw_data else None,
|
|
1652
|
+
raw_value=db_record.record.value if include_raw_data else None,
|
|
1653
|
+
)
|
|
1466
1654
|
|
|
1467
1655
|
@classmethod
|
|
1468
1656
|
def FromFile(
|
|
1469
1657
|
cls,
|
|
1470
|
-
file_path: pathlib.Path
|
|
1471
|
-
|
|
1472
|
-
|
|
1658
|
+
file_path: pathlib.Path,
|
|
1659
|
+
parse_value: bool = True,
|
|
1660
|
+
include_raw_data: bool = False,
|
|
1661
|
+
blob_folder_reader: Optional[BlobFolderReader] = None,
|
|
1662
|
+
) -> Generator[ChromiumIndexedDBRecord, None, None]:
|
|
1663
|
+
"""Yields ChromiumIndexedDBRecord from a file."""
|
|
1473
1664
|
for db_record in record.LevelDBRecord.FromFile(file_path):
|
|
1474
1665
|
try:
|
|
1475
|
-
yield cls.FromLevelDBRecord(
|
|
1476
|
-
|
|
1666
|
+
yield cls.FromLevelDBRecord(
|
|
1667
|
+
db_record,
|
|
1668
|
+
parse_value=parse_value,
|
|
1669
|
+
include_raw_data=include_raw_data,
|
|
1670
|
+
blob_folder_reader=blob_folder_reader,
|
|
1671
|
+
)
|
|
1672
|
+
except (
|
|
1477
1673
|
errors.ParserError,
|
|
1478
1674
|
errors.DecoderError,
|
|
1479
|
-
NotImplementedError
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1675
|
+
NotImplementedError,
|
|
1676
|
+
) as err:
|
|
1677
|
+
print(
|
|
1678
|
+
(
|
|
1679
|
+
"Error parsing Indexeddb record: "
|
|
1680
|
+
f"{err} at offset {db_record.record.offset} in "
|
|
1681
|
+
f"{db_record.path}"
|
|
1682
|
+
),
|
|
1683
|
+
file=sys.stderr,
|
|
1684
|
+
)
|
|
1685
|
+
print(f"Traceback: {traceback.format_exc()}", file=sys.stderr)
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
class BlobFolderReader:
|
|
1689
|
+
"""A blob folder reader for Chrome/Chromium.
|
|
1690
|
+
|
|
1691
|
+
Attributes:
|
|
1692
|
+
folder_name (str): the source blob folder.
|
|
1693
|
+
"""
|
|
1694
|
+
|
|
1695
|
+
def __init__(self, folder_name: pathlib.Path):
|
|
1696
|
+
"""Initializes the BlobFolderReader.
|
|
1697
|
+
|
|
1698
|
+
Args:
|
|
1699
|
+
folder_name: the source blob folder.
|
|
1700
|
+
|
|
1701
|
+
Raises:
|
|
1702
|
+
ValueError: if folder_name is None or not a directory.
|
|
1703
|
+
"""
|
|
1704
|
+
if not folder_name or not folder_name.is_dir():
|
|
1705
|
+
raise ValueError(f"{folder_name} is None or not a directory")
|
|
1706
|
+
self.folder_name = folder_name.absolute()
|
|
1707
|
+
|
|
1708
|
+
def ReadBlob(self, database_id: int, blob_id: int) -> tuple[str, bytes]:
|
|
1709
|
+
"""Reads a blob from the blob folder.
|
|
1710
|
+
|
|
1711
|
+
Args:
|
|
1712
|
+
database_id: the database id of the blob to read.
|
|
1713
|
+
blob_id: the blob id to read.
|
|
1714
|
+
|
|
1715
|
+
Returns:
|
|
1716
|
+
A tuple of the blob path and contents.
|
|
1717
|
+
|
|
1718
|
+
Raises:
|
|
1719
|
+
FileNotFoundError: if the database directory or blob folder or blob not
|
|
1720
|
+
found.
|
|
1721
|
+
"""
|
|
1722
|
+
directory_path = self.folder_name / f"{database_id:x}"
|
|
1723
|
+
if not directory_path.exists():
|
|
1724
|
+
raise FileNotFoundError(f"Database directory not found: {directory_path}")
|
|
1725
|
+
|
|
1726
|
+
blob_folder = directory_path / f"{(blob_id & 0xff00) >> 8:02x}"
|
|
1727
|
+
if not blob_folder.exists():
|
|
1728
|
+
raise FileNotFoundError(f"Blob folder not found: {blob_folder}")
|
|
1729
|
+
|
|
1730
|
+
blob_path = blob_folder / f"{blob_id:x}"
|
|
1731
|
+
if not blob_path.exists():
|
|
1732
|
+
raise FileNotFoundError(f"Blob ({blob_id}) not found: {blob_path}")
|
|
1733
|
+
|
|
1734
|
+
with open(blob_path, "rb") as f:
|
|
1735
|
+
return str(blob_path), f.read()
|
|
1736
|
+
|
|
1737
|
+
def ReadBlobsFromExternalObjectEntries(
|
|
1738
|
+
self, database_id: int, entries: list[ExternalObjectEntry]
|
|
1739
|
+
) -> Generator[tuple[str, Optional[bytes]], None, None]:
|
|
1740
|
+
"""Reads blobs from the blob folder.
|
|
1741
|
+
|
|
1742
|
+
Args:
|
|
1743
|
+
database_id: the database id.
|
|
1744
|
+
entries: the external object entries.
|
|
1745
|
+
|
|
1746
|
+
Yields:
|
|
1747
|
+
A tuple of blob path and contents or if the blob is not found, an error
|
|
1748
|
+
message and None.
|
|
1749
|
+
"""
|
|
1750
|
+
for entry in entries:
|
|
1751
|
+
if (
|
|
1752
|
+
entry.object_type
|
|
1753
|
+
in (
|
|
1754
|
+
definitions.ExternalObjectType.BLOB,
|
|
1755
|
+
definitions.ExternalObjectType.FILE,
|
|
1756
|
+
)
|
|
1757
|
+
and entry.blob_number is not None
|
|
1758
|
+
):
|
|
1759
|
+
try:
|
|
1760
|
+
yield self.ReadBlob(database_id, entry.blob_number)
|
|
1761
|
+
except FileNotFoundError as err:
|
|
1762
|
+
error_message = (
|
|
1763
|
+
f"Blob not found for ExternalObjectEntry at offset {entry.offset}"
|
|
1764
|
+
f": {err}"
|
|
1765
|
+
)
|
|
1766
|
+
print(error_message, file=sys.stderr)
|
|
1767
|
+
yield error_message, None
|
|
1486
1768
|
|
|
1487
1769
|
|
|
1488
1770
|
class FolderReader:
|
|
@@ -1502,36 +1784,59 @@ class FolderReader:
|
|
|
1502
1784
|
ValueError: if folder_name is None or not a directory.
|
|
1503
1785
|
"""
|
|
1504
1786
|
if not folder_name or not folder_name.is_dir():
|
|
1505
|
-
raise ValueError(f
|
|
1506
|
-
self.folder_name = folder_name
|
|
1787
|
+
raise ValueError(f"{folder_name} is None or not a directory")
|
|
1788
|
+
self.folder_name = folder_name.absolute()
|
|
1789
|
+
|
|
1790
|
+
# Locate the correponding blob folder. The folder_name should be
|
|
1791
|
+
# <origin>.leveldb and the blob folder should be <origin>.blob
|
|
1792
|
+
if str(self.folder_name).endswith(".leveldb"):
|
|
1793
|
+
self.blob_folder_reader = BlobFolderReader(
|
|
1794
|
+
pathlib.Path(str(self.folder_name).replace(".leveldb", ".blob"))
|
|
1795
|
+
)
|
|
1796
|
+
else:
|
|
1797
|
+
self.blob_folder_reader = None # type: ignore[assignment]
|
|
1507
1798
|
|
|
1508
1799
|
def GetRecords(
|
|
1509
1800
|
self,
|
|
1510
1801
|
use_manifest: bool = False,
|
|
1511
|
-
use_sequence_number: bool = False
|
|
1512
|
-
|
|
1513
|
-
|
|
1802
|
+
use_sequence_number: bool = False,
|
|
1803
|
+
parse_value: bool = True,
|
|
1804
|
+
include_raw_data: bool = False,
|
|
1805
|
+
) -> Generator[ChromiumIndexedDBRecord, None, None]:
|
|
1806
|
+
"""Yields ChromiumIndexedDBRecord.
|
|
1514
1807
|
|
|
1515
1808
|
Args:
|
|
1516
1809
|
use_manifest: True to use the current manifest in the folder as a means to
|
|
1517
1810
|
find the active file set.
|
|
1518
1811
|
use_sequence_number: True to use the sequence number to determine the
|
|
1812
|
+
recovered field.
|
|
1813
|
+
parse_value: True to parse values.
|
|
1814
|
+
|
|
1519
1815
|
Yields:
|
|
1520
|
-
|
|
1816
|
+
ChromiumIndexedDBRecord.
|
|
1521
1817
|
"""
|
|
1522
1818
|
leveldb_folder_reader = record.FolderReader(self.folder_name)
|
|
1523
1819
|
for leveldb_record in leveldb_folder_reader.GetRecords(
|
|
1524
|
-
use_manifest=use_manifest,
|
|
1525
|
-
|
|
1820
|
+
use_manifest=use_manifest, use_sequence_number=use_sequence_number
|
|
1821
|
+
):
|
|
1526
1822
|
try:
|
|
1527
|
-
yield
|
|
1528
|
-
|
|
1823
|
+
yield ChromiumIndexedDBRecord.FromLevelDBRecord(
|
|
1824
|
+
leveldb_record,
|
|
1825
|
+
parse_value=parse_value,
|
|
1826
|
+
include_raw_data=include_raw_data,
|
|
1827
|
+
blob_folder_reader=self.blob_folder_reader,
|
|
1828
|
+
)
|
|
1829
|
+
except (
|
|
1529
1830
|
errors.ParserError,
|
|
1530
1831
|
errors.DecoderError,
|
|
1531
|
-
NotImplementedError
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1832
|
+
NotImplementedError,
|
|
1833
|
+
) as err:
|
|
1834
|
+
print(
|
|
1835
|
+
(
|
|
1836
|
+
"Error parsing Indexeddb record: "
|
|
1837
|
+
f"{err} at offset {leveldb_record.record.offset} in "
|
|
1838
|
+
f"{leveldb_record.path}"
|
|
1839
|
+
),
|
|
1840
|
+
file=sys.stderr,
|
|
1841
|
+
)
|
|
1842
|
+
print(f"Traceback: {traceback.format_exc()}", file=sys.stderr)
|