Skip to content

Commit abc0120

Browse files
committed
use OrderedDict, and check for requested partitions that are missing
1 parent ccb5f93 commit abc0120

File tree

1 file changed

+88
-81
lines changed

1 file changed

+88
-81
lines changed

tetherback/tetherback.py

Lines changed: 88 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from tabulate import tabulate
1414
from enum import Enum
1515
from hashlib import md5
16+
from collections import OrderedDict as odict
1617

1718
adbxp = Enum('AdbTransport', 'tcp pipe_xo pipe_b64 pipe_bin')
1819

@@ -75,12 +76,12 @@
7576

7677
if args.nandroid:
7778
rp = args.extra + [x for x in ('boot','recovery','system','userdata','cache') if getattr(args, x)]
78-
backup_partitions = {p: ('%s.tar.gz'%p, None, None) for p in rp}
79+
backup_partitions = odict((p,('%s.tar.gz'%p, None, None)) for p in rp)
7980
else:
8081
rp = args.extra + [x for x in ('boot','recovery') if getattr(args, x)]
81-
backup_partitions = {p: ('%s.emmc.win'%p, None, None) for p in rp}
82+
backup_partitions = odict((p,('%s.emmc.win'%p, None, None)) for p in rp)
8283
mp = [x for x in ('cache','system') if getattr(args, x)]
83-
backup_partitions.update(**{p: ('%s.ext4.win'%p, '/%s'%p, '-p') for p in mp})
84+
backup_partitions.update((p,('%s.ext4.win'%p, '/%s'%p, '-p')) for p in mp)
8485

8586
if args.userdata:
8687
data_omit = []
@@ -156,7 +157,7 @@ def uevent_dict(path):
156157
print("Device reports TWRP version %s" % m.group(1), file=stderr)
157158

158159
# build partition map
159-
partmap = []
160+
partmap = odict()
160161
d = uevent_dict('/sys/block/mmcblk0/uevent')
161162
nparts = int(d['NPARTS'])
162163
print("Reading partition map for mmcblk0 (%d partitions)..." % nparts, file=stderr)
@@ -165,18 +166,25 @@ def uevent_dict(path):
165166
d = uevent_dict('/sys/block/mmcblk0/mmcblk0p%d/uevent'%ii)
166167
size = int(sp.check_output(adbcmd+('shell','cat /sys/block/mmcblk0/mmcblk0p%d/size'%ii)))
167168
# some devices have uppercase names, see #14
168-
partmap.append((d['PARTNAME'].lower(), d['DEVNAME'], int(d['PARTN']), size))
169+
partmap[d['PARTNAME'].lower()] = (d['DEVNAME'], int(d['PARTN']), size)
169170
pbar.update(ii)
170171
else:
171172
pbar.finish()
172173

173-
if args.dry_run or args.verbose > 0:
174+
# check that all partitions intended for backup exist
175+
missing = set(backup_partitions) - set(partmap)
176+
177+
# print partition map
178+
if args.dry_run or missing or args.verbose > 0:
174179
print()
175180
print(tabulate( [[ devname, partname, size//2] + backup_how(partname, backup_partitions)
176-
for partname, devname, partn, size in partmap],
181+
for partname, (devname, partn, size) in partmap.items() ],
177182
[ 'BLOCK DEVICE','NAME','SIZE (KiB)','FILENAME','FORMAT' ] ))
178183
print()
179184

185+
if missing:
186+
p.error("Partitions were requested for backup, but not found in the partition map: %s" % ', '.join(missing))
187+
180188
if args.dry_run:
181189
p.exit()
182190

@@ -194,79 +202,78 @@ def uevent_dict(path):
194202
if args.verify:
195203
sp.check_call(adbcmd+('shell','rm -f /tmp/md5in; mknod /tmp/md5in p'), stderr=sp.DEVNULL)
196204

