#!/bin/bash # nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid # Requirements: # - a modded android in recovery mode # - busybox in recovery mode # - 'adb shell' support running as root # - dump_image-arm-uclibc compiled and in current working dir # - mkyaffs2image|mkyaffs2image-$ARCH in current working dir # - root on a linux/mac (POSIX) machine for constructing system and data images # Using JF RC30 v1.2 recovery image works, RC8 v1.2 doesnt because of adb shell missing. # You can flash RC30 v1.2 recovery.img on an RC8 v1.2 phone until the RC8 v1.2 recovery mode is fixed. # Reference data: # dev: size erasesize name #mtd0: 00040000 00020000 "misc" #mtd1: 00500000 00020000 "recovery" #mtd2: 00280000 00020000 "boot" #mtd3: 04380000 00020000 "system" #mtd4: 04380000 00020000 "cache" #mtd5: 04ac0000 00020000 "userdata" # We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. # Logical steps (v2.0): # # 0. test for a target dir and the various tools needed, if not found then exit with error. # 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. # 2. mount system and data partitions read-only, set up adb portforward and create destdir # 3. check free space on /cache, exit if less blocks than 20MB free # 4. push required tools to device in /cache # 5 for partitions boot recovery misc: # 5a get md5sum for content of current partition *on the device* (no data transfered) # 5b while MD5sum comparison is incorrect (always is the first time): # 5b1 dump current partition to a netcat session # 5b2 start local netcat to dump image to current dir # 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) # 6 for partitions system data: # 6a get md5sum for tar of content of current partition *on the device* (no data transfered) # 6b while MD5sum comparison is incorrect (always is the first time): # 6b1 tar current partition to a netcat session # 6b2 start local netcat to dump tar to current dir # 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) # 6c if i'm running as root: # 6c1 create a temp dir using either tempdir command or the deviceid in /tmp # 6c2 extract tar to tempdir # 6c3 invoke mkyaffs2image to create the img # 6c4 clean up # 7. remove tools from device /cache # 8. umount system and data on device # 9. print success. DEVICEID=foo TOOLS="dump_image-arm-uclibc" TCPPORT=4531 echo "nandroid v2.1" # 0 if [ "$1" == "" ]; then echo "Usage: $0 destdir" echo "destdir will be created if it does not exist" exit 0 fi DESTDIR=$1 if [ ! -d $DESTDIR ]; then mkdir -p $DESTDIR if [ ! -d $DESTDIR ]; then echo "error: cannot create $DESTDIR" exit 1 fi else touch $DESTDIR/.nandroidwritable if [ ! -e $DESTDIR/.nandroidwritable ]; then echo "error: cannot write to $DESTDIR" exit 1 fi rm $DESTDIR/.nandroidwritable fi adb=`which adb` if [ "$adb" == "" ]; then echo "error: adb not found in path." exit 1 fi md5sum=`which md5sum` if [ "$md5sum" == "" ]; then echo "error: md5sum not found in path." exit 1 fi tar==`which tar` if [ "$tar" == "" ]; then echo "error: tar not found in path." exit 1 fi nc=`which nc` if [ "$nc" == "" ]; then nc=`which netcat` if [ "$nc" == "" ]; then echo "error: nc nor netcat found in path." exit 1 else nc=`which netcat` fi fi if [ -e `pwd`/mkyaffs2image-`uname -m` ]; then mkyaffs2image=`pwd`/mkyaffs2image-`uname -m` fi if [ -e `pwd`/mkyaffs2image ]; then mkyaffs2image=`pwd`/mkyaffs2image fi if [ "$mkyaffs2image" == "" ]; then echo "error: `pwd`/mkyaffs2image or mkyaffs2image-`uname -m` missing" echo "either use the provided binary or compile it in tartools/yaffs2/utils" exit 1 fi for tool in $TOOLS; do if [ ! -e ./$tool ]; then echo "error: $tool not found in current dir" echo "either use the provided binary or cross-compile it for arm in nandtools/android-imagetools" exit 1 fi done # 1 DEVICEID="`adb devices | grep recovery | awk '{ print $1 }'`" if [ "$DEVICEID" == "foo" ]; then echo "error: no phone found in recovery mode. power off phone. press and hold home button, then power up. keep home button pressed. then try again." exit 1 fi GOTROOT="`adb shell id -u | cut -c 1`" if [ ! "$GOTROOT" == "0" ]; then if [ "`adb shell whoami | grep 'uid 0'`" == "" ]; then echo "error: don't have root in recovery mode, aborting" exit 1 fi fi # 2. echo "mounting system and data read-only on device" adb shell "umount /system 2>/dev/null" adb shell "umount /data 2>/dev/null" adb shell mount -o ro /system adb shell mount -o ro /data if [ "`adb shell mount | grep 'system.*ro'`" == "" ]; then echo "error while mounting system read-only" exit 1 fi if [ "`adb shell mount | grep 'data.*ro'`" == "" ]; then echo "error while mounting system read-only" exit 1 fi echo "start adb portforward on port $TCPPORT" adb forward tcp:$TCPPORT tcp:$TCPPORT # 3. echo "checking free space on cache" FREEBLOCKS="`adb shell df -k /cache| grep cache | awk '{ print $4 }'`" # we need about 5MB for the tools plus max 8MB for the recovery + boot, so 20MB should be fine if [ $FREEBLOCKS -le 20000 ]; then echo "error: not enough free space available on cache partition, aborting." adb shell umount /system adb shell umount /data exit 1 fi # 4. echo -n "pushing tools to /cache: " for tool in $TOOLS; do echo -n "$tool... " adb push ./$tool /cache/$tool 2>/dev/null done echo "done" if [ ! "`adb shell 'ls /dev/mtd/mtd6ro 2>/dev/null'`" == "" ]; then echo -n "Dumping splash1 to $DESTDIR/splash1.img..." DEVICEMD5=`adb shell "dd if=/dev/mtd/mtd6ro skip=19072 bs=2048 count=150 2>/dev/null | md5sum" | awk '{ print $1 }'` sleep 1s MD5RESULT=1 # 5b while [ $MD5RESULT -eq 1 ]; do # 5b1 adb shell "dd if=/dev/mtd/mtd6ro skip=19072 bs=2048 count=150 2>/dev/null | nc -l -p $TCPPORT" & sleep 2s # 5b2 $nc 127.0.0.1 $TCPPORT > $DESTDIR/splash1.img sleep 1s echo -n " done, verifying..." # 5b3 echo "${DEVICEMD5} $DESTDIR/splash1.img" | md5sum --check --status - if [ $? -eq 1 ]; then echo "error: md5sum mismatch, retrying" else echo "OK" MD5RESULT=0 fi done echo -n "Dumping splash2 to $DESTDIR/splash2.img..." DEVICEMD5=`adb shell "dd if=/dev/mtd/mtd6ro skip=19456 bs=2048 count=150 2>/dev/null | md5sum" | awk '{ print $1 }'` sleep 1s MD5RESULT=1 # 5b while [ $MD5RESULT -eq 1 ]; do # 5b1 adb shell "dd if=/dev/mtd/mtd6ro skip=19456 bs=2048 count=150 2>/dev/null | nc -l -p $TCPPORT" & sleep 2s # 5b2 $nc 127.0.0.1 $TCPPORT > $DESTDIR/splash2.img sleep 1s echo -n " done, verifying... " # 5b3 echo "${DEVICEMD5} $DESTDIR/splash2.img" | md5sum --check --status - if [ $? -eq 1 ]; then echo "error: md5sum mismatch, retrying" else echo "OK" MD5RESULT=0 fi done fi # 5. for image in boot recovery misc; do # 5a echo -n "Dumping $image to $DESTDIR/$image.img..." DEVICEMD5=`adb shell "/cache/dump_image-arm-uclibc $image - | md5sum" | awk '{ print $1 }'` sleep 1s MD5RESULT=1 # 5b while [ $MD5RESULT -eq 1 ]; do # 5b1 adb shell "/cache/dump_image-arm-uclibc $image - | nc -l -p $TCPPORT" & sleep 2s # 5b2 $nc 127.0.0.1 $TCPPORT > $DESTDIR/$image.img sleep 1s echo -n " done, verifying... " # 5b3 echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum --check --status - if [ $? -eq 1 ]; then echo "error: md5sum mismatch, retrying" else echo "OK" MD5RESULT=0 fi done done # 6 for image in system data cache; do # 6a echo -n "Dumping $image to $DESTDIR/$image.tar..." DEVICEMD5=`adb shell "tar c -f - /$image 2>/dev/null | md5sum" | awk '{ print $1 }'` sleep 1s MD5RESULT=1 # 6b while [ $MD5RESULT -eq 1 ]; do # 6b1 out=`adb shell "tar c -f - /$image | nc -l -p $TCPPORT"` & sleep 2s # 6b2 $nc 127.0.0.1 $TCPPORT > $DESTDIR/$image.tar sleep 1s echo -n " done, verifying... " # 6b3 echo "${DEVICEMD5} $DESTDIR/$image.tar" | md5sum --check --status - if [ $? -eq 1 ]; then echo "error: md5sum mismatch, retrying" else echo "OK" MD5RESULT=0 fi done # 6c IMGMETHOD=echo if [ "`whoami`" == "root" ]; then IMGMETHOD=root fi if [ ! "`which fakeroot`" == "" ]; then if [ "`ldd $mkyaffs2image | grep 'not.*dynamic'`" == "" ]; then IMGMETHOD=fakeroot else echo "note: fakeroot found but $mkyaffs2image is statically linked" echo "replace with a dynamically linked copy to enable fakeroot support" fi fi case $IMGMETHOD in root) # 6c1 TMPDIR=$DESTDIR/$DEVICEID-$image-tmp mkdir $TMPDIR # 6c2 echo -n "Extracting $image.tar to $TMPDIR..." tar x -C $TMPDIR -f $DESTDIR/$image.tar # 6c3 echo -n " runnig mkyaffs2image..." $mkyaffs2image $TMPDIR/$image $DESTDIR/$image.img echo "done" # 6c4 #rm -rf $DESTDIR/$image.tar $TMPDIR ;; fakeroot) echo -n "Using fakeroot to generate $image.img from $image.tar..." TMPDIR=$DESTDIR/$DEVICEID-$image-tmp mkdir $TMPDIR echo "tar x -C $TMPDIR -f $DESTDIR/$image.tar" > $DESTDIR/fakeroot-$image.sh echo "$mkyaffs2image $TMPDIR/$image $DESTDIR/$image.img" >> $DESTDIR/fakeroot-$image.sh fakeroot /bin/bash $DESTDIR/fakeroot-$image.sh echo "done" #rm -rf $DESTDIR/$image.tar $TMPDIR $DESTDIR/fakeroot-$image.sh ;; echo) # 6d echo "To convert $image.tar to $image.img, run the following commands as root:" echo "" echo "mkdir /tmp/$DEVICEID-$image-tmp" echo "tar x -C /tmp/$DEVICEID-$image-tmp -f $DESTDIR/$image.tar" echo "$mkyaffs2image /tmp/$DEVICEID-$image-tmp/$image $DESTDIR/$image.img" echo "" echo "Make sure that /tmp/$DEVICEID-$image-tmp doesn't exist befor you extract, or use different paths." echo "Remember to remove the tmp dirs when you are done." ;; esac done # 7. echo -n "removing tools from /cache: " for tool in $TOOLS; do echo -n "$tool... " adb shell rm /cache/$tool done echo "done" # 8. echo "unmounting system and data on device" adb shell umount /system adb shell umount /data echo -n "generating md5sum file..." CWD=$PWD cd $DESTDIR md5sum *img > nandroid.md5 cd $CWD echo "done" # 9. echo "Backup successful."