#!/bin/sh # # do_mirror # (c) SPDsoft 2001-2011. v0.7.6 # Tue Oct 31 13:22:20 2006 # Thu Apr 12 14:06:14 2007 - Support for Solaris i86pc # Fri May 4 14:09:25 2007 - Linux-x86_64, fix_fstab # Wed May 21 12:52:48 2008 - Linux grub / no chroot # Thu Apr 22 15:33:00 2010 - IRIX, /dev/root /dev/usr aliases # Mon Oct 10 12:13:17 2011 - Linux, ext4 # Tue Mar 11 11:58:04 2014 - Linux, GPT # ex: set tabstop=4 # # # This script will clone your system disk to a second local or remote # identical hard disk. # If disks are not identical you should create the partition table # on the destination disk first, and then, use this script with # -p option. # # Requires: # Linux # Two identical disks $ORIG $DEST (default /dev/sda and /dev/sdb) # Original system on $ORIG # No more than a single swap partition # ext2/ext3/ext4 filesystems. # /new/[1-9] exists # For lilo/grub, /boot, /sbin and /etc reside on ${ORIG}1 # For GPT, gdisk: An fdisk-like partitioning tool for GPT disks # I'm not really sure about the "grub" setup. Since we are copying to # second disk, it should be hd1, I guess... # # Note: /dev/md original only supported on live mounted FS # Note: /dev/md0 is translated to /dev/?d1 # Note: GPT not supported when destination is remote # Warning!: GPT not tested # # Or: # Solaris 2.x # Two identical disks $ORIG $DEST # (default /dev/dsk/c0t0d0s2 /dev/dsk/c0t1d0s2) # Single drive mounted on $ORIG (this could be easily fixed -df-) # Original running system on ${ORIG}2 # ufs filesystems # /new/[0-9] exists # # Or: # IRIX 6.x # Two identical disks $ORIG $DEST # (default dev/dsk/dks0d1s DEST=/dev/dsk/dks0d3s) # Single drive mounted on $ORIG (this could be easily fixed -df-) # xfs/efs filesystems # /new/[0-9] exists # /root/tmp exists (with about 3 Mb free space) # Not implemented: copy of partition table, remote boot loader install # Note: IRIX uses /dev/root and /dev/usr aliases. Only these two # aliases are supported by this script. # # Disclaimer: # This script works for me in some typical configurations. # It is not a robust-luser-proof script. # Plesase, read it carefully, use '-n' switch and make sure it # will work for you. # THIS SCRIPT COULD ERASE ALL OF YOUR FILESYSTEMS! # Use it at your own risk # # NOTE: This script uses the output of "df", which may vary from # a system to another. Someday I will remove this dependence; by # now you can't use dev aliases nor Linux LABEL's; and your "df" # should print mount point on the last column. See code for details. # This means also that you can't boot from another disk and copy # a disk which is not mounted. I will fix this soon... # # Now, it should work with non mounted partitions on Linux, but is # not tested. # case `uname` in SunOS) ARCH=SunOS-`arch` FSTAB=etc/vfstab ;; Linux) ARCH=Linux-`arch` ;; IRIX*) ARCH=IRIX-mips ;; *) echo Error: unsupported system exit 1 ;; esac case $ARCH in SunOS-sun4) ORIG=/dev/dsk/c0t0d0s DEST=/dev/dsk/c0t1d0s GNU_TAR=gtar CPIO_FLAGS="" ;; SunOS-i86pc) ORIG=/dev/dsk/c1d0s DEST=/dev/dsk/c2d0s GNU_TAR=/usr/sfw/bin/gtar CPIO_FLAGS="" ;; IRIX*) ORIG=/dev/dsk/dks0d1s DEST=/dev/dsk/dks0d3s GNU_TAR=gtar CPIO_FLAGS="" ;; Linux-i*86|Linux-x86_64) ORIG=/dev/sda DEST=/dev/sdb GNU_TAR=tar CPIO_FLAGS=--sparse ;; *) echo Error: unsupported system exit 1 ;; esac CMD=`basename $0` Usage() { cat < EOF exit 1 } ECHO=echo #ECHO= CHECK= #CHECK=-c VERB= TOC=false NOTOC=false NOBOOT=false FIXFSTAB=false RSH="sh -c" TAR=: CPIO=false DUMP=false GRUB=1 EXCLUDE="" FSTAB=etc/fstab copy_fs() { if expr "$2" : "/new/.*" >/dev/null 2>&1 then : else echo "error: \"$2\" not under /new" exit 1 fi if $TAR then $ECHO sh -c "( cd $1 && $GNU_TAR \ --create \ --sparse \ --atime-preserve \ --preserve \ --read-full-records \ --one-file-system \ --totals \ --file=- \ . ) | $RSH \"( cd $2 && $GNU_TAR $VERB \ --extract \ --sparse \ --read-full-records \ --atime-preserve \ --file=- \ --preserve )\"" else if $CPIO then $ECHO sh -c "(cd $1; find . -xdev -depth -print | $RSH \"cpio -pdm $CPIO_FLAGS $VERB $2)\"" else case $ARCH in SunOS-*) $ECHO sh -c "ufsdump 0uf - $1 | $RSH \"(cd $2 && ufsrestore rf -; rm -f restoresymtable)\"" ;; Linux-i*86|Linux-x86_64) $ECHO sh -c "dump -0au -f - $1 | $RSH \"(cd $2 && /sbin/restore -r $VERB -f -; rm -f restoresymtable )\"" ;; IRIX*) case $FSTYPE in efs) echo "#### EFS dump not tested" $ECHO sh -c "dump -0u -f - $1 | $RSH \"(cd $2 && /sbin/restore xf -; rm -f restoresymtable )\"" ;; xfs) $ECHO sh -c "xfsdump -l 0 -p 60 -F - $1 | $RSH \"(cd $2 && /sbin/xfsrestore -F - .; rm -rf xfsrestorehousekeepingdir )\"" ;; esac ;; esac fi fi } fix_fstab() { $ECHO echo "## Fixing fstab" $ECHO $RSH "sed -e \"s,$ORIG,$DEST,g\" \ -e \"s,$RORIG,$RDEST,g\" \ /new/fstab > /new/$1/$FSTAB" < /dev/null } # # Note: I recall having read cpio is faster for fs copies; but # on a Linux 2.2.19/RH 6.2 system with SCSI disks: # # cpio: 10.48user 71.27system 7:24.40elapsed 18%CPU # tar: 12.86user 79.25system 6:37.65elapsed 23%CPU # (tar ignores sockets like /dev/log, which is a Bad Thing) # dump: user 0m22.95s sys 1m20.69s real 7m52.55s # # and on a Sun E450 Solaris 8 system with SCSI disks: # # cpio: real 1:12:50.5 user 16.0 sys 5:28.3 # gtar: real 1:06:25.5 user 23.9 sys 6:28.2 # (tar ignores sockets and doors like etc/sysevent/sysevent_door) # dump: real 1:05:18.1 user 1:51.5 sys 6:51.2 # set -- `getopt FPpbDCvcynhg:f:t:e: $*` for i in $* do case $i in -h) Usage; shift;; -y) ECHO= ; shift;; -n) ECHO=echo ; shift;; -c) CHECK=-c ; shift;; -v) VERB=-v ; shift;; -b) NOBOOT=: ; shift;; -P) TOC=: ; shift;; -F) FIXFSTAB=: ; shift;; -p) NOTOC=: ; shift;; -C) TAR=false ; CPIO=: ; DUMP=false ; shift;; -D) TAR=false ; CPIO=false ; DUMP=: ; shift;; -f) ORIG=$2; shift; shift;; -t) DEST=$2; shift; shift;; -e) EXCLUDE=$2; shift; shift;; -g) GRUB=$2; shift; shift;; esac done RHOST="" if expr "$DEST" : ".*:.*" >/dev/null 2>&1 then RHOST=`echo "$DEST" | sed -e 's/:.*$//'` DEST=`echo "$DEST" | sed -e 's/^.*://'` $ECHO echo "#### Using $DEST on remote host $RHOST" #RSH="ssh -xT $RHOST" RSH="ssh -xT -c blowfish -o 'compression no' $RHOST" fi [ -d /root/tmp ] || mkdir -p /root/tmp set -e cd /root/tmp [ -d /new ] || mkdir -p /new for f in 0 1 2 3 4 5 6 7 8 9 do [ -d /new/$f ] || mkdir -p /new/$f done [ -d /new/orig ] || mkdir -p /new/orig for f in 0 1 2 3 4 5 6 7 8 9 do [ -d /new/orig/$f ] || mkdir -p /new/orig/$f done set +e $ECHO echo "#### `date`" RORIG=`echo ${ORIG} | sed -e "s:dsk:rdsk:"` RDEST=`echo ${DEST} | sed -e "s:dsk:rdsk:"` case $ARCH in SunOS-*) prtvtoc -h ${ORIG}2 | sed -e 's,[ ]*/.*,,' > orig.txt ;; Linux-i*86|Linux-x86_64) if sfdisk -l $ORIG 2>&1 | fgrep "Use GNU Parted" > /dev/null then gdisk -l $ORIG | grep -v GUID > orig.txt else /sbin/fdisk -u -l $ORIG > orig.txt fi ;; IRIX*) prtvtoc -h ${ORIG}0 > orig.txt ;; esac if $NOTOC then : else $ECHO echo "#### Comparing $DEST $ORIG partition tables" case $ARCH in SunOS-*) $RSH "prtvtoc -h ${DEST}2" | sed -e 's,[ ]*/.*,,' > dest.txt ;; Linux-i*86|Linux-x86_64) if sfdisk -l $ORIG 2>&1 | fgrep "Use GNU Parted" > /dev/null then gdisk -l $DEST | grep -v GUID | sed -e "s:$DEST:$ORIG:g" > dest.txt else $RSH "/sbin/fdisk -u -l $DEST" | sed -e "s:$DEST:$ORIG:g" > dest.txt fi ;; IRIX*) $RSH "prtvtoc -h ${DEST}0" > dest.txt ;; esac if $TOC then :>dest.txt $ECHO echo "## Forcing new TOC on $DEST" fi if cmp -s orig.txt dest.txt then : else $ECHO echo "#### Creating partition table on $DEST" $ECHO echo "## `diff orig.txt dest.txt`" case $ARCH in SunOS-*) $ECHO sh -c \ "prtvtoc ${ORIG}2 | $RSH \"fmthard -s - ${RDEST}2\"" ;; Linux-i*86|Linux-x86_64) if sfdisk -l $ORIG 2>&1 | fgrep "Use GNU Parted" > /dev/null then $ECHO echo "#### GPT (GUID Partition Table) on $ORIG" if [ "$RHOST" != "" ] then $ECHO echo "#### GPT not supported on remote copy" exit 1 fi $ECHO sgdisk -R=$DEST $ORIG $ECHO sgdisk -G $DEST else $ECHO sh -c \ "/sbin/sfdisk -d $ORIG | sed -e s:$ORIG:$DEST:g | $RSH \"/sbin/sfdisk $DEST\"" $ECHO $RSH "/sbin/sfdisk -V $DEST" SWAP=`$RSH "/sbin/fdisk -l $DEST" | awk '/swap$/ {print $1}'` if [ "$SWAP" != "" ] then $ECHO echo "## setting up swap area on $SWAP" $ECHO $RSH "/sbin/mkswap $CHECK $SWAP" fi fi ;; IRIX*) echo "#### Sorry, not implemented by now" # format: ::: # # #prtvtoc -h /dev/dsk/${RORIG}vol |\ #sed -e -e '/volume/d' \ #-e 's/\([ ]*\)\([0-9][0-9]*\)\([ ]*\)\([a-z][a-z]*\)\([^0-9]*\)\([0-9][0-9]*\)\([^0-9]*\)\([0-9][0-9]*\)\([ ]*\)/\2:\4:\6:\8/' |\ #tr \\012 ";" | sed -e 's/;$//' # /usr/sysadm/privbin/setDiskParts -d ${RDEST}vol -p "$format" -m exit 1 ;; esac fi fi EORIG=`echo "$ORIG" | sed -e 's,/,\\\/,g'` ( # # Output of this block must be partition id's (one number per line) # case $ARCH in SunOS-i86pc) df | sed -e "/($EORIG/!d" \ -e 's:\(.*/dev/dsk/c.d.s\)\([0-9]\)\(.*\):\2:' ;; SunOS-sun4) df | sed -e "/($EORIG/!d" \ -e 's:\(.*/dev/dsk/c.t.d.s\)\([0-9]\)\(.*\):\2:' ;; Linux-i*86|Linux-x86_64) case "$ORIG" in /dev/md*) df | sed -e "/^$EORIG/"'!d' -e 's:\(/dev/[a-z]*\)\([0-9]\)\(.*\):\2:' ;; ## braindamage: bash will try to find event "d" *) # # We will dump only Linux partitions (tag '83') # sed -ne 's/\/dev\/[^ ]*\([0-9]\).*83[ ][ ]*Linux/\1/p' orig.txt ;; esac ;; IRIX*) df | sed -e "/^$EORIG/!d" \ -e 's:\(^/dev/dsk/dks.d.s\)\([0-9]\)\(.*\):\2:' ;; esac ) |\ while read PART do if [ "$EXCLUDE" != "" ] then if expr ":${EXCLUDE}" : "\([^:]*:\)\{0,\}\($PART\)\(:[^:]*\)\{0,\}" > /dev/null 2>&1 then $ECHO echo "#### Skipping filesystem $ORIG$PART" continue fi fi $ECHO echo "#### Clonating filesystem contents at $ORIG$PART" $ECHO echo "## creating filesystem $DEST$PART" case $ARCH in SunOS-*) OFS=`df | egrep -e "$ORIG$PART" | awk '{print $1}'` FSTYPE=`df -k $OFS | tail -1 | awk '{print $2}'` MOUNTED=: ;; Linux-i*86|Linux-x86_64) OFS=`df | egrep -e "^$ORIG$PART" | awk '{print $6}'` if [ "_$OFS" = "_" ] then FSTYPE=`mount -v -o ro $ORIG$PART /new/orig/$PART 2>&1 | sed -ne 's/\/dev.*type \([^ ]*\).*/\1/p'` MOUNTED=false OFS=/new/orig/$PART else FSTYPE=`mount | sed -n "s/$EORIG$PART.*type \([^ ]*\) .*/\1/p"` $ECHO echo "## Warning: $ORIG$PART (${FSTYPE}) is mounted" MOUNTED=: fi ;; IRIX*) SED=cat SEDR="" SEDA="" if df / | egrep ^/dev/root then MAJORMINOR=`ls -lL /dev/root | awk '{print $5}'` DEVICE=`ls -l /dev/dsk/d* | awk "/$MAJORMINOR/"' {print $NF}'` SEDR="s:^/dev/root:$DEVICE:" SEDA="-e" SED=sed fi if df /usr | egrep ^/dev/usr then MAJORMINOR=`ls -lL /dev/usr | awk '{print $5}'` DEVICE=`ls -l /dev/dsk/d* | awk "/$MAJORMINOR/"' {print $NF}'` SEDU="s:^/dev/usr:$DEVICE:" SEDA="-e" SED=sed fi OFS=`df | $SED $SEDA $SEDR $SEDA $SEDU |\ egrep -e "^$ORIG$PART" | awk '{print $7}'` FSTYPE=`df -k $OFS | tail -1 | awk '{print $2}'` MOUNTED=: ;; esac OPART=$PART case $ARCH in SunOS-*) $ECHO $RSH "echo y | newfs $VERB $DEST$PART" < /dev/null ;; Linux-i*86|Linux-x86_64) if [ "$PART" = "0" ] then # special case for /dev/md0 OPART=1 fi if tune2fs -l $ORIG$PART 2>/dev/null |\ egrep -e "^Filesystem features:.*extent" >/dev/null 2>&1 then # ext4 $ECHO $RSH "/sbin/mkfs $CHECK -t ext4 -v $DEST$OPART" < /dev/null else # ext3/ext2 $ECHO $RSH "/sbin/mkfs $CHECK -v $DEST$OPART" < /dev/null if tune2fs -l $ORIG$PART 2>/dev/null |\ egrep -e "^Filesystem features:.*has_journal" >/dev/null 2>&1 then $ECHO echo "## creating journal $DEST$OPART" $ECHO $RSH "/sbin/tune2fs -j $DEST$OPART" fi LABEL=`/sbin/e2label $ORIG$PART` if [ "$LABEL" != "" ] then $ECHO echo "## creating label $DEST$OPART $LABEL" $ECHO $RSH "/sbin/e2label $DEST$OPART $LABEL" fi fi ;; IRIX*) $ECHO $RSH "mkfs -t $FSTYPE $DEST$PART" < /dev/null ;; esac $ECHO $RSH "mount $DEST$OPART /new/$OPART" < /dev/null $ECHO echo "## copying $OFS ($ORIG$PART) to /new/$OPART" copy_fs $OFS /new/$OPART < /dev/null if [ -f $OFS/$FSTAB ] then if $FIXFSTAB then FSTAB_PART=$OPART $ECHO echo "#### Fixing fstab on $DEST part $FSTAB_PART" $ECHO $RSH "cp $OFS/$FSTAB /new/fstab" < /dev/null fix_fstab $FSTAB_PART fi fi $MOUNTED || $ECHO umount /new/orig/$PART $ECHO $RSH "umount /new/$OPART" < /dev/null done $ECHO echo "#### `date`" if $NOBOOT then : else $ECHO echo "#### Installing boot loader on $DEST" case $ARCH in SunOS-i86pc) rel=`uname -r|sed -e 's,.*\.,,'` if [ "$rel" -ge 10 ] then $ECHO $RSH "/sbin/installgrub \ /boot/grub/stage1 \ /boot/grub/stage2 \ ${RDEST}0" else $ECHO $RSH "/usr/sbin/installboot \ /usr/platform/`uname -i`/lib/fs/ufs/pboot \ /usr/platform/`uname -i`/lib/fs/ufs/bootblk \ ${RDEST}2" fi ;; SunOS-sun4) $ECHO $RSH "/usr/sbin/installboot \ /usr/platform/`uname -i`/lib/fs/ufs/bootblk ${RDEST}0" ;; Linux-i*86|Linux-x86_64) $ECHO $RSH "mount ${DEST}1 /new/1" if [ -f /boot/grub/grub.conf ] then $ECHO echo "#### using grub" $ECHO $RSH "/sbin/grub --batch <<-EOF root (hd$GRUB,0) find /boot/grub/stage1 setup (hd$GRUB) EOF" else $ECHO echo "#### using lilo" $ECHO $RSH "/sbin/lilo -v -r /new/1 -b $DEST" fi $ECHO $RSH "umount /new/1" ;; IRIX*) #dvhtool -v list /dev/rdsk/dks0d1vh OVH=`echo $ORIG | sed -e 's,/dsk/,/rdsk/,' -e 's/s$/vh/'` DVH=`echo $DEST | sed -e 's,/dsk/,/rdsk/,' -e 's/s$/vh/'` cd /root/tmp || exit if [ "$RHOST" != "" ] then echo Remote header block edit not implemented on IRIX else for f in sgilabel sash ide symmon do $ECHO dvhtool -v get $f $f $OVH $ECHO dvhtool -v delete $f $DVH $ECHO dvhtool -v add $f $f $DVH done fi ;; esac fi $ECHO echo "#### `date`"