197-
for partname, devname, partn, size in partmap:
198-
if partname in backup_partitions:
199-
fn, mount, taropts = backup_partitions[partname]
205+
for partname, (fn, mount, taropts) in backup_partitions.items():
206+
devname, partn, size = partmap[partname]
207+
208+
if mount:
209+
print("Saving tarball of %s (mounted at %s), %d MiB uncompressed..." % (devname, mount, size/2048))
210+
fstype = really_mount('/dev/block/'+devname, mount)
211+
if not fstype:
212+
raise RuntimeError('%s: could not mount %s' % (partname, mount))
213+
elif fstype != 'ext4':
214+
raise RuntimeError('%s: expected ext4 filesystem, but found %s' % (partname, fstype))
215+
cmdline = 'tar -czC %s %s . 2> /dev/null' % (mount, taropts or '')
216+
else:
217+
print("Saving partition %s (%s), %d MiB uncompressed..." % (partname, devname, size/2048))
218+
if not really_umount('/dev/block/'+devname, mount):
219+
raise RuntimeError('%s: could not unmount %s' % (partname, mount))
220+
cmdline = 'dd if=/dev/block/%s 2> /dev/null | gzip -f' % devname
221+
222+
if args.verify:
223+
cmdline = 'md5sum /tmp/md5in > /tmp/md5out & %s | tee /tmp/md5in' % cmdline
224+
localmd5 = md5()
225+
226+
if args.transport == adbxp.pipe_bin:
227+
# need stty -onlcr to make adb-shell an 8-bit-clean pipe: http://stackoverflow.com/a/20141481/20789
228+
child = sp.Popen(adbcmd+('shell','stty -onlcr && '+cmdline), stdout=sp.PIPE)
229+
block_iter = iter(lambda: child.stdout.read(65536), b'')
230+
elif args.transport == adbxp.pipe_b64:
231+
# pipe output through base64: excruciatingly slow
232+
child = sp.Popen(adbcmd+('shell',cmdline+'| base64'), stdout=sp.PIPE)
233+
block_iter = iter(lambda: b64dec(b''.join(child.stdout.readlines(65536))), b'')
234+
elif args.transport == adbxp.pipe_xo:
235+
# use adb exec-out, which is
236+
# (a) only available with newer versions of adb on the host, and
237+
# (b) only works with newer versions of TWRP (works with 2.8.0 for @kerlerm)
238+
# https://plus.google.com/110558071969009568835/posts/Ar3FdhknHo3
239+
# https://android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f/adb/commandline.c#1244
240+
child = sp.Popen(adbcmd+('exec-out',cmdline), stdout=sp.PIPE)
241+
block_iter = iter(lambda: child.stdout.read(65536), b'')
242+
else:
243+
port = really_forward(5600+partn, 5700+partn)
244+
if not port:
245+
raise RuntimeError('%s: could not ADB-forward a TCP port')
246+
child = sp.Popen(adbcmd+('shell',cmdline + '| nc -l -p%d -w3'%port), stdout=sp.PIPE)
200247

