"""Read-only Btrfs inspection for this single-device USB. Never writes a filesystem.""" import struct import uuid import zlib from compression import zstd def u16(b, o): return struct.unpack_from('>1) ^ (0x82f63b78 if _c&1 else 0) _crc_table.append(_c) def crc32c(data): c=0xffffffff for byte in data: c=_crc_table[(c ^ byte)&255] ^ (c>>8) return c ^ 0xffffffff class Btrfs: def __init__(self, stream, start): self.stream=stream; self.start=start; self.chunks={}; self.nodes={} self.sb=self.physical(65536,4096) assert self.sb[64:72]==b'_BHRfS_M', 'Not Btrfs' assert u16(self.sb,196)==0, 'Expected CRC32C filesystem' assert crc32c(self.sb[32:])==u32(self.sb,0), 'Bad Btrfs superblock checksum' assert u64(self.sb,136)==1, 'Expected one Btrfs device' self.uuid=str(uuid.UUID(bytes=self.sb[32:48])) self.nodesize=u32(self.sb,148) assert self.nodesize in (4096,8192,16384,32768,65536) p=0x32b; end=p+u32(self.sb,160) while p=0 and length>=0 self.stream.seek(self.start+offset); data=self.stream.read(length) assert len(data)==length, 'Short Btrfs read' return data def chunk(self,logical,data): count=u16(data,44); flags=u64(data,24) # Only SINGLE or DUP profiles; no striped/address-interleaved layouts. assert flags & ~0x27 == 0, f'Unsupported chunk profile {flags:x}' assert 1<=count<=2 self.chunks[logical]=(u64(data,0),u64(data,56),flags) def logical(self,offset,length): out=bytearray() while length: for start,(size,physical,_) in self.chunks.items(): if start<=offset=0 and level<8 if level: assert 101+count*33<=self.nodesize for i in range(count): yield from self.items(u64(b,101+i*33+17)) else: assert 101+count*25<=self.nodesize for i in range(count): o=101+i*25; p=101+u32(b,o+17); n=u32(b,o+21) assert 101<=p<=p+n<=self.nodesize yield key(b,o), b[p:p+n] def directory(self,inode): result={} for k,data in self.index.get(inode,[]): if k[1]!=84: continue p=0 while p=0 and offset+count<=len(blob) result[k[2]:k[2]+count]=blob[offset:offset+count] return bytes(result) if __name__=='__main__': import argparse import sys parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('image') parser.add_argument('--offset', type=int, required=True, help='Filesystem start in bytes') parser.add_argument('paths', nargs='*') args=parser.parse_args() if sys.flags.optimize: parser.error('Run without -O; filesystem verification requires assertions') if args.offset<0: parser.error('--offset must be nonnegative') with open(args.image,'rb') as stream: fs=Btrfs(stream,args.offset) print('UUID',fs.uuid,'generation',u64(fs.sb,72),'root',fs.fsroot) print('Root entries', sorted(fs.directory(256))) for path in args.paths: print(path) print(fs.read(path).decode('utf-8','replace'))