mirror of
https://gitee.com/openharmony/third_party_littlefs
synced 2024-11-27 09:01:27 +00:00
0ea2871e24
Not sure how this went unnoticed, I guess this is the first bug that needed in-depth inspection after the a last-minute argument cleanup in the debug scripts.
184 lines
6.2 KiB
Python
Executable File
184 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import struct
|
|
import sys
|
|
import json
|
|
import io
|
|
import itertools as it
|
|
from readmdir import Tag, MetadataPair
|
|
|
|
def main(args):
|
|
superblock = None
|
|
gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0'
|
|
dirs = []
|
|
mdirs = []
|
|
corrupted = []
|
|
cycle = False
|
|
with open(args.disk, 'rb') as f:
|
|
tail = (args.block1, args.block2)
|
|
hard = False
|
|
while True:
|
|
for m in it.chain((m for d in dirs for m in d), mdirs):
|
|
if set(m.blocks) == set(tail):
|
|
# cycle detected
|
|
cycle = m.blocks
|
|
if cycle:
|
|
break
|
|
|
|
# load mdir
|
|
data = []
|
|
blocks = {}
|
|
for block in tail:
|
|
f.seek(block * args.block_size)
|
|
data.append(f.read(args.block_size)
|
|
.ljust(args.block_size, b'\xff'))
|
|
blocks[id(data[-1])] = block
|
|
|
|
mdir = MetadataPair(data)
|
|
mdir.blocks = tuple(blocks[id(p.data)] for p in mdir.pair)
|
|
|
|
# fetch some key metadata as a we scan
|
|
try:
|
|
mdir.tail = mdir[Tag('tail', 0, 0)]
|
|
if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff':
|
|
mdir.tail = None
|
|
except KeyError:
|
|
mdir.tail = None
|
|
|
|
# have superblock?
|
|
try:
|
|
nsuperblock = mdir[
|
|
Tag(0x7ff, 0x3ff, 0), Tag('superblock', 0, 0)]
|
|
superblock = nsuperblock, mdir[Tag('inlinestruct', 0, 0)]
|
|
except KeyError:
|
|
pass
|
|
|
|
# have gstate?
|
|
try:
|
|
ngstate = mdir[Tag('movestate', 0, 0)]
|
|
gstate = bytes((a or 0) ^ (b or 0)
|
|
for a,b in it.zip_longest(gstate, ngstate.data))
|
|
except KeyError:
|
|
pass
|
|
|
|
# corrupted?
|
|
if not mdir:
|
|
corrupted.append(mdir)
|
|
|
|
# add to directories
|
|
mdirs.append(mdir)
|
|
if mdir.tail is None or not mdir.tail.is_('hardtail'):
|
|
dirs.append(mdirs)
|
|
mdirs = []
|
|
|
|
if mdir.tail is None:
|
|
break
|
|
|
|
tail = struct.unpack('<II', mdir.tail.data)
|
|
hard = mdir.tail.is_('hardtail')
|
|
|
|
# find paths
|
|
dirtable = {}
|
|
for dir in dirs:
|
|
dirtable[frozenset(dir[0].blocks)] = dir
|
|
|
|
pending = [("/", dirs[0])]
|
|
while pending:
|
|
path, dir = pending.pop(0)
|
|
for mdir in dir:
|
|
for tag in mdir.tags:
|
|
if tag.is_('dir'):
|
|
try:
|
|
npath = tag.data.decode('utf8')
|
|
dirstruct = mdir[Tag('dirstruct', tag.id, 0)]
|
|
nblocks = struct.unpack('<II', dirstruct.data)
|
|
nmdir = dirtable[frozenset(nblocks)]
|
|
pending.append(((path + '/' + npath), nmdir))
|
|
except KeyError:
|
|
pass
|
|
|
|
dir[0].path = path.replace('//', '/')
|
|
|
|
# print littlefs + version info
|
|
version = ('?', '?')
|
|
if superblock:
|
|
version = tuple(reversed(
|
|
struct.unpack('<HH', superblock[1].data[0:4].ljust(4, b'\xff'))))
|
|
print("%-47s%s" % ("littlefs v%s.%s" % version,
|
|
"data (truncated, if it fits)"
|
|
if not any([args.no_truncate, args.log, args.all]) else ""))
|
|
|
|
# print gstate
|
|
print("gstate 0x%s" % ''.join('%02x' % c for c in gstate))
|
|
tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0])
|
|
blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff'))
|
|
if tag.size or not tag.isvalid:
|
|
print(" orphans >=%d" % max(tag.size, 1))
|
|
if tag.type:
|
|
print(" move dir {%#x, %#x} id %d" % (
|
|
blocks[0], blocks[1], tag.id))
|
|
|
|
# print mdir info
|
|
for i, dir in enumerate(dirs):
|
|
print("dir %s" % (json.dumps(dir[0].path)
|
|
if hasattr(dir[0], 'path') else '(orphan)'))
|
|
|
|
for j, mdir in enumerate(dir):
|
|
print("mdir {%#x, %#x} rev %d (was %d)%s%s" % (
|
|
mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev,
|
|
' (corrupted!)' if not mdir else '',
|
|
' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data)
|
|
if mdir.tail else ''))
|
|
|
|
f = io.StringIO()
|
|
if args.log:
|
|
mdir.dump_log(f, truncate=not args.no_truncate)
|
|
elif args.all:
|
|
mdir.dump_all(f, truncate=not args.no_truncate)
|
|
else:
|
|
mdir.dump_tags(f, truncate=not args.no_truncate)
|
|
|
|
lines = list(filter(None, f.getvalue().split('\n')))
|
|
for k, line in enumerate(lines):
|
|
print("%s %s" % (
|
|
' ' if j == len(dir)-1 else
|
|
'v' if k == len(lines)-1 else
|
|
'|',
|
|
line))
|
|
|
|
errcode = 0
|
|
for mdir in corrupted:
|
|
errcode = errcode or 1
|
|
print("*** corrupted mdir {%#x, %#x}! ***" % (
|
|
mdir.blocks[0], mdir.blocks[1]))
|
|
|
|
if cycle:
|
|
errcode = errcode or 2
|
|
print("*** cycle detected {%#x, %#x}! ***" % (
|
|
cycle[0], cycle[1]))
|
|
|
|
return errcode
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
import sys
|
|
parser = argparse.ArgumentParser(
|
|
description="Dump semantic info about the metadata tree in littlefs")
|
|
parser.add_argument('disk',
|
|
help="File representing the block device.")
|
|
parser.add_argument('block_size', type=lambda x: int(x, 0),
|
|
help="Size of a block in bytes.")
|
|
parser.add_argument('block1', nargs='?', default=0,
|
|
type=lambda x: int(x, 0),
|
|
help="Optional first block address for finding the superblock.")
|
|
parser.add_argument('block2', nargs='?', default=1,
|
|
type=lambda x: int(x, 0),
|
|
help="Optional second block address for finding the superblock.")
|
|
parser.add_argument('-l', '--log', action='store_true',
|
|
help="Show tags in log.")
|
|
parser.add_argument('-a', '--all', action='store_true',
|
|
help="Show all tags in log, included tags in corrupted commits.")
|
|
parser.add_argument('-T', '--no-truncate', action='store_true',
|
|
help="Show the full contents of files/attrs/tags.")
|
|
sys.exit(main(parser.parse_args()))
|