From 82f4fd3c0ff240b31cadbcda2268b7ef742450dd Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 31 Mar 2025 18:09:49 -0500 Subject: [PATCH] scripts: Dropped list/tuple distinction in Rbyd.fetch Also tweaked how we fetch shrubs, adding Rbyd.fetchshrub and Btree.fetchshrub instead of overloading the bd argument. Oh, and also added --trunk to dbgmtree.py and dbglfs.py. Actually _using_ --trunk isn't advised, since it will probably just result in a corrupted filesystem, but these scripts are for accessing things that aren't normally allowed anyways. The reason for dropping the list/tuple distinction is because it was a big ugly hack, unpythonic, and likely to catch users (and myself) by surprise. Now, Rbyd.fetch and friends always require separate block/trunk arguments, and the exercise of deciding which trunk to use is left up to the caller. --- scripts/dbgblock.py | 10 +-- scripts/dbgbmap.py | 10 +-- scripts/dbgbtree.py | 161 ++++++++++++++++++------------------ scripts/dbgcat.py | 10 +-- scripts/dbglfs.py | 197 +++++++++++++++++++++----------------------- scripts/dbgmtree.py | 175 +++++++++++++++++++-------------------- scripts/dbgrbyd.py | 138 ++++++++++++++++--------------- scripts/tracebd.py | 10 +-- 8 files changed, 350 insertions(+), 361 deletions(-) diff --git a/scripts/dbgblock.py b/scripts/dbgblock.py index 45b02261..c291e88d 100755 --- a/scripts/dbgblock.py +++ b/scripts/dbgblock.py @@ -32,10 +32,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -66,7 +66,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def xxd(data, width=16): for i in range(0, len(data), width): diff --git a/scripts/dbgbmap.py b/scripts/dbgbmap.py index 9320ef4b..0898dd0a 100755 --- a/scripts/dbgbmap.py +++ b/scripts/dbgbmap.py @@ -97,10 +97,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -131,7 +131,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def crc32c(data, crc=0): crc ^= 0xffffffff diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 7390841a..a27862a1 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -80,10 +80,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -114,7 +114,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def crc32c(data, crc=0): crc ^= 0xffffffff @@ -456,9 +456,9 @@ class Rattr: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.data = data @@ -532,9 +532,9 @@ class Ralt: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.jump = jump @@ -587,9 +587,9 @@ class Rbyd: gcksumdelta=None, corrupt=False): if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.trunk = trunk self.weight = weight self.rev = rev @@ -632,51 +632,7 @@ class Rbyd: return hash((frozenset(self.blocks), self.trunk)) @classmethod - def fetch(cls, bd, blocks, trunk=None): - # multiple blocks? unfortunately this must be a list - if isinstance(blocks, list): - # fetch all blocks - rbyds = [cls.fetch(bd, block, trunk) for block in blocks] - # determine most recent revision - i = 0 - for i_, rbyd in enumerate(rbyds): - # compare with sequence arithmetic - if rbyd and ( - not rbyds[i] - or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) - or (rbyd.rev == rbyds[i].rev - and rbyd.trunk > rbyds[i].trunk)): - i = i_ - # keep track of the other blocks - rbyd = rbyds[i] - rbyd.blocks += tuple( - rbyds[(i+1+j) % len(rbyds)].block - for j in range(len(rbyds)-1)) - # and patch the gcksumdelta if we have one - if rbyd.gcksumdelta is not None: - rbyd.gcksumdelta.blocks = rbyd.blocks - return rbyd - - block = blocks - - # blocks may also encode trunks - block, trunk = ( - block[0] if isinstance(block, tuple) - else block, - trunk if trunk is not None - else block[1] if isinstance(block, tuple) - else None) - - # bd can be either a bd reference or a preread block - # - # preread blocks can be useful for avoiding race conditions - # with cksums and shrubs - if isinstance(bd, Bd): - # seek/read the block - data = bd.readblock(block) - else: - data = bd - + def _fetch(cls, data, block, trunk=None): # fetch the rbyd rev = fromle32(data[0:4]) cksum = 0 @@ -773,6 +729,39 @@ class Rbyd: gcksumdelta=gcksumdelta, corrupt=not trunk_) + @classmethod + def fetch(cls, bd, blocks, trunk=None): + # multiple blocks? + if not isinstance(blocks, int): + # fetch all blocks + rbyds = [cls.fetch(bd, block, trunk) for block in blocks] + # determine most recent revision + i = 0 + for i_, rbyd in enumerate(rbyds): + # compare with sequence arithmetic + if rbyd and ( + not rbyds[i] + or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) + or (rbyd.rev == rbyds[i].rev + and rbyd.trunk > rbyds[i].trunk)): + i = i_ + # keep track of the other blocks + rbyd = rbyds[i] + rbyd.blocks += tuple( + rbyds[(i+1+j) % len(rbyds)].block + for j in range(len(rbyds)-1)) + # and patch the gcksumdelta if we have one + if rbyd.gcksumdelta is not None: + rbyd.gcksumdelta.blocks = rbyd.blocks + return rbyd + + # seek/read the block + block = blocks + data = bd.readblock(block) + + # fetch the rbyd + return cls._fetch(data, block, trunk) + @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): # try to fetch the rbyd normally @@ -789,6 +778,15 @@ class Rbyd: return rbyd + @classmethod + def fetchshrub(cls, rbyd, trunk): + # steal the original rbyd's data + # + # this helps avoid race conditions with cksums and stuff + shrub = cls._fetch(rbyd.data, rbyd.block, trunk) + shrub.blocks = rbyd.blocks + return shrub + def lookupnext(self, rid, tag=None, *, path=False): if not self or rid >= self.weight: @@ -1157,32 +1155,21 @@ class Btree: @classmethod def fetch(cls, bd, blocks, trunk=None): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetch does most of the work here - rbyd = Rbyd.fetch(data, blocks, trunk) + rbyd = Rbyd.fetch(bd, blocks, trunk) return cls(bd, rbyd) @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetchck does most of the work here - rbyd = Rbyd.fetchck(data, blocks, trunk, weight, cksum) + rbyd = Rbyd.fetchck(bd, blocks, trunk, weight, cksum) return cls(bd, rbyd) + @classmethod + def fetchshrub(cls, bd, rbyd, trunk): + shrub = Rbyd.fetchshrub(rbyd, trunk) + return cls(bd, shrub) + def lookupleaf(self, bid, *, path=None, depth=None): @@ -1589,9 +1576,9 @@ class Btree: def main(disk, roots=None, *, + trunk=None, block_size=None, block_count=None, - trunk=None, color='auto', **args): # figure out what color should be @@ -1609,9 +1596,19 @@ def main(disk, roots=None, *, block_count = block_count_ # flatten roots, default to block 0 - if not roots: - roots = [(0,)] - roots = [block for roots_ in roots for block in roots_] + roots = list(it.chain.from_iterable(roots)) if roots else [0] + + # roots may also encode trunks + roots, trunk = ( + [block[0] if isinstance(block, tuple) + else block + for block in roots], + trunk if trunk is not None + else ft.reduce( + lambda x, y: y, + (block[1] for block in roots + if isinstance(block, tuple)), + None)) # we seek around a bunch, so just keep the disk open with open(disk, 'rb') as f: @@ -1739,6 +1736,10 @@ if __name__ == "__main__": nargs='*', type=rbydaddr, help="Block address of the roots of the tree.") + parser.add_argument( + '--trunk', + type=lambda x: int(x, 0), + help="Use this offset as the trunk of the tree.") parser.add_argument( '-b', '--block-size', type=bdgeom, @@ -1747,10 +1748,6 @@ if __name__ == "__main__": '--block-count', type=lambda x: int(x, 0), help="Block count in blocks.") - parser.add_argument( - '--trunk', - type=lambda x: int(x, 0), - help="Use this offset as the trunk of the tree.") parser.add_argument( '--color', choices=['never', 'always', 'auto'], diff --git a/scripts/dbgcat.py b/scripts/dbgcat.py index e34ba1cb..ec642635 100755 --- a/scripts/dbgcat.py +++ b/scripts/dbgcat.py @@ -32,10 +32,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -66,7 +66,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def xxd(data, width=16): for i in range(0, len(data), width): diff --git a/scripts/dbglfs.py b/scripts/dbglfs.py index edef2e74..e9b0768c 100755 --- a/scripts/dbglfs.py +++ b/scripts/dbglfs.py @@ -81,10 +81,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -115,7 +115,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def crc32c(data, crc=0): crc ^= 0xffffffff @@ -505,9 +505,9 @@ class Rattr: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.data = data @@ -581,9 +581,9 @@ class Ralt: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.jump = jump @@ -636,9 +636,9 @@ class Rbyd: gcksumdelta=None, corrupt=False): if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.trunk = trunk self.weight = weight self.rev = rev @@ -681,51 +681,7 @@ class Rbyd: return hash((frozenset(self.blocks), self.trunk)) @classmethod - def fetch(cls, bd, blocks, trunk=None): - # multiple blocks? unfortunately this must be a list - if isinstance(blocks, list): - # fetch all blocks - rbyds = [cls.fetch(bd, block, trunk) for block in blocks] - # determine most recent revision - i = 0 - for i_, rbyd in enumerate(rbyds): - # compare with sequence arithmetic - if rbyd and ( - not rbyds[i] - or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) - or (rbyd.rev == rbyds[i].rev - and rbyd.trunk > rbyds[i].trunk)): - i = i_ - # keep track of the other blocks - rbyd = rbyds[i] - rbyd.blocks += tuple( - rbyds[(i+1+j) % len(rbyds)].block - for j in range(len(rbyds)-1)) - # and patch the gcksumdelta if we have one - if rbyd.gcksumdelta is not None: - rbyd.gcksumdelta.blocks = rbyd.blocks - return rbyd - - block = blocks - - # blocks may also encode trunks - block, trunk = ( - block[0] if isinstance(block, tuple) - else block, - trunk if trunk is not None - else block[1] if isinstance(block, tuple) - else None) - - # bd can be either a bd reference or a preread block - # - # preread blocks can be useful for avoiding race conditions - # with cksums and shrubs - if isinstance(bd, Bd): - # seek/read the block - data = bd.readblock(block) - else: - data = bd - + def _fetch(cls, data, block, trunk=None): # fetch the rbyd rev = fromle32(data[0:4]) cksum = 0 @@ -822,6 +778,39 @@ class Rbyd: gcksumdelta=gcksumdelta, corrupt=not trunk_) + @classmethod + def fetch(cls, bd, blocks, trunk=None): + # multiple blocks? + if not isinstance(blocks, int): + # fetch all blocks + rbyds = [cls.fetch(bd, block, trunk) for block in blocks] + # determine most recent revision + i = 0 + for i_, rbyd in enumerate(rbyds): + # compare with sequence arithmetic + if rbyd and ( + not rbyds[i] + or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) + or (rbyd.rev == rbyds[i].rev + and rbyd.trunk > rbyds[i].trunk)): + i = i_ + # keep track of the other blocks + rbyd = rbyds[i] + rbyd.blocks += tuple( + rbyds[(i+1+j) % len(rbyds)].block + for j in range(len(rbyds)-1)) + # and patch the gcksumdelta if we have one + if rbyd.gcksumdelta is not None: + rbyd.gcksumdelta.blocks = rbyd.blocks + return rbyd + + # seek/read the block + block = blocks + data = bd.readblock(block) + + # fetch the rbyd + return cls._fetch(data, block, trunk) + @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): # try to fetch the rbyd normally @@ -838,6 +827,15 @@ class Rbyd: return rbyd + @classmethod + def fetchshrub(cls, rbyd, trunk): + # steal the original rbyd's data + # + # this helps avoid race conditions with cksums and stuff + shrub = cls._fetch(rbyd.data, rbyd.block, trunk) + shrub.blocks = rbyd.blocks + return shrub + def lookupnext(self, rid, tag=None, *, path=False): if not self or rid >= self.weight: @@ -1206,32 +1204,21 @@ class Btree: @classmethod def fetch(cls, bd, blocks, trunk=None): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetch does most of the work here - rbyd = Rbyd.fetch(data, blocks, trunk) + rbyd = Rbyd.fetch(bd, blocks, trunk) return cls(bd, rbyd) @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetchck does most of the work here - rbyd = Rbyd.fetchck(data, blocks, trunk, weight, cksum) + rbyd = Rbyd.fetchck(bd, blocks, trunk, weight, cksum) return cls(bd, rbyd) + @classmethod + def fetchshrub(cls, bd, rbyd, trunk): + shrub = Rbyd.fetchshrub(rbyd, trunk) + return cls(bd, shrub) + def lookupleaf(self, bid, *, path=None, depth=None): @@ -1804,13 +1791,9 @@ class Mdir: return hash(frozenset(self.blocks)) @classmethod - def fetch(cls, bd, mid, blocks): - rbyd = Rbyd.fetch(bd, blocks) - # this affects mbits - if isinstance(bd, Bd): - return cls(mid, rbyd, mbits=Mtree.mbits_(bd)) - else: - return cls(mid, rbyd) + def fetch(cls, bd, mid, blocks, trunk=None): + rbyd = Rbyd.fetch(bd, blocks, trunk) + return cls(mid, rbyd, mbits=Mtree.mbits_(bd)) def lookup_(self, mid, tag=None, mask=None, *, path=False): @@ -1999,11 +1982,8 @@ class Mtree: return hash(self.mrootanchor) @classmethod - def fetch(cls, bd, blocks=None, *, + def fetch(cls, bd, blocks=None, trunk=None, *, depth=None): - # we need a real bd reference here - assert isinstance(bd, Bd) - # default to blocks 0x{0,1} if blocks is None: blocks = [0, 1] @@ -2012,7 +1992,7 @@ class Mtree: mbits = Mtree.mbits_(bd) # fetch the mrootanchor - mrootanchor = Mdir.fetch(bd, -1, blocks) + mrootanchor = Mdir.fetch(bd, -1, blocks, trunk) # follow the mroot chain to try to find the active mroot mroot = mrootanchor @@ -2736,15 +2716,10 @@ class Bptr: @classmethod def fetch(cls, bd, rattr, block, off, size, cksize, cksum): - # bd can be either a bd reference or a preread block - if isinstance(bd, Bd): - # seek/read cksize bytes from the block, the actual data - # should always be a subset of this - bd.seek(block) - ckdata = bd.read(cksize) - else: - # truncate to cksize - ckdata = bd[:cksize] + # seek/read cksize bytes from the block, the actual data should + # always be a subset of cksize + bd.seek(block) + ckdata = bd.read(cksize) return cls(rattr, block, off, size, cksize, cksum, ckdata) @@ -3303,10 +3278,10 @@ class Lfs: return hash(self.mrootanchor) @classmethod - def fetch(cls, bd, blocks=None, *, + def fetch(cls, bd, blocks=None, trunk=None, *, depth=None): # Mtree does most of the work here - mtree = Mtree.fetch(bd, blocks, + mtree = Mtree.fetch(bd, blocks, trunk, depth=depth) return cls(bd, mtree) @@ -3597,8 +3572,7 @@ class Lfs: if (self.struct is not None and (self.struct.tag & ~0x3) == TAG_BSHRUB): weight, trunk = fromshrub(self.struct.data) - self.bshrub = Btree.fetch( - (lfs.bd, mdir.data), self.mdir.blocks, trunk) + self.bshrub = Btree.fetchshrub(lfs.bd, mdir.rbyd, trunk) elif (self.struct is not None and (self.struct.tag & ~0x3) == TAG_BTREE): weight, block, trunk, cksum = frombtree(self.struct.data) @@ -4393,6 +4367,7 @@ def dbg_files(lfs, paths, def main(disk, mroots=None, paths=None, *, + trunk=None, block_size=None, block_count=None, color='auto', @@ -4426,9 +4401,19 @@ def main(disk, mroots=None, paths=None, *, block_count = block_count_ # flatten mroots, default to 0x{0,1} - if not mroots: - mroots = [(0,1)] - mroots = [block for mroots_ in mroots for block in mroots_] + mroots = list(it.chain.from_iterable(mroots)) if mroots else [0, 1] + + # mroots may also encode trunks + mroots, trunk = ( + [block[0] if isinstance(block, tuple) + else block + for block in mroots], + trunk if trunk is not None + else ft.reduce( + lambda x, y: y, + (block[1] for block in mroots + if isinstance(block, tuple)), + None)) # we seek around a bunch, so just keep the disk open with open(disk, 'rb') as f: @@ -4439,7 +4424,7 @@ def main(disk, mroots=None, paths=None, *, # fetch the filesystem bd = Bd(f, block_size, block_count) - lfs = Lfs.fetch(bd, mroots) + lfs = Lfs.fetch(bd, mroots, trunk) # print some information about the filesystem print('littlefs%s v%s.%s %sx%s %s w%s.%s, rev %08x, cksum %08x%s' % ( @@ -4503,7 +4488,7 @@ if __name__ == "__main__": def __call__(self, parser, namespace, values, option): for value in values: # mroot? - if isinstance(value, list): + if not isinstance(value, str): if getattr(namespace, 'mroots', None) is None: namespace.mroots = [] namespace.mroots.append(value) @@ -4525,6 +4510,10 @@ if __name__ == "__main__": action=AppendMrootOrPath, help="Paths to show, must start with a leading slash. Defaults " "to the root directory.") + parser.add_argument( + '--trunk', + type=lambda x: int(x, 0), + help="Use this offset as the trunk of the mroots.") parser.add_argument( '-b', '--block-size', type=bdgeom, diff --git a/scripts/dbgmtree.py b/scripts/dbgmtree.py index b48591df..0a9f68d2 100755 --- a/scripts/dbgmtree.py +++ b/scripts/dbgmtree.py @@ -80,10 +80,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -114,7 +114,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def crc32c(data, crc=0): crc ^= 0xffffffff @@ -471,9 +471,9 @@ class Rattr: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.data = data @@ -547,9 +547,9 @@ class Ralt: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.jump = jump @@ -602,9 +602,9 @@ class Rbyd: gcksumdelta=None, corrupt=False): if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.trunk = trunk self.weight = weight self.rev = rev @@ -647,51 +647,7 @@ class Rbyd: return hash((frozenset(self.blocks), self.trunk)) @classmethod - def fetch(cls, bd, blocks, trunk=None): - # multiple blocks? unfortunately this must be a list - if isinstance(blocks, list): - # fetch all blocks - rbyds = [cls.fetch(bd, block, trunk) for block in blocks] - # determine most recent revision - i = 0 - for i_, rbyd in enumerate(rbyds): - # compare with sequence arithmetic - if rbyd and ( - not rbyds[i] - or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) - or (rbyd.rev == rbyds[i].rev - and rbyd.trunk > rbyds[i].trunk)): - i = i_ - # keep track of the other blocks - rbyd = rbyds[i] - rbyd.blocks += tuple( - rbyds[(i+1+j) % len(rbyds)].block - for j in range(len(rbyds)-1)) - # and patch the gcksumdelta if we have one - if rbyd.gcksumdelta is not None: - rbyd.gcksumdelta.blocks = rbyd.blocks - return rbyd - - block = blocks - - # blocks may also encode trunks - block, trunk = ( - block[0] if isinstance(block, tuple) - else block, - trunk if trunk is not None - else block[1] if isinstance(block, tuple) - else None) - - # bd can be either a bd reference or a preread block - # - # preread blocks can be useful for avoiding race conditions - # with cksums and shrubs - if isinstance(bd, Bd): - # seek/read the block - data = bd.readblock(block) - else: - data = bd - + def _fetch(cls, data, block, trunk=None): # fetch the rbyd rev = fromle32(data[0:4]) cksum = 0 @@ -788,6 +744,39 @@ class Rbyd: gcksumdelta=gcksumdelta, corrupt=not trunk_) + @classmethod + def fetch(cls, bd, blocks, trunk=None): + # multiple blocks? + if not isinstance(blocks, int): + # fetch all blocks + rbyds = [cls.fetch(bd, block, trunk) for block in blocks] + # determine most recent revision + i = 0 + for i_, rbyd in enumerate(rbyds): + # compare with sequence arithmetic + if rbyd and ( + not rbyds[i] + or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) + or (rbyd.rev == rbyds[i].rev + and rbyd.trunk > rbyds[i].trunk)): + i = i_ + # keep track of the other blocks + rbyd = rbyds[i] + rbyd.blocks += tuple( + rbyds[(i+1+j) % len(rbyds)].block + for j in range(len(rbyds)-1)) + # and patch the gcksumdelta if we have one + if rbyd.gcksumdelta is not None: + rbyd.gcksumdelta.blocks = rbyd.blocks + return rbyd + + # seek/read the block + block = blocks + data = bd.readblock(block) + + # fetch the rbyd + return cls._fetch(data, block, trunk) + @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): # try to fetch the rbyd normally @@ -804,6 +793,15 @@ class Rbyd: return rbyd + @classmethod + def fetchshrub(cls, rbyd, trunk): + # steal the original rbyd's data + # + # this helps avoid race conditions with cksums and stuff + shrub = cls._fetch(rbyd.data, rbyd.block, trunk) + shrub.blocks = rbyd.blocks + return shrub + def lookupnext(self, rid, tag=None, *, path=False): if not self or rid >= self.weight: @@ -1172,32 +1170,21 @@ class Btree: @classmethod def fetch(cls, bd, blocks, trunk=None): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetch does most of the work here - rbyd = Rbyd.fetch(data, blocks, trunk) + rbyd = Rbyd.fetch(bd, blocks, trunk) return cls(bd, rbyd) @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): - # bd can either be a bd reference or a tuple of bd + data to - # avoid rereads, but we need a real bd reference somehow - if isinstance(bd, tuple): - bd, data = bd - else: - bd, data = bd, bd - assert isinstance(bd, Bd) - # rbyd fetchck does most of the work here - rbyd = Rbyd.fetchck(data, blocks, trunk, weight, cksum) + rbyd = Rbyd.fetchck(bd, blocks, trunk, weight, cksum) return cls(bd, rbyd) + @classmethod + def fetchshrub(cls, bd, rbyd, trunk): + shrub = Rbyd.fetchshrub(rbyd, trunk) + return cls(bd, shrub) + def lookupleaf(self, bid, *, path=None, depth=None): @@ -1770,13 +1757,9 @@ class Mdir: return hash(frozenset(self.blocks)) @classmethod - def fetch(cls, bd, mid, blocks): - rbyd = Rbyd.fetch(bd, blocks) - # this affects mbits - if isinstance(bd, Bd): - return cls(mid, rbyd, mbits=Mtree.mbits_(bd)) - else: - return cls(mid, rbyd) + def fetch(cls, bd, mid, blocks, trunk=None): + rbyd = Rbyd.fetch(bd, blocks, trunk) + return cls(mid, rbyd, mbits=Mtree.mbits_(bd)) def lookup_(self, mid, tag=None, mask=None, *, path=False): @@ -1965,11 +1948,8 @@ class Mtree: return hash(self.mrootanchor) @classmethod - def fetch(cls, bd, blocks=None, *, + def fetch(cls, bd, blocks=None, trunk=None, *, depth=None): - # we need a real bd reference here - assert isinstance(bd, Bd) - # default to blocks 0x{0,1} if blocks is None: blocks = [0, 1] @@ -1978,7 +1958,7 @@ class Mtree: mbits = Mtree.mbits_(bd) # fetch the mrootanchor - mrootanchor = Mdir.fetch(bd, -1, blocks) + mrootanchor = Mdir.fetch(bd, -1, blocks, trunk) # follow the mroot chain to try to find the active mroot mroot = mrootanchor @@ -2648,6 +2628,7 @@ class Mtree: def main(disk, mroots=None, *, + trunk=None, block_size=None, block_count=None, color='auto', @@ -2667,9 +2648,19 @@ def main(disk, mroots=None, *, block_count = block_count_ # flatten mroots, default to 0x{0,1} - if not mroots: - mroots = [(0,1)] - mroots = [block for mroots_ in mroots for block in mroots_] + mroots = list(it.chain.from_iterable(mroots)) if mroots else [0, 1] + + # mroots may also encode trunks + mroots, trunk = ( + [block[0] if isinstance(block, tuple) + else block + for block in mroots], + trunk if trunk is not None + else ft.reduce( + lambda x, y: y, + (block[1] for block in mroots + if isinstance(block, tuple)), + None)) # we seek around a bunch, so just keep the disk open with open(disk, 'rb') as f: @@ -2680,7 +2671,7 @@ def main(disk, mroots=None, *, # fetch the mtree bd = Bd(f, block_size, block_count) - mtree = Mtree.fetch(bd, mroots, + mtree = Mtree.fetch(bd, mroots, trunk, depth=args.get('depth')) # print some information about the mtree @@ -2899,6 +2890,10 @@ if __name__ == "__main__": nargs='*', type=rbydaddr, help="Block address of the mroots. Defaults to 0x{0,1}.") + parser.add_argument( + '--trunk', + type=lambda x: int(x, 0), + help="Use this offset as the trunk of the mroots.") parser.add_argument( '-b', '--block-size', type=bdgeom, diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index 204307fd..8dad8c5e 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -90,10 +90,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -124,7 +124,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) def crc32c(data, crc=0): crc ^= 0xffffffff @@ -439,9 +439,9 @@ class Rattr: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.data = data @@ -515,9 +515,9 @@ class Ralt: self.tag = tag self.weight = weight if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.toff = toff self.tdata = tdata self.jump = jump @@ -570,9 +570,9 @@ class Rbyd: gcksumdelta=None, corrupt=False): if isinstance(blocks, int): - self.blocks = [blocks] + self.blocks = (blocks,) else: - self.blocks = list(blocks) + self.blocks = blocks self.trunk = trunk self.weight = weight self.rev = rev @@ -615,51 +615,7 @@ class Rbyd: return hash((frozenset(self.blocks), self.trunk)) @classmethod - def fetch(cls, bd, blocks, trunk=None): - # multiple blocks? unfortunately this must be a list - if isinstance(blocks, list): - # fetch all blocks - rbyds = [cls.fetch(bd, block, trunk) for block in blocks] - # determine most recent revision - i = 0 - for i_, rbyd in enumerate(rbyds): - # compare with sequence arithmetic - if rbyd and ( - not rbyds[i] - or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) - or (rbyd.rev == rbyds[i].rev - and rbyd.trunk > rbyds[i].trunk)): - i = i_ - # keep track of the other blocks - rbyd = rbyds[i] - rbyd.blocks += tuple( - rbyds[(i+1+j) % len(rbyds)].block - for j in range(len(rbyds)-1)) - # and patch the gcksumdelta if we have one - if rbyd.gcksumdelta is not None: - rbyd.gcksumdelta.blocks = rbyd.blocks - return rbyd - - block = blocks - - # blocks may also encode trunks - block, trunk = ( - block[0] if isinstance(block, tuple) - else block, - trunk if trunk is not None - else block[1] if isinstance(block, tuple) - else None) - - # bd can be either a bd reference or a preread block - # - # preread blocks can be useful for avoiding race conditions - # with cksums and shrubs - if isinstance(bd, Bd): - # seek/read the block - data = bd.readblock(block) - else: - data = bd - + def _fetch(cls, data, block, trunk=None): # fetch the rbyd rev = fromle32(data[0:4]) cksum = 0 @@ -756,6 +712,39 @@ class Rbyd: gcksumdelta=gcksumdelta, corrupt=not trunk_) + @classmethod + def fetch(cls, bd, blocks, trunk=None): + # multiple blocks? + if not isinstance(blocks, int): + # fetch all blocks + rbyds = [cls.fetch(bd, block, trunk) for block in blocks] + # determine most recent revision + i = 0 + for i_, rbyd in enumerate(rbyds): + # compare with sequence arithmetic + if rbyd and ( + not rbyds[i] + or not ((rbyd.rev - rbyds[i].rev) & 0x80000000) + or (rbyd.rev == rbyds[i].rev + and rbyd.trunk > rbyds[i].trunk)): + i = i_ + # keep track of the other blocks + rbyd = rbyds[i] + rbyd.blocks += tuple( + rbyds[(i+1+j) % len(rbyds)].block + for j in range(len(rbyds)-1)) + # and patch the gcksumdelta if we have one + if rbyd.gcksumdelta is not None: + rbyd.gcksumdelta.blocks = rbyd.blocks + return rbyd + + # seek/read the block + block = blocks + data = bd.readblock(block) + + # fetch the rbyd + return cls._fetch(data, block, trunk) + @classmethod def fetchck(cls, bd, blocks, trunk, weight, cksum): # try to fetch the rbyd normally @@ -772,6 +761,15 @@ class Rbyd: return rbyd + @classmethod + def fetchshrub(cls, rbyd, trunk): + # steal the original rbyd's data + # + # this helps avoid race conditions with cksums and stuff + shrub = cls._fetch(rbyd.data, rbyd.block, trunk) + shrub.blocks = rbyd.blocks + return shrub + def lookupnext(self, rid, tag=None, *, path=False): if not self or rid >= self.weight: @@ -1516,9 +1514,9 @@ def dbg_tree(rbyd, *, def main(disk, blocks=None, *, + trunk=None, block_size=None, block_count=None, - trunk=None, color='auto', **args): # figure out what color should be @@ -1536,9 +1534,19 @@ def main(disk, blocks=None, *, block_count = block_count_ # flatten blocks, default to block 0 - if not blocks: - blocks = [(0,)] - blocks = [block for blocks_ in blocks for block in blocks_] + blocks = list(it.chain.from_iterable(blocks)) if blocks else [0] + + # blocks may also encode trunks + blocks, trunk = ( + [block[0] if isinstance(block, tuple) + else block + for block in blocks], + trunk if trunk is not None + else ft.reduce( + lambda x, y: y, + (block[1] for block in blocks + if isinstance(block, tuple)), + None)) with open(disk, 'rb') as f: # if block_size is omitted, assume the block device is one big block @@ -1548,7 +1556,7 @@ def main(disk, blocks=None, *, # fetch the rbyd bd = Bd(f, block_size, block_count) - rbyd = Rbyd.fetch(bd, blocks) + rbyd = Rbyd.fetch(bd, blocks, trunk) # print some information about the rbyd print('rbyd %s w%d, rev %08x, size %d, cksum %08x' % ( @@ -1587,6 +1595,10 @@ if __name__ == "__main__": nargs='*', type=rbydaddr, help="Block address of metadata blocks.") + parser.add_argument( + '--trunk', + type=lambda x: int(x, 0), + help="Use this offset as the trunk of the tree.") parser.add_argument( '-b', '--block-size', type=bdgeom, @@ -1595,10 +1607,6 @@ if __name__ == "__main__": '--block-count', type=lambda x: int(x, 0), help="Block count in blocks.") - parser.add_argument( - '--trunk', - type=lambda x: int(x, 0), - help="Use this offset as the trunk of the tree.") parser.add_argument( '--color', choices=['never', 'always', 'auto'], diff --git a/scripts/tracebd.py b/scripts/tracebd.py index dd69b616..67d7a329 100755 --- a/scripts/tracebd.py +++ b/scripts/tracebd.py @@ -80,10 +80,10 @@ def bdgeom(s): return int(s, b) # parse some rbyd addr encodings -# 0xa -> [0xa] -# 0xa.c -> [(0xa, 0xc)] -# 0x{a,b} -> [0xa, 0xb] -# 0x{a,b}.c -> [(0xa, 0xc), (0xb, 0xc)] +# 0xa -> (0xa,) +# 0xa.c -> ((0xa, 0xc),) +# 0x{a,b} -> (0xa, 0xb) +# 0x{a,b}.c -> ((0xa, 0xc), (0xb, 0xc)) def rbydaddr(s): s = s.strip() b = 10 @@ -114,7 +114,7 @@ def rbydaddr(s): else: addr.append(int(s, b)) - return addr + return tuple(addr) class RingIO: