scripts: dbgblock.py/dbgcat.py: Fixed some range corner cases

Mainly fixing unbounded ranges, which required a bit of tweaking of when
we flatten block arguments.

This adopts the trick of using slice as the representation of, well,
slices in arguments instead of tuples. This avoids type confusion with
rbydaddr also returning tuples (of tuples!).
This commit is contained in:
Christopher Haster
2025-03-16 01:21:54 -05:00
parent cb0119681d
commit c028735741
2 changed files with 85 additions and 56 deletions
+33 -32
View File
@@ -97,17 +97,6 @@ def main(disk, block=None, *,
if block_count is None: if block_count is None:
block_count = block_count_ block_count = block_count_
# flatten block, default to block 0
if not block:
block = (0,)
if len(block) > 1:
print("error: more than one block address?",
file=sys.stderr)
sys.exit(-1)
block = block[0]
with open(disk, 'rb') as f: with open(disk, 'rb') as f:
# if block_size is omitted, assume the block device is one big block # if block_size is omitted, assume the block device is one big block
if block_size is None: if block_size is None:
@@ -116,37 +105,45 @@ def main(disk, block=None, *,
# block may also encode an offset # block may also encode an offset
block, off, size = ( block, off, size = (
block[0] if isinstance(block, tuple) else block, block[0] if isinstance(block, tuple)
off[0] if isinstance(off, tuple) else block,
off.start if isinstance(off, slice)
else off if off is not None else off if off is not None
else size[0] if isinstance(size, tuple) and len(size) > 1 else size.start if isinstance(size, slice)
else block[1] if isinstance(block, tuple) else block[1] if isinstance(block, tuple)
else None, else None,
size[1] - size[0] if isinstance(size, tuple) and len(size) > 1 (size.stop - (size.start or 0)
else size[0] if isinstance(size, tuple) if size.stop is not None
else None) if isinstance(size, slice)
else size if size is not None else size if size is not None
else off[1] - off[0] else ((off.stop - (off.start or 0))
if isinstance(off, tuple) and len(off) > 1 if off.stop is not None
else block_size) else None) if isinstance(off, slice)
else None)
# bound to block_size
block_ = block if block is not None else 0
off_ = off if off is not None else 0
size_ = size if size is not None else block_size - off_
# read the block # read the block
f.seek((block * block_size) + (off or 0)) f.seek((block_ * block_size) + off_)
data = f.read(size) data = f.read(size_)
# calculate checksum # calculate checksum
cksum = crc32c(data) cksum = crc32c(data)
# print the header # print the header
print('block %s, size %d, cksum %08x' % ( print('block %s, size %d, cksum %08x' % (
'0x%x.%x' % (block, off) '0x%x.%x' % (block_, off_)
if off is not None if off is not None
else '0x%x' % block, else '0x%x' % block_,
size, size_,
cksum)) cksum))
# render the hex view # render the hex view
for o, line in enumerate(xxd(data)): for o, line in enumerate(xxd(data)):
print('%08x: %s' % ((off or 0) + 16*o, line)) print('%08x: %s' % (off_ + 16*o, line))
if __name__ == "__main__": if __name__ == "__main__":
@@ -161,7 +158,7 @@ if __name__ == "__main__":
parser.add_argument( parser.add_argument(
'block', 'block',
nargs='?', nargs='?',
type=rbydaddr, type=lambda x: (lambda x: x)(*rbydaddr(x)),
help="Block address.") help="Block address.")
parser.add_argument( parser.add_argument(
'-b', '--block-size', '-b', '--block-size',
@@ -173,15 +170,19 @@ if __name__ == "__main__":
help="Block count in blocks.") help="Block count in blocks.")
parser.add_argument( parser.add_argument(
'--off', '--off',
type=lambda x: tuple( type=lambda x: (
int(x, 0) if x.strip() else None slice(*(int(x, 0) if x.strip() else None
for x in x.split(',')), for x in x.split(',', 1)))
if ',' in x
else int(x, 0)),
help="Show a specific offset, may be a range.") help="Show a specific offset, may be a range.")
parser.add_argument( parser.add_argument(
'-n', '--size', '-n', '--size',
type=lambda x: tuple( type=lambda x: (
int(x, 0) if x.strip() else None slice(*(int(x, 0) if x.strip() else None
for x in x.split(',')), for x in x.split(',', 1)))
if ',' in x
else int(x, 0)),
help="Show this many bytes, may be a range.") help="Show this many bytes, may be a range.")
sys.exit(main(**{k: v sys.exit(main(**{k: v
for k, v in vars(parser.parse_intermixed_args()).items() for k, v in vars(parser.parse_intermixed_args()).items()
+52 -24
View File
@@ -4,6 +4,7 @@
if __name__ == "__main__": if __name__ == "__main__":
__import__('sys').path.pop(0) __import__('sys').path.pop(0)
import itertools as it
import os import os
@@ -96,40 +97,59 @@ def main(disk, blocks=None, *,
if block_count is None: if block_count is None:
block_count = block_count_ block_count = block_count_
# flatten block, default to block 0
if not blocks:
blocks = [(0,)]
blocks = [block for blocks_ in blocks for block in blocks_]
with open(disk, 'rb') as f: with open(disk, 'rb') as f:
# if block_size is omitted, assume the block device is one big block # if block_size is omitted, assume the block device is one big block
if block_size is None: if block_size is None:
f.seek(0, os.SEEK_END) f.seek(0, os.SEEK_END)
block_size = f.tell() block_size = f.tell()
block_count = 1
# if block_count is omitted, derive the block_count from our file size
if block_count is None:
f.seek(0, os.SEEK_END)
block_count = f.tell() // block_size
# flatten blocks, default to block 0
blocks = (list(it.chain.from_iterable(
range(block.start or 0, block.stop or block_count)
if isinstance(block, slice)
else block
for block in blocks))
if blocks
else [0])
# blocks may also encode offsets # blocks may also encode offsets
blocks, offs, size = ( blocks, offs, size = (
[block[0] if isinstance(block, tuple) else block [block[0] if isinstance(block, tuple)
else block
for block in blocks], for block in blocks],
[off[0] if isinstance(off, tuple) [off.start if isinstance(off, slice)
else off if off is not None else off if off is not None
else size[0] else size.start if isinstance(size, slice)
if isinstance(size, tuple) and len(size) > 1
else block[1] if isinstance(block, tuple) else block[1] if isinstance(block, tuple)
else None else None
for block in blocks], for block in blocks],
size[1] - size[0] if isinstance(size, tuple) and len(size) > 1 (size.stop - (size.start or 0)
else size[0] if isinstance(size, tuple) if size.stop is not None
else None) if isinstance(size, slice)
else size if size is not None else size if size is not None
else off[1] - off[0] else ((off.stop - (off.start or 0))
if isinstance(off, tuple) and len(off) > 1 if off.stop is not None
else block_size) else None) if isinstance(off, slice)
else None)
# cat the blocks # cat the blocks
for block, off in zip(blocks, offs): for block, off in zip(blocks, offs):
f.seek((block * block_size) + (off or 0)) # bound to block_size
data = f.read(size) block_ = block if block is not None else 0
off_ = off if off is not None else 0
size_ = size if size is not None else block_size - off_
# cat the block
f.seek((block_ * block_size) + off_)
data = f.read(size_)
sys.stdout.buffer.write(data) sys.stdout.buffer.write(data)
sys.stdout.flush() sys.stdout.flush()
@@ -145,8 +165,12 @@ if __name__ == "__main__":
parser.add_argument( parser.add_argument(
'blocks', 'blocks',
nargs='*', nargs='*',
type=rbydaddr, type=lambda x: (
help="Block address.") slice(*(int(x, 0) if x.strip() else None
for x in x.split(',', 1)))
if ',' in x and '{' not in x
else rbydaddr(x)),
help="Block addresses, may be a range.")
parser.add_argument( parser.add_argument(
'-b', '--block-size', '-b', '--block-size',
type=bdgeom, type=bdgeom,
@@ -157,15 +181,19 @@ if __name__ == "__main__":
help="Block count in blocks.") help="Block count in blocks.")
parser.add_argument( parser.add_argument(
'--off', '--off',
type=lambda x: tuple( type=lambda x: (
int(x, 0) if x.strip() else None slice(*(int(x, 0) if x.strip() else None
for x in x.split(',')), for x in x.split(',', 1)))
if ',' in x
else int(x, 0)),
help="Show a specific offset, may be a range.") help="Show a specific offset, may be a range.")
parser.add_argument( parser.add_argument(
'-n', '--size', '-n', '--size',
type=lambda x: tuple( type=lambda x: (
int(x, 0) if x.strip() else None slice(*(int(x, 0) if x.strip() else None
for x in x.split(',')), for x in x.split(',', 1)))
if ',' in x
else int(x, 0)),
help="Show this many bytes, may be a range.") help="Show this many bytes, may be a range.")
sys.exit(main(**{k: v sys.exit(main(**{k: v
for k, v in vars(parser.parse_intermixed_args()).items() for k, v in vars(parser.parse_intermixed_args()).items()