201-
if mount:
202-
print("Saving tarball of %s (mounted at %s), %d MiB uncompressed..." % (devname, mount, size/2048))
203-
fstype = really_mount('/dev/block/'+devname, mount)
204-
if not fstype:
205-
raise RuntimeError('%s: could not mount %s' % (partname, mount))
206-
elif fstype != 'ext4':
207-
raise RuntimeError('%s: expected ext4 filesystem, but found %s' % (partname, fstype))
208-
cmdline = 'tar -czC %s %s . 2> /dev/null' % (mount, taropts or '')
209-
else:
210-
print("Saving partition %s (%s), %d MiB uncompressed..." % (partname, devname, size/2048))
211-
if not really_umount('/dev/block/'+devname, mount):
212-
raise RuntimeError('%s: could not unmount %s' % (partname, mount))
213-
cmdline = 'dd if=/dev/block/%s 2> /dev/null | gzip -f' % devname
214-
215-
if args.verify:
216-
cmdline = 'md5sum /tmp/md5in > /tmp/md5out & %s | tee /tmp/md5in' % cmdline
217-
localmd5 = md5()
218-
219-
if args.transport == adbxp.pipe_bin:
220-
# need stty -onlcr to make adb-shell an 8-bit-clean pipe: http://stackoverflow.com/a/20141481/20789
221-
child = sp.Popen(adbcmd+('shell','stty -onlcr && '+cmdline), stdout=sp.PIPE)
222-
block_iter = iter(lambda: child.stdout.read(65536), b'')
223-
elif args.transport == adbxp.pipe_b64:
224-
# pipe output through base64: excruciatingly slow
225-
child = sp.Popen(adbcmd+('shell',cmdline+'| base64'), stdout=sp.PIPE)
226-
block_iter = iter(lambda: b64dec(b''.join(child.stdout.readlines(65536))), b'')
227-
elif args.transport == adbxp.pipe_xo:
228-
# use adb exec-out, which is
229-
# (a) only available with newer versions of adb on the host, and
230-
# (b) only works with newer versions of TWRP (works with 2.8.0 for @kerlerm)
231-
# https://plus.google.com/110558071969009568835/posts/Ar3FdhknHo3
232-
# https://android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f/adb/commandline.c#1244
233-
child = sp.Popen(adbcmd+('exec-out',cmdline), stdout=sp.PIPE)
234-
block_iter = iter(lambda: child.stdout.read(65536), b'')
248+
# FIXME: need a better way to check that socket is ready to transmit
249+
time.sleep(1)
250+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
251+
s.connect(('localhost', port))
252+
block_iter = iter(lambda: s.recv(65536), b'')
253+
254+
pbwidgets = [' %s: ' % fn, Percentage(), ' ', ETA(), ' ', FileTransferSpeed(), ' ', DataSize() ]
255+
pbar = ProgressBar(max_value=size*512, widgets=pbwidgets).start()
256+
257+
with open(fn, 'wb') as out:
258+
for block in block_iter:
259+
out.write(block)
260+
if args.verify:
261+
localmd5.update(block)
262+
pbar.update(out.tell())
235263
else:
236-
port = really_forward(5600+partn, 5700+partn)
237-
if not port:
238-
raise RuntimeError('%s: could not ADB-forward a TCP port')
239-
child = sp.Popen(adbcmd+('shell',cmdline + '| nc -l -p%d -w3'%port), stdout=sp.PIPE)
240-
241-
# FIXME: need a better way to check that socket is ready to transmit
242-
time.sleep(1)
243-
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
244-
s.connect(('localhost', port))
245-
block_iter = iter(lambda: s.recv(65536), b'')
246-
247-
pbwidgets = [' %s: ' % fn, Percentage(), ' ', ETA(), ' ', FileTransferSpeed(), ' ', DataSize() ]
248-
pbar = ProgressBar(max_value=size*512, widgets=pbwidgets).start()
249-
250-
with open(fn, 'wb') as out:
251-
for block in block_iter:
252-
out.write(block)
253-
if args.verify:
254-
localmd5.update(block)
255-
pbar.update(out.tell())
256-
else:
257-
pbar.max_value = out.tell() or pbar.max_value # need to adjust for the smaller compressed size
258-
pbar.finish()
259-
260-
if args.verify:
261-
devicemd5 = sp.check_output(adbcmd+('shell','cat /tmp/md5out')).strip().decode().split()[0]
262-
localmd5 = localmd5.hexdigest()
263-
if devicemd5 != localmd5:
264-
raise RuntimeError("md5sum mismatch (local %s, device %s)" % (localmd5, devicemd5))
265-
with open(fn+'.md5', 'w') as md5out:
266-
print('%s *%s' % (localmd5, fn), file=md5out)
267-
268-
if args.transport==adbxp.tcp:
269-
s.close()
270-
if not really_unforward(port):
271-
raise RuntimeError('could not remove ADB-forward for TCP port %d' % port)
272-
child.terminate()
264+
pbar.max_value = out.tell() or pbar.max_value # need to adjust for the smaller compressed size
265+
pbar.finish()
266+
267+
if args.verify:
268+
devicemd5 = sp.check_output(adbcmd+('shell','cat /tmp/md5out')).strip().decode().split()[0]
269+
localmd5 = localmd5.hexdigest()
270+
if devicemd5 != localmd5:
271+
raise RuntimeError("md5sum mismatch (local %s, device %s)" % (localmd5, devicemd5))
272+
with open(fn+'.md5', 'w') as md5out:
273+
print('%s *%s' % (localmd5, fn), file=md5out)
274+
275+
if args.transport==adbxp.tcp:
276+
s.close()
277+
if not really_unforward(port):
278+
raise RuntimeError('could not remove ADB-forward for TCP port %d' % port)
279+
child.terminate()

0 commit comments

Comments
 (0)