119ea8026Sopenharmony_ci#!/usr/bin/env python3
219ea8026Sopenharmony_ci
319ea8026Sopenharmony_ciimport struct
419ea8026Sopenharmony_ciimport binascii
519ea8026Sopenharmony_ciimport sys
619ea8026Sopenharmony_ciimport itertools as it
719ea8026Sopenharmony_ci
819ea8026Sopenharmony_ciTAG_TYPES = {
919ea8026Sopenharmony_ci    'splice':       (0x700, 0x400),
1019ea8026Sopenharmony_ci    'create':       (0x7ff, 0x401),
1119ea8026Sopenharmony_ci    'delete':       (0x7ff, 0x4ff),
1219ea8026Sopenharmony_ci    'name':         (0x700, 0x000),
1319ea8026Sopenharmony_ci    'reg':          (0x7ff, 0x001),
1419ea8026Sopenharmony_ci    'dir':          (0x7ff, 0x002),
1519ea8026Sopenharmony_ci    'superblock':   (0x7ff, 0x0ff),
1619ea8026Sopenharmony_ci    'struct':       (0x700, 0x200),
1719ea8026Sopenharmony_ci    'dirstruct':    (0x7ff, 0x200),
1819ea8026Sopenharmony_ci    'ctzstruct':    (0x7ff, 0x202),
1919ea8026Sopenharmony_ci    'inlinestruct': (0x7ff, 0x201),
2019ea8026Sopenharmony_ci    'userattr':     (0x700, 0x300),
2119ea8026Sopenharmony_ci    'tail':         (0x700, 0x600),
2219ea8026Sopenharmony_ci    'softtail':     (0x7ff, 0x600),
2319ea8026Sopenharmony_ci    'hardtail':     (0x7ff, 0x601),
2419ea8026Sopenharmony_ci    'gstate':       (0x700, 0x700),
2519ea8026Sopenharmony_ci    'movestate':    (0x7ff, 0x7ff),
2619ea8026Sopenharmony_ci    'crc':          (0x700, 0x500),
2719ea8026Sopenharmony_ci    'ccrc':         (0x780, 0x500),
2819ea8026Sopenharmony_ci    'fcrc':         (0x7ff, 0x5ff),
2919ea8026Sopenharmony_ci}
3019ea8026Sopenharmony_ci
3119ea8026Sopenharmony_ciclass Tag:
3219ea8026Sopenharmony_ci    def __init__(self, *args):
3319ea8026Sopenharmony_ci        if len(args) == 1:
3419ea8026Sopenharmony_ci            self.tag = args[0]
3519ea8026Sopenharmony_ci        elif len(args) == 3:
3619ea8026Sopenharmony_ci            if isinstance(args[0], str):
3719ea8026Sopenharmony_ci                type = TAG_TYPES[args[0]][1]
3819ea8026Sopenharmony_ci            else:
3919ea8026Sopenharmony_ci                type = args[0]
4019ea8026Sopenharmony_ci
4119ea8026Sopenharmony_ci            if isinstance(args[1], str):
4219ea8026Sopenharmony_ci                id = int(args[1], 0) if args[1] not in 'x.' else 0x3ff
4319ea8026Sopenharmony_ci            else:
4419ea8026Sopenharmony_ci                id = args[1]
4519ea8026Sopenharmony_ci
4619ea8026Sopenharmony_ci            if isinstance(args[2], str):
4719ea8026Sopenharmony_ci                size = int(args[2], str) if args[2] not in 'x.' else 0x3ff
4819ea8026Sopenharmony_ci            else:
4919ea8026Sopenharmony_ci                size = args[2]
5019ea8026Sopenharmony_ci
5119ea8026Sopenharmony_ci            self.tag = (type << 20) | (id << 10) | size
5219ea8026Sopenharmony_ci        else:
5319ea8026Sopenharmony_ci            assert False
5419ea8026Sopenharmony_ci
5519ea8026Sopenharmony_ci    @property
5619ea8026Sopenharmony_ci    def isvalid(self):
5719ea8026Sopenharmony_ci        return not bool(self.tag & 0x80000000)
5819ea8026Sopenharmony_ci
5919ea8026Sopenharmony_ci    @property
6019ea8026Sopenharmony_ci    def isattr(self):
6119ea8026Sopenharmony_ci        return not bool(self.tag & 0x40000000)
6219ea8026Sopenharmony_ci
6319ea8026Sopenharmony_ci    @property
6419ea8026Sopenharmony_ci    def iscompactable(self):
6519ea8026Sopenharmony_ci        return bool(self.tag & 0x20000000)
6619ea8026Sopenharmony_ci
6719ea8026Sopenharmony_ci    @property
6819ea8026Sopenharmony_ci    def isunique(self):
6919ea8026Sopenharmony_ci        return not bool(self.tag & 0x10000000)
7019ea8026Sopenharmony_ci
7119ea8026Sopenharmony_ci    @property
7219ea8026Sopenharmony_ci    def type(self):
7319ea8026Sopenharmony_ci        return (self.tag & 0x7ff00000) >> 20
7419ea8026Sopenharmony_ci
7519ea8026Sopenharmony_ci    @property
7619ea8026Sopenharmony_ci    def type1(self):
7719ea8026Sopenharmony_ci        return (self.tag & 0x70000000) >> 20
7819ea8026Sopenharmony_ci
7919ea8026Sopenharmony_ci    @property
8019ea8026Sopenharmony_ci    def type3(self):
8119ea8026Sopenharmony_ci        return (self.tag & 0x7ff00000) >> 20
8219ea8026Sopenharmony_ci
8319ea8026Sopenharmony_ci    @property
8419ea8026Sopenharmony_ci    def id(self):
8519ea8026Sopenharmony_ci        return (self.tag & 0x000ffc00) >> 10
8619ea8026Sopenharmony_ci
8719ea8026Sopenharmony_ci    @property
8819ea8026Sopenharmony_ci    def size(self):
8919ea8026Sopenharmony_ci        return (self.tag & 0x000003ff) >> 0
9019ea8026Sopenharmony_ci
9119ea8026Sopenharmony_ci    @property
9219ea8026Sopenharmony_ci    def dsize(self):
9319ea8026Sopenharmony_ci        return 4 + (self.size if self.size != 0x3ff else 0)
9419ea8026Sopenharmony_ci
9519ea8026Sopenharmony_ci    @property
9619ea8026Sopenharmony_ci    def chunk(self):
9719ea8026Sopenharmony_ci        return self.type & 0xff
9819ea8026Sopenharmony_ci
9919ea8026Sopenharmony_ci    @property
10019ea8026Sopenharmony_ci    def schunk(self):
10119ea8026Sopenharmony_ci        return struct.unpack('b', struct.pack('B', self.chunk))[0]
10219ea8026Sopenharmony_ci
10319ea8026Sopenharmony_ci    def is_(self, type):
10419ea8026Sopenharmony_ci        try:
10519ea8026Sopenharmony_ci            if ' ' in type:
10619ea8026Sopenharmony_ci                type1, type3 = type.split()
10719ea8026Sopenharmony_ci                return (self.is_(type1) and
10819ea8026Sopenharmony_ci                    (self.type & ~TAG_TYPES[type1][0]) == int(type3, 0))
10919ea8026Sopenharmony_ci
11019ea8026Sopenharmony_ci            return self.type == int(type, 0)
11119ea8026Sopenharmony_ci
11219ea8026Sopenharmony_ci        except (ValueError, KeyError):
11319ea8026Sopenharmony_ci            return (self.type & TAG_TYPES[type][0]) == TAG_TYPES[type][1]
11419ea8026Sopenharmony_ci
11519ea8026Sopenharmony_ci    def mkmask(self):
11619ea8026Sopenharmony_ci        return Tag(
11719ea8026Sopenharmony_ci            0x700 if self.isunique else 0x7ff,
11819ea8026Sopenharmony_ci            0x3ff if self.isattr else 0,
11919ea8026Sopenharmony_ci            0)
12019ea8026Sopenharmony_ci
12119ea8026Sopenharmony_ci    def chid(self, nid):
12219ea8026Sopenharmony_ci        ntag = Tag(self.type, nid, self.size)
12319ea8026Sopenharmony_ci        if hasattr(self, 'off'):    ntag.off    = self.off
12419ea8026Sopenharmony_ci        if hasattr(self, 'data'):   ntag.data   = self.data
12519ea8026Sopenharmony_ci        if hasattr(self, 'ccrc'):   ntag.crc    = self.crc
12619ea8026Sopenharmony_ci        if hasattr(self, 'erased'): ntag.erased = self.erased
12719ea8026Sopenharmony_ci        return ntag
12819ea8026Sopenharmony_ci
12919ea8026Sopenharmony_ci    def typerepr(self):
13019ea8026Sopenharmony_ci        if (self.is_('ccrc')
13119ea8026Sopenharmony_ci                and getattr(self, 'ccrc', 0xffffffff) != 0xffffffff):
13219ea8026Sopenharmony_ci            crc_status = ' (bad)'
13319ea8026Sopenharmony_ci        elif self.is_('fcrc') and getattr(self, 'erased', False):
13419ea8026Sopenharmony_ci            crc_status = ' (era)'
13519ea8026Sopenharmony_ci        else:
13619ea8026Sopenharmony_ci            crc_status = ''
13719ea8026Sopenharmony_ci
13819ea8026Sopenharmony_ci        reverse_types = {v: k for k, v in TAG_TYPES.items()}
13919ea8026Sopenharmony_ci        for prefix in range(12):
14019ea8026Sopenharmony_ci            mask = 0x7ff & ~((1 << prefix)-1)
14119ea8026Sopenharmony_ci            if (mask, self.type & mask) in reverse_types:
14219ea8026Sopenharmony_ci                type = reverse_types[mask, self.type & mask]
14319ea8026Sopenharmony_ci                if prefix > 0:
14419ea8026Sopenharmony_ci                    return '%s %#x%s' % (
14519ea8026Sopenharmony_ci                        type, self.type & ((1 << prefix)-1), crc_status)
14619ea8026Sopenharmony_ci                else:
14719ea8026Sopenharmony_ci                    return '%s%s' % (type, crc_status)
14819ea8026Sopenharmony_ci        else:
14919ea8026Sopenharmony_ci            return '%02x%s' % (self.type, crc_status)
15019ea8026Sopenharmony_ci
15119ea8026Sopenharmony_ci    def idrepr(self):
15219ea8026Sopenharmony_ci        return repr(self.id) if self.id != 0x3ff else '.'
15319ea8026Sopenharmony_ci
15419ea8026Sopenharmony_ci    def sizerepr(self):
15519ea8026Sopenharmony_ci        return repr(self.size) if self.size != 0x3ff else 'x'
15619ea8026Sopenharmony_ci
15719ea8026Sopenharmony_ci    def __repr__(self):
15819ea8026Sopenharmony_ci        return 'Tag(%r, %d, %d)' % (self.typerepr(), self.id, self.size)
15919ea8026Sopenharmony_ci
16019ea8026Sopenharmony_ci    def __lt__(self, other):
16119ea8026Sopenharmony_ci        return (self.id, self.type) < (other.id, other.type)
16219ea8026Sopenharmony_ci
16319ea8026Sopenharmony_ci    def __bool__(self):
16419ea8026Sopenharmony_ci        return self.isvalid
16519ea8026Sopenharmony_ci
16619ea8026Sopenharmony_ci    def __int__(self):
16719ea8026Sopenharmony_ci        return self.tag
16819ea8026Sopenharmony_ci
16919ea8026Sopenharmony_ci    def __index__(self):
17019ea8026Sopenharmony_ci        return self.tag
17119ea8026Sopenharmony_ci
17219ea8026Sopenharmony_ciclass MetadataPair:
17319ea8026Sopenharmony_ci    def __init__(self, blocks):
17419ea8026Sopenharmony_ci        if len(blocks) > 1:
17519ea8026Sopenharmony_ci            self.pair = [MetadataPair([block]) for block in blocks]
17619ea8026Sopenharmony_ci            self.pair = sorted(self.pair, reverse=True)
17719ea8026Sopenharmony_ci
17819ea8026Sopenharmony_ci            self.data = self.pair[0].data
17919ea8026Sopenharmony_ci            self.rev  = self.pair[0].rev
18019ea8026Sopenharmony_ci            self.tags = self.pair[0].tags
18119ea8026Sopenharmony_ci            self.ids  = self.pair[0].ids
18219ea8026Sopenharmony_ci            self.log  = self.pair[0].log
18319ea8026Sopenharmony_ci            self.all_ = self.pair[0].all_
18419ea8026Sopenharmony_ci            return
18519ea8026Sopenharmony_ci
18619ea8026Sopenharmony_ci        self.pair = [self]
18719ea8026Sopenharmony_ci        self.data = blocks[0]
18819ea8026Sopenharmony_ci        block = self.data
18919ea8026Sopenharmony_ci
19019ea8026Sopenharmony_ci        self.rev, = struct.unpack('<I', block[0:4])
19119ea8026Sopenharmony_ci        crc = binascii.crc32(block[0:4])
19219ea8026Sopenharmony_ci        fcrctag = None
19319ea8026Sopenharmony_ci        fcrcdata = None
19419ea8026Sopenharmony_ci
19519ea8026Sopenharmony_ci        # parse tags
19619ea8026Sopenharmony_ci        corrupt = False
19719ea8026Sopenharmony_ci        tag = Tag(0xffffffff)
19819ea8026Sopenharmony_ci        off = 4
19919ea8026Sopenharmony_ci        self.log = []
20019ea8026Sopenharmony_ci        self.all_ = []
20119ea8026Sopenharmony_ci        while len(block) - off >= 4:
20219ea8026Sopenharmony_ci            ntag, = struct.unpack('>I', block[off:off+4])
20319ea8026Sopenharmony_ci
20419ea8026Sopenharmony_ci            tag = Tag((int(tag) ^ ntag) & 0x7fffffff)
20519ea8026Sopenharmony_ci            tag.off = off + 4
20619ea8026Sopenharmony_ci            tag.data = block[off+4:off+tag.dsize]
20719ea8026Sopenharmony_ci            if tag.is_('ccrc'):
20819ea8026Sopenharmony_ci                crc = binascii.crc32(block[off:off+2*4], crc)
20919ea8026Sopenharmony_ci            else:
21019ea8026Sopenharmony_ci                crc = binascii.crc32(block[off:off+tag.dsize], crc)
21119ea8026Sopenharmony_ci            tag.crc = crc
21219ea8026Sopenharmony_ci            off += tag.dsize
21319ea8026Sopenharmony_ci
21419ea8026Sopenharmony_ci            self.all_.append(tag)
21519ea8026Sopenharmony_ci
21619ea8026Sopenharmony_ci            if tag.is_('fcrc') and len(tag.data) == 8:
21719ea8026Sopenharmony_ci                fcrctag = tag
21819ea8026Sopenharmony_ci                fcrcdata = struct.unpack('<II', tag.data)
21919ea8026Sopenharmony_ci            elif tag.is_('ccrc'):
22019ea8026Sopenharmony_ci                # is valid commit?
22119ea8026Sopenharmony_ci                if crc != 0xffffffff:
22219ea8026Sopenharmony_ci                    corrupt = True
22319ea8026Sopenharmony_ci                if not corrupt:
22419ea8026Sopenharmony_ci                    self.log = self.all_.copy()
22519ea8026Sopenharmony_ci                    # end of commit?
22619ea8026Sopenharmony_ci                    if fcrcdata:
22719ea8026Sopenharmony_ci                        fcrcsize, fcrc = fcrcdata
22819ea8026Sopenharmony_ci                        fcrc_ = 0xffffffff ^ binascii.crc32(
22919ea8026Sopenharmony_ci                            block[off:off+fcrcsize])
23019ea8026Sopenharmony_ci                        if fcrc_ == fcrc:
23119ea8026Sopenharmony_ci                            fcrctag.erased = True
23219ea8026Sopenharmony_ci                            corrupt = True
23319ea8026Sopenharmony_ci
23419ea8026Sopenharmony_ci                # reset tag parsing
23519ea8026Sopenharmony_ci                crc = 0
23619ea8026Sopenharmony_ci                tag = Tag(int(tag) ^ ((tag.type & 1) << 31))
23719ea8026Sopenharmony_ci                fcrctag = None
23819ea8026Sopenharmony_ci                fcrcdata = None
23919ea8026Sopenharmony_ci
24019ea8026Sopenharmony_ci        # find active ids
24119ea8026Sopenharmony_ci        self.ids = list(it.takewhile(
24219ea8026Sopenharmony_ci            lambda id: Tag('name', id, 0) in self,
24319ea8026Sopenharmony_ci            it.count()))
24419ea8026Sopenharmony_ci
24519ea8026Sopenharmony_ci        # find most recent tags
24619ea8026Sopenharmony_ci        self.tags = []
24719ea8026Sopenharmony_ci        for tag in self.log:
24819ea8026Sopenharmony_ci            if tag.is_('crc') or tag.is_('splice'):
24919ea8026Sopenharmony_ci                continue
25019ea8026Sopenharmony_ci            elif tag.id == 0x3ff:
25119ea8026Sopenharmony_ci                if tag in self and self[tag] is tag:
25219ea8026Sopenharmony_ci                    self.tags.append(tag)
25319ea8026Sopenharmony_ci            else:
25419ea8026Sopenharmony_ci                # id could have change, I know this is messy and slow
25519ea8026Sopenharmony_ci                # but it works
25619ea8026Sopenharmony_ci                for id in self.ids:
25719ea8026Sopenharmony_ci                    ntag = tag.chid(id)
25819ea8026Sopenharmony_ci                    if ntag in self and self[ntag] is tag:
25919ea8026Sopenharmony_ci                        self.tags.append(ntag)
26019ea8026Sopenharmony_ci
26119ea8026Sopenharmony_ci        self.tags = sorted(self.tags)
26219ea8026Sopenharmony_ci
26319ea8026Sopenharmony_ci    def __bool__(self):
26419ea8026Sopenharmony_ci        return bool(self.log)
26519ea8026Sopenharmony_ci
26619ea8026Sopenharmony_ci    def __lt__(self, other):
26719ea8026Sopenharmony_ci        # corrupt blocks don't count
26819ea8026Sopenharmony_ci        if not self or not other:
26919ea8026Sopenharmony_ci            return bool(other)
27019ea8026Sopenharmony_ci
27119ea8026Sopenharmony_ci        # use sequence arithmetic to avoid overflow
27219ea8026Sopenharmony_ci        return not ((other.rev - self.rev) & 0x80000000)
27319ea8026Sopenharmony_ci
27419ea8026Sopenharmony_ci    def __contains__(self, args):
27519ea8026Sopenharmony_ci        try:
27619ea8026Sopenharmony_ci            self[args]
27719ea8026Sopenharmony_ci            return True
27819ea8026Sopenharmony_ci        except KeyError:
27919ea8026Sopenharmony_ci            return False
28019ea8026Sopenharmony_ci
28119ea8026Sopenharmony_ci    def __getitem__(self, args):
28219ea8026Sopenharmony_ci        if isinstance(args, tuple):
28319ea8026Sopenharmony_ci            gmask, gtag = args
28419ea8026Sopenharmony_ci        else:
28519ea8026Sopenharmony_ci            gmask, gtag = args.mkmask(), args
28619ea8026Sopenharmony_ci
28719ea8026Sopenharmony_ci        gdiff = 0
28819ea8026Sopenharmony_ci        for tag in reversed(self.log):
28919ea8026Sopenharmony_ci            if (gmask.id != 0 and tag.is_('splice') and
29019ea8026Sopenharmony_ci                    tag.id <= gtag.id - gdiff):
29119ea8026Sopenharmony_ci                if tag.is_('create') and tag.id == gtag.id - gdiff:
29219ea8026Sopenharmony_ci                    # creation point
29319ea8026Sopenharmony_ci                    break
29419ea8026Sopenharmony_ci
29519ea8026Sopenharmony_ci                gdiff += tag.schunk
29619ea8026Sopenharmony_ci
29719ea8026Sopenharmony_ci            if ((int(gmask) & int(tag)) ==
29819ea8026Sopenharmony_ci                    (int(gmask) & int(gtag.chid(gtag.id - gdiff)))):
29919ea8026Sopenharmony_ci                if tag.size == 0x3ff:
30019ea8026Sopenharmony_ci                    # deleted
30119ea8026Sopenharmony_ci                    break
30219ea8026Sopenharmony_ci
30319ea8026Sopenharmony_ci                return tag
30419ea8026Sopenharmony_ci
30519ea8026Sopenharmony_ci        raise KeyError(gmask, gtag)
30619ea8026Sopenharmony_ci
30719ea8026Sopenharmony_ci    def _dump_tags(self, tags, f=sys.stdout, truncate=True):
30819ea8026Sopenharmony_ci        f.write("%-8s  %-8s  %-13s %4s %4s" % (
30919ea8026Sopenharmony_ci            'off', 'tag', 'type', 'id', 'len'))
31019ea8026Sopenharmony_ci        if truncate:
31119ea8026Sopenharmony_ci            f.write('  data (truncated)')
31219ea8026Sopenharmony_ci        f.write('\n')
31319ea8026Sopenharmony_ci
31419ea8026Sopenharmony_ci        for tag in tags:
31519ea8026Sopenharmony_ci            f.write("%08x: %08x  %-14s %3s %4s" % (
31619ea8026Sopenharmony_ci                tag.off, tag,
31719ea8026Sopenharmony_ci                tag.typerepr(), tag.idrepr(), tag.sizerepr()))
31819ea8026Sopenharmony_ci            if truncate:
31919ea8026Sopenharmony_ci                f.write("  %-23s  %-8s\n" % (
32019ea8026Sopenharmony_ci                    ' '.join('%02x' % c for c in tag.data[:8]),
32119ea8026Sopenharmony_ci                    ''.join(c if c >= ' ' and c <= '~' else '.'
32219ea8026Sopenharmony_ci                        for c in map(chr, tag.data[:8]))))
32319ea8026Sopenharmony_ci            else:
32419ea8026Sopenharmony_ci                f.write("\n")
32519ea8026Sopenharmony_ci                for i in range(0, len(tag.data), 16):
32619ea8026Sopenharmony_ci                    f.write("  %08x: %-47s  %-16s\n" % (
32719ea8026Sopenharmony_ci                        tag.off+i,
32819ea8026Sopenharmony_ci                        ' '.join('%02x' % c for c in tag.data[i:i+16]),
32919ea8026Sopenharmony_ci                        ''.join(c if c >= ' ' and c <= '~' else '.'
33019ea8026Sopenharmony_ci                            for c in map(chr, tag.data[i:i+16]))))
33119ea8026Sopenharmony_ci
33219ea8026Sopenharmony_ci    def dump_tags(self, f=sys.stdout, truncate=True):
33319ea8026Sopenharmony_ci        self._dump_tags(self.tags, f=f, truncate=truncate)
33419ea8026Sopenharmony_ci
33519ea8026Sopenharmony_ci    def dump_log(self, f=sys.stdout, truncate=True):
33619ea8026Sopenharmony_ci        self._dump_tags(self.log, f=f, truncate=truncate)
33719ea8026Sopenharmony_ci
33819ea8026Sopenharmony_ci    def dump_all(self, f=sys.stdout, truncate=True):
33919ea8026Sopenharmony_ci        self._dump_tags(self.all_, f=f, truncate=truncate)
34019ea8026Sopenharmony_ci
34119ea8026Sopenharmony_cidef main(args):
34219ea8026Sopenharmony_ci    blocks = []
34319ea8026Sopenharmony_ci    with open(args.disk, 'rb') as f:
34419ea8026Sopenharmony_ci        for block in [args.block1, args.block2]:
34519ea8026Sopenharmony_ci            if block is None:
34619ea8026Sopenharmony_ci                continue
34719ea8026Sopenharmony_ci            f.seek(block * args.block_size)
34819ea8026Sopenharmony_ci            blocks.append(f.read(args.block_size)
34919ea8026Sopenharmony_ci                .ljust(args.block_size, b'\xff'))
35019ea8026Sopenharmony_ci
35119ea8026Sopenharmony_ci    # find most recent pair
35219ea8026Sopenharmony_ci    mdir = MetadataPair(blocks)
35319ea8026Sopenharmony_ci
35419ea8026Sopenharmony_ci    try:
35519ea8026Sopenharmony_ci        mdir.tail = mdir[Tag('tail', 0, 0)]
35619ea8026Sopenharmony_ci        if mdir.tail.size != 8 or mdir.tail.data == 8*b'\xff':
35719ea8026Sopenharmony_ci            mdir.tail = None
35819ea8026Sopenharmony_ci    except KeyError:
35919ea8026Sopenharmony_ci        mdir.tail = None
36019ea8026Sopenharmony_ci
36119ea8026Sopenharmony_ci    print("mdir {%s} rev %d%s%s%s" % (
36219ea8026Sopenharmony_ci        ', '.join('%#x' % b
36319ea8026Sopenharmony_ci            for b in [args.block1, args.block2]
36419ea8026Sopenharmony_ci            if b is not None),
36519ea8026Sopenharmony_ci        mdir.rev,
36619ea8026Sopenharmony_ci        ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:])
36719ea8026Sopenharmony_ci        if len(mdir.pair) > 1 else '',
36819ea8026Sopenharmony_ci        ' (corrupted!)' if not mdir else '',
36919ea8026Sopenharmony_ci        ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data)
37019ea8026Sopenharmony_ci        if mdir.tail else ''))
37119ea8026Sopenharmony_ci    if args.all:
37219ea8026Sopenharmony_ci        mdir.dump_all(truncate=not args.no_truncate)
37319ea8026Sopenharmony_ci    elif args.log:
37419ea8026Sopenharmony_ci        mdir.dump_log(truncate=not args.no_truncate)
37519ea8026Sopenharmony_ci    else:
37619ea8026Sopenharmony_ci        mdir.dump_tags(truncate=not args.no_truncate)
37719ea8026Sopenharmony_ci
37819ea8026Sopenharmony_ci    return 0 if mdir else 1
37919ea8026Sopenharmony_ci
38019ea8026Sopenharmony_ciif __name__ == "__main__":
38119ea8026Sopenharmony_ci    import argparse
38219ea8026Sopenharmony_ci    import sys
38319ea8026Sopenharmony_ci    parser = argparse.ArgumentParser(
38419ea8026Sopenharmony_ci        description="Dump useful info about metadata pairs in littlefs.")
38519ea8026Sopenharmony_ci    parser.add_argument('disk',
38619ea8026Sopenharmony_ci        help="File representing the block device.")
38719ea8026Sopenharmony_ci    parser.add_argument('block_size', type=lambda x: int(x, 0),
38819ea8026Sopenharmony_ci        help="Size of a block in bytes.")
38919ea8026Sopenharmony_ci    parser.add_argument('block1', type=lambda x: int(x, 0),
39019ea8026Sopenharmony_ci        help="First block address for finding the metadata pair.")
39119ea8026Sopenharmony_ci    parser.add_argument('block2', nargs='?', type=lambda x: int(x, 0),
39219ea8026Sopenharmony_ci        help="Second block address for finding the metadata pair.")
39319ea8026Sopenharmony_ci    parser.add_argument('-l', '--log', action='store_true',
39419ea8026Sopenharmony_ci        help="Show tags in log.")
39519ea8026Sopenharmony_ci    parser.add_argument('-a', '--all', action='store_true',
39619ea8026Sopenharmony_ci        help="Show all tags in log, included tags in corrupted commits.")
39719ea8026Sopenharmony_ci    parser.add_argument('-T', '--no-truncate', action='store_true',
39819ea8026Sopenharmony_ci        help="Don't truncate large amounts of data.")
39919ea8026Sopenharmony_ci    sys.exit(main(parser.parse_args()))
400