Skip to content

Instantly share code, notes, and snippets.

@lab313ru
Last active April 16, 2024 10:15
Show Gist options
  • Save lab313ru/9be731c4800ba2ae09982d273c1efa07 to your computer and use it in GitHub Desktop.
Save lab313ru/9be731c4800ba2ae09982d273c1efa07 to your computer and use it in GitHub Desktop.
Dump ext2 filesystem via U-Boot mmc menu
import os.path
import re
import sys
import struct
import serial
from tqdm import tqdm
import re
DIR_R = re.compile(r'^<DIR>[ \t]+(\d+)[ \t]+(.+)$')
FILE_R = re.compile(r'^[ \t]+(\d+)[ \t]+(.+)$')
SYM_R = re.compile(r'^<SYM>[ \t]+(\d+)[ \t]+(.+)$')
FILE_GOOD_R = re.compile(r'(\d+) bytes read in .+$')
IGNORE = ['.qmlc', '.jsc', '.wav', '.caf', '.mkv', '.png', '.mo', '.css', '.pdf']
IGNORE_NAMES = tuple(['libQt5'])
IGNORE_DIRS = ['.//usr/share/zoneinfo', './/usr/lib/gstreamer-1.0', './/usr/lib/locale']
RAM_ADDR = 0x10000000
BLOCK_SIZE = 0x1000
def exec_cmd(s, cmd):
# print('executing %s' % cmd.decode())
s.write(b'%s\n' % cmd)
lines = []
line = ''
while True:
b = s.read()
if b in [b'\n', b'\r']:
line = line.rstrip('\r\n')
if len(line):
lines.append(line)
line = ''
continue
elif b == b'' and line.startswith('=> '):
break
line += b.decode(errors='replace')
return lines
def parse_hex_lines(lines, max_size):
res = bytearray()
for line in lines:
data, _ = line.split(" ", maxsplit=1)
_, strdata = data.split(maxsplit=1)
data = bytes.fromhex(strdata)
res.extend(data)
return bytes(res)[:max_size]
def dump_file(s, path, size):
w_path = '/'.join(['./', path])
fn, fe = os.path.splitext(path)
fn = os.path.basename(fn)
if size == 0 or fe in IGNORE or fn.startswith(IGNORE_NAMES):
with open(w_path, 'wb') as w:
pass
return
if os.path.exists(w_path) and os.path.getsize(w_path) == size:
return
res = exec_cmd(s, b'ext2load mmc 1:2 %x %s' % (RAM_ADDR, path.encode()))
m = FILE_GOOD_R.match(res[1])
real_sz = -1
if m is not None:
real_sz = m.group(1)
if real_sz == -1:
with open(w_path, 'wb') as w:
pass
return
off = 0
bs = min(size, BLOCK_SIZE)
with open(w_path, 'wb') as w:
with tqdm(total=size) as pb:
lines = exec_cmd(s, b'md.b %x %x' % (RAM_ADDR + off, bs))[1:]
buf = parse_hex_lines(lines, bs)
w.write(buf)
pb.update(bs)
size -= bs
off += bs
while size > 0:
lines = exec_cmd(s, b'\n')
blk = min(size, bs)
buf = parse_hex_lines(lines, blk)
w.write(buf)
pb.update(blk)
size -= blk
off += blk
def dump_mmc_1_2(s, root='./', level=0):
os.makedirs(root, exist_ok=True)
if root in IGNORE_DIRS:
return
res = exec_cmd(s, b'ext2ls mmc 1:2 %s' % root.encode())
res = res[1:]
count = len(res) - 2
dirs = []
for i, line in enumerate(res):
m = DIR_R.match(line)
if not m:
m = SYM_R.match(line)
if not m:
m = FILE_R.match(line)
if not m:
continue
# do file
size = int(m.group(1))
name = m.group(2)
print('%s[%04d/%04d] %s %d' % (' ' * level, i + 1 - 2, count, name, size))
dump_file(s, '/'.join([root, name]), size)
continue
# do sym
size = int(m.group(1))
name = m.group(2)
print('%s[%04d/%04d] <%s> %d' % (' ' * level, i + 1 - 2, count, name, size))
dump_file(s, '/'.join([root, name]), 0)
continue
# do dir
name = m.group(2)
if name not in ['.', '..']:
print('%s[%04d/%04d] [%s]' % (' ' * level, i + 1 - 2, count, name))
dump_mmc_1_2(s, '/'.join([root, name]), level+1)
# return sub_dirs
def main():
# s = serial.Serial('COM15', 115200, timeout=0.001)
s = serial.Serial('/dev/ttyUSB0', 115200, timeout=0.001)
dump_mmc_1_2(s)
s.close()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment