| #!/sbin/sh |
| |
| # nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid |
| |
| # Requirements: |
| |
| # - a modded android in recovery mode (JF 1.3 will work by default) |
| # - adb shell as root in recovery mode if not using a pre-made recovery image |
| # - busybox in recovery mode |
| # - dump_image-arm-uclibc compiled and in path on phone |
| # - mkyaffs2image-arm-uclibc compiled and installed in path on phone |
| |
| # 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" |
| #mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 |
| |
| # 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.1): |
| # |
| # 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. |
| |
| |
| RECOVERY=foo |
| |
| echo "nandroid-mobile v2.1" |
| mkfstab.sh > /etc/fstab |
| |
| if [ "$1" == "" ]; then |
| echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" |
| echo "- backup will store a full system backup on /sdcard/nandroid" |
| echo "- restore path will restore the last made backup for boot, system, recovery and data" |
| exit 0 |
| fi |
| |
| case $1 in |
| backup) |
| mkyaffs2image=`which mkyaffs2image` |
| if [ "$mkyaffs2image" == "" ]; then |
| mkyaffs2image=`which mkyaffs2image-arm-uclibc` |
| if [ "$mkyaffs2image" == "" ]; then |
| echo "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" |
| exit 1 |
| fi |
| fi |
| dump_image=`which dump_image` |
| if [ "$dump_image" == "" ]; then |
| dump_image=`which dump_image-arm-uclibc` |
| if [ "$dump_image" == "" ]; then |
| echo "error: dump_image or dump_image-arm-uclibc not found in path" |
| exit 1 |
| fi |
| fi |
| break |
| ;; |
| restore) |
| flash_image=`which flash_image` |
| if [ "$flash_image" == "" ]; then |
| flash_image=`which flash_image-arm-uclibc` |
| if [ "$flash_image" == "" ]; then |
| echo "error: flash_image or flash_image-arm-uclibc not found in path" |
| exit 1 |
| fi |
| fi |
| unyaffs=`which unyaffs` |
| if [ "$unyaffs" == "" ]; then |
| echo "error: unyaffs not found in path" |
| exit 1 |
| fi |
| break |
| ;; |
| esac |
| |
| # 1 |
| RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` |
| if [ "$RECOVERY" == "foo" ]; then |
| echo "error: not running in recovery mode, aborting" |
| exit 1 |
| fi |
| if [ ! "`id -u 2>/dev/null`" == "0" ]; then |
| if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then |
| echo "error: must run as root, aborting" |
| exit 1 |
| fi |
| fi |
| |
| |
| case $1 in |
| restore) |
| ENERGY=`cat /sys/class/power_supply/battery/capacity` |
| if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then |
| ENERGY=100 |
| fi |
| if [ ! $ENERGY -ge 30 ]; then |
| echo "Error: not enough battery power" |
| echo "Connect charger or USB power and try again" |
| exit 1 |
| fi |
| RESTOREPATH=$2 |
| if [ ! -f $RESTOREPATH/nandroid.md5 ]; then |
| echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" |
| exit 1 |
| fi |
| umount /system 2>/dev/null |
| umount /data 2>/dev/null |
| mount -o rw /system || FAIL=1 |
| mount -o rw /data || FAIL=2 |
| case $FAIL in |
| 1) echo "Error mounting system read-write"; umount /system /data /cache; exit 1;; |
| 2) echo "Error mounting data read-write"; umount /system /data /cache; exit 1;; |
| esac |
| |
| echo "Verifying backup images..." |
| CWD=$PWD |
| cd $RESTOREPATH |
| md5sum -c nandroid.md5 |
| if [ $? -eq 1 ]; then |
| echo "error: md5sum mismatch, aborting" |
| exit 1 |
| fi |
| for image in boot; do |
| echo "Flashing $image..." |
| $flash_image $image $image.img |
| done |
| curdir=$(pwd) |
| for image in system data cache; do |
| echo "Unpacking $image..." |
| cd /$image |
| rm -rf * |
| unyaffs $curdir/$image.img |
| cd $curdir |
| done |
| sync |
| umount /system |
| umount /data |
| echo "Restore done" |
| exit 0 |
| ;; |
| backup) |
| break |
| ;; |
| *) |
| echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" |
| echo "- backup will store a full system backup on /sdcard/nandroid" |
| echo "- restore path will restore the last made backup for boot, system, recovery and data" |
| exit 1 |
| ;; |
| esac |
| |
| # 2. |
| echo "mounting system and data read-only, sdcard read-write" |
| umount /system 2>/dev/null |
| umount /data 2>/dev/null |
| umount /sdcard 2>/dev/null |
| mount -o ro /system || FAIL=1 |
| mount -o ro /data || FAIL=2 |
| mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 |
| case $FAIL in |
| 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; |
| 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; |
| 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; |
| esac |
| |
| TIMESTAMP="`date +%Y%m%d-%H%M`" |
| BASEDIR=/sdcard/nandroid |
| if [ !-z "$2" ]; then |
| BASEDIR=$2 |
| fi |
| |
| DESTDIR=$BASEDIR/$TIMESTAMP |
| if [ ! -d $DESTDIR ]; then |
| mkdir -p $DESTDIR |
| if [ ! -d $DESTDIR ]; then |
| echo "error: cannot create $DESTDIR" |
| umount /system 2>/dev/null |
| umount /data 2>/dev/null |
| umount /sdcard 2>/dev/null |
| exit 1 |
| fi |
| else |
| touch $DESTDIR/.nandroidwritable |
| if [ ! -e $DESTDIR/.nandroidwritable ]; then |
| echo "error: cannot write to $DESTDIR" |
| umount /system 2>/dev/null |
| umount /data 2>/dev/null |
| umount /sdcard 2>/dev/null |
| exit 1 |
| fi |
| rm $DESTDIR/.nandroidwritable |
| fi |
| |
| # 3. |
| echo "checking free space on sdcard" |
| FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" |
| # we need about 130MB for the dump |
| if [ $FREEBLOCKS -le 130000 ]; then |
| echo "error: not enough free space available on sdcard (need 130mb), aborting." |
| umount /system 2>/dev/null |
| umount /data 2>/dev/null |
| umount /sdcard 2>/dev/null |
| exit 1 |
| fi |
| |
| |
| |
| if [ -e /dev/mtd/mtd6ro ]; then |
| echo -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." |
| dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null |
| echo "done" |
| sleep 1s |
| echo -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." |
| dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null |
| echo "done" |
| fi |
| |
| |
| # 5. |
| for image in boot recovery misc; do |
| # 5a |
| DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` |
| sleep 1s |
| MD5RESULT=1 |
| # 5b |
| echo -n "Dumping $image to $DESTDIR/$image.img..." |
| ATTEMPT=0 |
| while [ $MD5RESULT -eq 1 ]; do |
| let ATTEMPT=$ATTEMPT+1 |
| # 5b1 |
| $dump_image $image $DESTDIR/$image.img |
| sync |
| # 5b3 |
| echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - |
| if [ $? -eq 1 ]; then |
| true |
| else |
| MD5RESULT=0 |
| fi |
| if [ "$ATTEMPT" == "5" ]; then |
| echo "fatal error while trying to dump $image, aborting" |
| umount /system |
| umount /data |
| umount /sdcard |
| exit 1 |
| fi |
| done |
| echo "done" |
| done |
| |
| # 6 |
| for image in system data cache; do |
| # 6a |
| echo -n "Dumping $image to $DESTDIR/$image.img..." |
| $mkyaffs2image /$image $DESTDIR/$image.img |
| sync |
| echo "done" |
| done |
| |
| |
| # 7. |
| echo -n "generating md5sum file..." |
| CWD=$PWD |
| cd $DESTDIR |
| md5sum *img > nandroid.md5 |
| cd $CWD |
| echo "done" |
| |
| # 8. |
| echo "unmounting system, data and sdcard" |
| umount /system |
| umount /data |
| umount /sdcard |
| |
| # 9. |
| echo "Backup successful." |