2015-12-25 19:09:45 -05:00
|
|
|
#!/usr/bin/env python
|
2015-12-26 08:52:04 -05:00
|
|
|
#
|
|
|
|
# Dump partitions to file.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2015 Peter Wu <peter@lekensteyn.nl>
|
|
|
|
# Licensed under the MIT license <http://opensource.org/licenses/MIT>.
|
|
|
|
|
2015-12-26 17:18:35 -05:00
|
|
|
from contextlib import closing
|
2015-12-25 19:09:45 -05:00
|
|
|
import argparse, logging, os, struct
|
2015-12-26 17:18:35 -05:00
|
|
|
import lglaf, partitions
|
2015-12-25 19:09:45 -05:00
|
|
|
|
2015-12-26 17:18:35 -05:00
|
|
|
_logger = logging.getLogger("extract-partitions")
|
2015-12-25 19:09:45 -05:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-d", "--outdir", default=".",
|
|
|
|
help="Output directory for disk images.")
|
2015-12-26 08:52:04 -05:00
|
|
|
# Do not dump partitions larger than this size
|
2015-12-25 19:09:45 -05:00
|
|
|
# (userdata 11728 MiB, system 2064 MiB, cache 608 MiB, cust 256 MiB)
|
2015-12-27 10:37:56 -05:00
|
|
|
parser.add_argument("--max-size", metavar="kbytes", type=int, default=65535,
|
|
|
|
help="Maximum partition size to dump (in KiB) or 0 to dump all (default %(default)d)")
|
2015-12-25 19:09:45 -05:00
|
|
|
parser.add_argument("--debug", action='store_true', help="Enable debug messages")
|
2017-10-05 11:19:27 -04:00
|
|
|
parser.add_argument("--skip-hello", action="store_true",
|
|
|
|
help="Immediately send commands, skip HELO message")
|
2015-12-25 19:09:45 -05:00
|
|
|
|
2015-12-26 17:18:35 -05:00
|
|
|
def dump_partitions(comm, disk_fd, outdir, max_size):
|
2017-11-25 17:49:17 -05:00
|
|
|
diskinfo = partitions.get_partitions(comm, disk_fd)
|
|
|
|
for part in diskinfo.gpt.partitions:
|
2017-12-07 06:23:24 -05:00
|
|
|
part_offset = part.first_lba * partitions.BLOCK_SIZE
|
2017-12-21 13:11:03 -05:00
|
|
|
part_size = (part.last_lba - (part.first_lba - 1)) * partitions.BLOCK_SIZE
|
2017-11-25 17:49:17 -05:00
|
|
|
part_name = part.name
|
|
|
|
part_label = "/dev/mmcblk0p%i" % part.index
|
2015-12-27 10:37:56 -05:00
|
|
|
if max_size and part_size > max_size:
|
2015-12-27 05:21:32 -05:00
|
|
|
_logger.info("Ignoring large partition %s (%s) of size %dK",
|
|
|
|
part_label, part_name, part_size / 1024)
|
2015-12-25 19:09:45 -05:00
|
|
|
continue
|
2015-12-27 05:21:32 -05:00
|
|
|
out_path = os.path.join(outdir, "%s.bin" % part_name)
|
2015-12-26 17:18:35 -05:00
|
|
|
try:
|
|
|
|
current_size = os.path.getsize(out_path)
|
|
|
|
if current_size > part_size:
|
|
|
|
_logger.warn("%s: unexpected size %dK, larger than %dK",
|
|
|
|
out_path, current_size / 1024, part_size / 1024)
|
|
|
|
continue
|
|
|
|
elif current_size == part_size:
|
2015-12-27 05:21:32 -05:00
|
|
|
_logger.info("Skipping partition %s (%s), already found at %s",
|
|
|
|
part_label, part_name, out_path)
|
2015-12-26 17:18:35 -05:00
|
|
|
continue
|
|
|
|
except OSError: pass
|
2015-12-27 05:21:32 -05:00
|
|
|
_logger.info("Dumping partition %s (%s) to %s (%d bytes)",
|
|
|
|
part_label, part_name, out_path, part_size)
|
2015-12-26 17:18:35 -05:00
|
|
|
partitions.dump_partition(comm, disk_fd, out_path, part_offset, part_size)
|
2015-12-25 19:09:45 -05:00
|
|
|
|
|
|
|
def main():
|
|
|
|
args = parser.parse_args()
|
|
|
|
logging.basicConfig(format='%(asctime)s %(name)s: %(levelname)s: %(message)s',
|
|
|
|
level=logging.DEBUG if args.debug else logging.INFO)
|
|
|
|
|
|
|
|
try: os.makedirs(args.outdir)
|
|
|
|
except OSError: pass
|
|
|
|
|
|
|
|
comm = lglaf.autodetect_device()
|
|
|
|
with closing(comm):
|
2017-10-05 11:19:27 -04:00
|
|
|
if not args.skip_hello:
|
|
|
|
lglaf.try_hello(comm)
|
|
|
|
|
2015-12-26 17:18:35 -05:00
|
|
|
with partitions.laf_open_disk(comm) as disk_fd:
|
|
|
|
_logger.debug("Opened fd %d for disk", disk_fd)
|
|
|
|
dump_partitions(comm, disk_fd, args.outdir, args.max_size * 1024)
|
2015-12-25 19:09:45 -05:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|